From 4656fc3a52b6159c5c61f386f3413e540c636d3f Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Wed, 2 Apr 2025 23:32:33 -0700 Subject: [PATCH 01/20] wip --- Cargo.lock | 408 +++++++++++- Cargo.toml | 18 +- src/integration/containers/mod.rs | 2 + src/integration/containers/op_reth.rs | 175 ++++++ src/integration/containers/rollup_boost.rs | 99 +++ src/integration/integration_test.rs | 28 +- src/integration/mod.rs | 698 +++++---------------- src/integration/service_rb.rs | 72 --- src/integration/service_reth.rs | 93 --- src/integration/testdata/jwt_secret.hex | 1 + src/integration/testdata/p2p_secret.hex | 1 + 11 files changed, 871 insertions(+), 724 deletions(-) create mode 100644 src/integration/containers/mod.rs create mode 100644 src/integration/containers/op_reth.rs create mode 100644 src/integration/containers/rollup_boost.rs delete mode 100644 src/integration/service_rb.rs delete mode 100644 src/integration/service_reth.rs create mode 100644 src/integration/testdata/jwt_secret.hex create mode 100644 src/integration/testdata/p2p_secret.hex diff --git a/Cargo.lock b/Cargo.lock index e86146da..72f99995 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -753,6 +753,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" @@ -848,6 +854,56 @@ dependencies = [ "zeroize", ] +[[package]] +name = "bollard" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97ccca1260af6a459d75994ad5acc1651bcabcbdbc41467cc9786519ab854c30" +dependencies = [ + "base64 0.22.1", + "bollard-stubs", + "bytes", + "futures-core", + "futures-util", + "hex", + "home", + "http", + "http-body-util", + "hyper", + "hyper-named-pipe", + "hyper-rustls", + "hyper-util", + "hyperlocal", + "log", + "pin-project-lite", + "rustls", + "rustls-native-certs", + "rustls-pemfile", + "rustls-pki-types", + "serde", + "serde_derive", + "serde_json", + "serde_repr", + "serde_urlencoded", + "thiserror 2.0.12", + "tokio", + "tokio-util", + "tower-service", + "url", + "winapi", +] + +[[package]] +name = "bollard-stubs" +version = "1.47.1-rc.27.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f179cfbddb6e77a5472703d4b30436bff32929c0aa8a9008ecf23d1d3cdd0da" +dependencies = [ + "serde", + "serde_repr", + "serde_with", +] + [[package]] name = "brotli" version = "7.0.0" @@ -969,8 +1025,10 @@ checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" dependencies = [ "android-tzdata", "iana-time-zone", + "js-sys", "num-traits", "serde", + "wasm-bindgen", "windows-link", ] @@ -1368,6 +1426,17 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "docker_credential" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31951f49556e34d90ed28342e1df7e1cb7a229c4cab0aecc627b5d91edd41d07" +dependencies = [ + "base64 0.21.7", + "serde", + "serde_json", +] + [[package]] name = "dotenv" version = "0.15.0" @@ -1445,6 +1514,19 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -1461,6 +1543,17 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if 1.0.0", + "home", + "windows-sys 0.48.0", +] + [[package]] name = "ethereum_serde_utils" version = "0.7.0" @@ -1549,6 +1642,18 @@ dependencies = [ "subtle", ] +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "libredox", + "windows-sys 0.59.0", +] + [[package]] name = "fixed-hash" version = "0.8.0" @@ -1821,6 +1926,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hermit-abi" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e" + [[package]] name = "hex" version = "0.4.3" @@ -1900,6 +2011,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humantime" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" + [[package]] name = "hyper" version = "1.6.0" @@ -1921,6 +2038,21 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-named-pipe" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b7d8abf35697b81a825e386fc151e0d503e8cb5fcb93cc8669c376dfd6f278" +dependencies = [ + "hex", + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", + "winapi", +] + [[package]] name = "hyper-rustls" version = "0.27.5" @@ -1973,6 +2105,21 @@ dependencies = [ "tracing", ] +[[package]] +name = "hyperlocal" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "986c5ce3b994526b3cd75578e62554abd09f0899d6206de48b3e96ab34ccc8c7" +dependencies = [ + "hex", + "http-body-util", + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "iana-time-zone" version = "0.1.63" @@ -2206,6 +2353,17 @@ dependencies = [ "serde", ] +[[package]] +name = "is-terminal" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" +dependencies = [ + "hermit-abi 0.5.0", + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -2341,7 +2499,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c872b6c9961a4ccc543e321bb5b89f6b2d2c7fe8b61906918273a3333c95400c" dependencies = [ "async-trait", - "base64", + "base64 0.22.1", "http-body", "hyper", "hyper-rustls", @@ -2417,7 +2575,7 @@ version = "9.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" dependencies = [ - "base64", + "base64 0.22.1", "js-sys", "pem", "ring", @@ -2493,6 +2651,17 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.9.0", + "libc", + "redox_syscall 0.5.10", +] + [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -2588,7 +2757,7 @@ version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd7399781913e5393588a8d8c6a2867bf85fb38eaf2502fdce465aad2dc6f034" dependencies = [ - "base64", + "base64 0.22.1", "http-body-util", "hyper", "hyper-rustls", @@ -2647,9 +2816,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.5" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430" dependencies = [ "adler2", ] @@ -2773,7 +2942,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", ] @@ -2902,7 +3071,7 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f8870d3024727e99212eb3bb1762ec16e255e3e6f58eeb3dc8db1aa226746d" dependencies = [ - "base64", + "base64 0.22.1", "hex", "opentelemetry", "opentelemetry_sdk", @@ -2993,11 +3162,36 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.5.10", "smallvec", "windows-targets 0.52.6", ] +[[package]] +name = "parse-display" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914a1c2265c98e2446911282c6ac86d8524f495792c38c5bd884f80499c7538a" +dependencies = [ + "parse-display-derive", + "regex", + "regex-syntax 0.8.5", +] + +[[package]] +name = "parse-display-derive" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ae7800a4c974efd12df917266338e79a7a74415173caf7e70aa0a0707345281" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "regex-syntax 0.8.5", + "structmeta", + "syn 2.0.100", +] + [[package]] name = "paste" version = "1.0.15" @@ -3010,7 +3204,7 @@ version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" dependencies = [ - "base64", + "base64 0.22.1", "serde", ] @@ -3374,6 +3568,15 @@ dependencies = [ "bitflags 2.9.0", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" version = "0.5.10" @@ -3433,7 +3636,7 @@ version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "futures-channel", "futures-core", @@ -3523,9 +3726,11 @@ dependencies = [ "anyhow", "assert_cmd", "bytes", + "chrono", "clap", "ctor", "dotenv", + "env_logger", "eyre", "futures", "http", @@ -3535,6 +3740,7 @@ dependencies = [ "hyper-util", "jsonrpsee", "lazy_static", + "log", "metrics", "metrics-exporter-prometheus", "metrics-util", @@ -3552,6 +3758,7 @@ dependencies = [ "rustls", "serde", "serde_json", + "testcontainers", "thiserror 2.0.12", "time", "tokio", @@ -3701,6 +3908,15 @@ dependencies = [ "security-framework", ] +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "rustls-pki-types" version = "1.11.0" @@ -3894,6 +4110,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -3912,7 +4139,7 @@ version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" dependencies = [ - "base64", + "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", @@ -4080,7 +4307,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e859df029d160cb88608f5d7df7fb4753fd20fdfb4de5644f3d8b8440841721" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "futures", "http", @@ -4118,6 +4345,29 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "structmeta" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e1575d8d40908d70f6fd05537266b90ae71b15dbbe7a8b7dffa2b759306d329" +dependencies = [ + "proc-macro2", + "quote", + "structmeta-derive", + "syn 2.0.100", +] + +[[package]] +name = "structmeta-derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "strum" version = "0.27.1" @@ -4225,12 +4475,50 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "termtree" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" +[[package]] +name = "testcontainers" +version = "0.23.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a4f01f39bb10fc2a5ab23eb0d888b1e2bb168c157f61a1b98e6c501c639c74" +dependencies = [ + "async-trait", + "bollard", + "bollard-stubs", + "bytes", + "docker_credential", + "either", + "etcetera", + "futures", + "log", + "memchr", + "parse-display", + "pin-project-lite", + "serde", + "serde_json", + "serde_with", + "thiserror 2.0.12", + "tokio", + "tokio-stream", + "tokio-tar", + "tokio-util", + "url", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -4391,6 +4679,21 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "tokio-tar" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5714c010ca3e5c27114c1cdeb9d14641ace49874aa5626d7149e47aedace75" +dependencies = [ + "filetime", + "futures-core", + "libc", + "redox_syscall 0.3.5", + "tokio", + "tokio-stream", + "xattr", +] + [[package]] name = "tokio-util" version = "0.7.14" @@ -4431,7 +4734,7 @@ dependencies = [ "async-stream", "async-trait", "axum", - "base64", + "base64 0.22.1", "bytes", "h2", "http", @@ -4514,7 +4817,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" dependencies = [ "async-compression", - "base64", + "base64 0.22.1", "bitflags 2.9.0", "bytes", "futures-core", @@ -4712,6 +5015,7 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] @@ -5097,6 +5401,15 @@ dependencies = [ "windows-targets 0.42.2", ] +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -5130,6 +5443,21 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -5168,6 +5496,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -5186,6 +5520,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -5204,6 +5544,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -5234,6 +5580,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -5252,6 +5604,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -5270,6 +5628,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -5288,6 +5652,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -5339,6 +5709,16 @@ dependencies = [ "tap", ] +[[package]] +name = "xattr" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" +dependencies = [ + "libc", + "rustix 1.0.5", +] + [[package]] name = "yoke" version = "0.7.5" diff --git a/Cargo.toml b/Cargo.toml index e1aabb10..d1e19840 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,13 +45,21 @@ paste = "1.0.15" # dev dependencies for integration tests parking_lot = "0.12.3" -time = { version = "0.3.36", features = ["macros", "formatting", "parsing"], optional = true } -lazy_static = {version = "1.5.0", optional = true } +time = { version = "0.3.36", features = [ + "macros", + "formatting", + "parsing", +], optional = true } +lazy_static = { version = "1.5.0", optional = true } [dev-dependencies] op-alloy-consensus = "0.12.0" alloy-rpc-types-eth = "0.13.0" +chrono = { version = "0.4.40", features = ["serde"] } anyhow = "1.0" +log = "0.4" +env_logger = "0.10" +testcontainers = { version = "0.23.3" } assert_cmd = "2.0.10" predicates = "3.1.2" tokio-util = { version = "0.7.13" } @@ -61,11 +69,7 @@ reth-rpc-layer = { git = "https://github.com/paradigmxyz/reth.git", rev = "v1.3. ctor = "0.4.1" [features] -integration = [ - "dep:lazy_static", - "dep:time", - "dep:alloy-eips" -] +integration = ["dep:lazy_static", "dep:time", "dep:alloy-eips"] [[bin]] name = "rollup-boost" diff --git a/src/integration/containers/mod.rs b/src/integration/containers/mod.rs new file mode 100644 index 00000000..41ef9049 --- /dev/null +++ b/src/integration/containers/mod.rs @@ -0,0 +1,2 @@ +pub mod op_reth; +pub mod rollup_boost; diff --git a/src/integration/containers/op_reth.rs b/src/integration/containers/op_reth.rs new file mode 100644 index 00000000..214d157c --- /dev/null +++ b/src/integration/containers/op_reth.rs @@ -0,0 +1,175 @@ +use std::{ + borrow::Cow, + collections::HashMap, + path::PathBuf, + time::{SystemTime, UNIX_EPOCH}, +}; + +use serde_json::Value; +use testcontainers::{CopyToContainer, Image, core::WaitFor}; + +use crate::integration::JWT_SECRET; + +const NAME: &str = "ghcr.io/paradigmxyz/op-reth"; +const TAG: &str = "v1.3.4"; + +#[derive(Debug, Clone)] +pub struct OpRethConfig { + chain: PathBuf, + jwt_secret: PathBuf, + p2p_secret: Option, + pub trusted_peers: Vec, + pub authrpc_port: u16, + pub datadir: String, + pub disable_discovery: bool, + pub p2p_port: u16, + pub color: String, + pub ipcdisable: bool, + + pub env_vars: HashMap, + copy_to_sources: Vec, +} + +impl Default for OpRethConfig { + fn default() -> Self { + Self { + chain: PathBuf::from(format!( + "{}/src/integration/testdata/genesis.json", + env!("CARGO_MANIFEST_DIR") + )), + jwt_secret: PathBuf::from(format!( + "{}/src/integration/testdata/jwt_secret.hex", + env!("CARGO_MANIFEST_DIR") + )), + p2p_secret: None, + trusted_peers: vec![], + authrpc_port: 8551, + datadir: "data".to_string(), + disable_discovery: true, + p2p_port: 30303, + color: "never".to_string(), + ipcdisable: true, + + env_vars: Default::default(), + copy_to_sources: vec![], + } + .update_copy_to_sources() + } +} + +impl OpRethConfig { + pub fn with_env_vars( + mut self, + env_vars: impl IntoIterator, impl Into)>, + ) -> Self { + self.env_vars + .extend(env_vars.into_iter().map(|(k, v)| (k.into(), v.into()))); + self + } + + pub fn set_trusted_peers(mut self, trusted_peers: Vec) -> Self { + self.trusted_peers = trusted_peers; + self + } + + fn update_copy_to_sources(mut self) -> Self { + // Write the genesis file to the test directory and update the timestamp to the current time + let genesis = { + // Read the template file + let template = include_str!("../testdata/genesis.json"); + + // Parse the JSON + let mut genesis: Value = serde_json::from_str(template).unwrap(); + + // Update the timestamp field + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs(); + + if let Some(config) = genesis.as_object_mut() { + // Assuming timestamp is at the root level - adjust path as needed + config["timestamp"] = Value::String(format!("0x{:x}", timestamp)); + } + + serde_json::to_string_pretty(&genesis).unwrap() + }; + + self.copy_to_sources = vec![ + CopyToContainer::new(self.jwt_secret.clone(), "jwt_secret.hex".to_string()), + CopyToContainer::new(genesis.into_bytes(), "genesis.json".to_string()), + ]; + + if let Some(p2p_secret) = &self.p2p_secret { + self.copy_to_sources.push(CopyToContainer::new( + p2p_secret.clone(), + "p2p_secret.hex".to_string(), + )); + } + + self + } + + pub fn set_jwt_secret(mut self, jwt_secret: PathBuf) -> Self { + self.jwt_secret = jwt_secret; + self.update_copy_to_sources() + } + + pub fn set_p2p_secret(mut self, p2p_secret: Option) -> Self { + self.p2p_secret = p2p_secret; + self.update_copy_to_sources() + } +} + +impl Image for OpRethConfig { + fn name(&self) -> &str { + NAME + } + + fn tag(&self) -> &str { + TAG + } + + fn ready_conditions(&self) -> Vec { + vec![WaitFor::message_on_stdout("Starting consensus")] + } + + fn env_vars( + &self, + ) -> impl IntoIterator>, impl Into>)> { + &self.env_vars + } + + fn copy_to_sources(&self) -> impl IntoIterator { + self.copy_to_sources.iter() + } + + fn cmd(&self) -> impl IntoIterator>> { + let mut cmd = vec![ + "node".to_string(), + "--authrpc.port".to_string(), + self.authrpc_port.to_string(), + "--authrpc.jwtsecret=jwt_secret.hex".to_string(), + "--chain=genesis.json".to_string(), + "--datadir".to_string(), + self.datadir.clone(), + "--port".to_string(), + self.p2p_port.to_string(), + "--color".to_string(), + self.color.clone(), + ]; + if self.p2p_secret.is_some() { + cmd.push("--p2p-secret-key=p2p_secret.hex".to_string()); + } + if !self.trusted_peers.is_empty() { + cmd.extend(["--trusted-peers".to_string(), self.trusted_peers.join(",")]); + } + if self.disable_discovery { + cmd.push("--disable-discovery".to_string()); + } + if self.ipcdisable { + cmd.push("--ipcdisable".to_string()); + } + cmd + } +} diff --git a/src/integration/containers/rollup_boost.rs b/src/integration/containers/rollup_boost.rs new file mode 100644 index 00000000..c51393b8 --- /dev/null +++ b/src/integration/containers/rollup_boost.rs @@ -0,0 +1,99 @@ +use std::{ + fs::File, + io::Write, + sync::{Arc, Mutex}, +}; + +use clap::Parser; +use http::Uri; +use tokio::task::JoinHandle; +use tracing::{Level, instrument::WithSubscriber as _}; +use tracing_subscriber::fmt::MakeWriter; + +#[derive(Clone)] +struct SharedFileWriter { + file: Arc>, +} + +impl<'a> MakeWriter<'a> for SharedFileWriter { + type Writer = SharedFileGuard<'a>; + + fn make_writer(&'a self) -> Self::Writer { + SharedFileGuard { + guard: self.file.lock().unwrap(), // you can handle poisoning if needed + } + } +} + +pub struct SharedFileGuard<'a> { + guard: std::sync::MutexGuard<'a, File>, +} + +impl Write for SharedFileGuard<'_> { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.guard.write(buf) + } + + fn flush(&mut self) -> std::io::Result<()> { + self.guard.flush() + } +} + +use crate::Args; + +#[derive(Debug)] +pub struct RollupBoost { + pub args: Args, + pub log_file: Arc>, + pub handle: JoinHandle>, +} + +impl RollupBoost { + pub fn rpc_endpoint(&self) -> String { + format!("http://localhost:{}", self.args.rpc_port) + } + + pub fn debug_endpoint(&self) -> String { + format!("http://localhost:{}", self.args.debug_server_port) + } +} + +#[derive(Clone, Debug)] +pub struct RollupBoostConfig { + pub args: Args, +} + +impl Default for RollupBoostConfig { + fn default() -> Self { + Self { + args: Args::parse_from([ + "rollup-boost", + "--l2-jwt-path=../testdata/jwt_secret.hex", + "--builder-jwt-path=../testdata/jwt_secret.hex", + "--tracing", + "--metrics", + ]), + } + } +} + +impl RollupBoostConfig { + pub fn start(self, log_file: File) -> RollupBoost { + let log_file = Arc::new(Mutex::new(log_file)); + let writer = SharedFileWriter { + file: log_file.clone(), + }; + let subscriber = tracing_subscriber::fmt() + .with_writer(writer) + .with_env_filter(format!("rollup_boost={}", Level::TRACE)) + .finish(); + + let handle = tokio::spawn(self.args.clone().run().with_subscriber(subscriber)); + + RollupBoost { + args: self.args, + log_file, + handle, + } + } +} diff --git a/src/integration/integration_test.rs b/src/integration/integration_test.rs index b2b6b6a9..ebe5a79e 100644 --- a/src/integration/integration_test.rs +++ b/src/integration/integration_test.rs @@ -2,6 +2,7 @@ mod tests { use alloy_primitives::B256; use serde_json::Value; + use std::io::Read; use std::sync::{Arc, Mutex}; use std::time::Duration; @@ -149,10 +150,9 @@ mod tests { #[tokio::test] async fn test_integration_remote_builder_down() -> eyre::Result<()> { - let mut harness = - RollupBoostTestHarnessBuilder::new("test_integration_remote_builder_down") - .build() - .await?; + let harness = RollupBoostTestHarnessBuilder::new("test_integration_remote_builder_down") + .build() + .await?; let mut block_generator = harness.get_block_generator().await?; for _ in 0..3 { @@ -164,8 +164,7 @@ mod tests { } // stop the builder - let builder_service = harness._framework.get_mut_service("builder")?; - builder_service.stop()?; + harness.builder.stop().await; // create 3 new blocks that are processed by the l2 builder for _ in 0..3 { @@ -174,7 +173,7 @@ mod tests { } // start the builder again - builder_service.start_and_ready()?; + harness.builder.start().await; // the next block is computed by the l2 builder because the builder is not synced with the previous 3 blocks // But, once the builder receives the FCU request from rollup-boost, it will sync up the blocks with the @@ -259,7 +258,7 @@ mod tests { Some(result) }); - let mut harness = + let harness = RollupBoostTestHarnessBuilder::new("test_integration_builder_returns_incorrect_block") .proxy_handler(handler) .build() @@ -274,11 +273,16 @@ mod tests { } // check that at some point we had the log "builder payload was not valid" which signals // that the builder returned a payload that was not valid and rollup-boost did not process it. - let rb_service = harness._framework.get_mut_service("rollup-boost")?; - - let logs = rb_service.get_logs()?; + // read lines + let mut buf = String::new(); + harness + .rollup_boost + .log_file + .lock() + .unwrap() + .read_to_string(&mut buf)?; assert!( - logs.contains("Invalid payload"), + buf.contains("Invalid payload"), "Logs should contain the message 'builder payload was not valid'" ); diff --git a/src/integration/mod.rs b/src/integration/mod.rs index 3f90c7b2..4351ca18 100644 --- a/src/integration/mod.rs +++ b/src/integration/mod.rs @@ -10,460 +10,101 @@ use alloy_rpc_types_engine::{ ForkchoiceState, ForkchoiceUpdated, PayloadAttributes, PayloadId, PayloadStatus, PayloadStatusEnum, }; -use bytes::BytesMut; +use containers::op_reth::OpRethConfig; +use containers::rollup_boost::{RollupBoost, RollupBoostConfig}; +use eyre::bail; +use futures::FutureExt; +use futures::future::BoxFuture; use jsonrpsee::http_client::{HttpClient, transport::HttpBackend}; use jsonrpsee::proc_macros::rpc; -use lazy_static::lazy_static; -use op_alloy_consensus::TxDeposit; -use op_alloy_rpc_types_engine::OpPayloadAttributes; +use op_alloy_rpc_types_engine::{OpExecutionPayloadEnvelopeV3, OpPayloadAttributes}; use proxy::{DynHandlerFn, start_proxy_server}; -use serde_json::Value; -use std::collections::{HashMap, HashSet}; -use std::path::{Path, PathBuf}; +use std::io::Read; +use std::net::TcpListener; +use std::path::PathBuf; use std::str::FromStr; -use std::sync::Mutex; -use std::time::UNIX_EPOCH; -use std::{ - fs::{File, OpenOptions}, - io, - io::prelude::*, - process::{Child, Command}, - time::{Duration, SystemTime}, -}; -use thiserror::Error; +use std::sync::Arc; +use std::time::{Duration, SystemTime}; +use testcontainers::core::logs::LogFrame; +use testcontainers::core::logs::consumer::LogConsumer; +use testcontainers::runners::AsyncRunner; +use testcontainers::{ContainerAsync, Image, ImageExt}; +use tokio::io::{AsyncBufReadExt, AsyncWriteExt as _}; +use tracing::info; + use time::{OffsetDateTime, format_description}; /// Default JWT token for testing purposes -pub const DEFAULT_JWT_TOKEN: &str = - "688f5d737bad920bdfb2fc2f488d6b6209eebda1dae949a8de91398d932c517a"; +pub const JWT_SECRET: &str = "688f5d737bad920bdfb2fc2f488d6b6209eebda1dae949a8de91398d932c517a"; + +pub const L2_P2P_SECRET: &str = "a11ac89899cd86e36b6fb881ec1255b8a92a688790b7d950f8b7d8dd626671fb"; +pub const L2_P2P_ENODE: &str = "3479db4d9217fb5d7a8ed4d61ac36e120b05d36c2eefb795dc42ff2e971f251a2315f5649ea1833271e020b9adc98d5db9973c7ed92d6b2f1f2223088c3d852f"; +mod containers; mod integration_test; mod proxy; -mod service_rb; -mod service_reth; - -#[derive(Debug, Error)] -pub enum IntegrationError { - #[error("Failed to spawn process")] - SpawnError, - #[error("Binary not found")] - BinaryNotFound, - #[error("Failed to setup integration framework")] - SetupError, - #[error("Log error")] - LogError, - #[error("Service already running")] - ServiceAlreadyRunning, - #[error("Service stopped")] - ServiceStopped, - #[error(transparent)] - AddrParseError(#[from] std::net::AddrParseError), -} - -#[derive(Debug, Clone)] -pub enum Arg { - Port { name: String, preferred: u16 }, - Dir { name: String }, - Value(String), - // FilePath is an argument that writes the given content to a file in the test directory - // and returns the path to the file as an argument - FilePath { name: String, content: String }, -} - -impl From for Arg { - fn from(s: String) -> Self { - Arg::Value(s) - } -} - -impl From<&str> for Arg { - fn from(s: &str) -> Self { - Arg::Value(s.to_string()) - } -} - -impl From for Arg { - fn from(path: PathBuf) -> Self { - Arg::Value( - path.to_str() - .expect("Failed to convert path to string") - .to_string(), - ) - } -} - -impl From<&Path> for Arg { - fn from(path: &Path) -> Self { - Arg::Value( - path.to_str() - .expect("Failed to convert path to string") - .to_string(), - ) - } -} - -impl From<&String> for Arg { - fn from(s: &String) -> Self { - Arg::Value(s.clone()) - } -} - -impl From<&PathBuf> for Arg { - fn from(path: &PathBuf) -> Self { - Arg::Value( - path.to_str() - .expect("Failed to convert path to string") - .to_string(), - ) - } -} - -pub struct ServiceCommand { - program: String, - args: Vec, -} - -impl ServiceCommand { - pub fn new(program: impl Into) -> Self { - Self { - program: program.into(), - args: Vec::new(), - } - } - - pub fn arg(mut self, arg: impl Into) -> Self { - self.args.push(arg.into()); - self - } -} - -pub struct ReadyParams { - pub log_pattern: String, - pub duration: Duration, -} - -pub trait Service { - fn command(&self) -> ServiceCommand; - fn ready(&self) -> ReadyParams; -} - -pub struct ServiceInstance { - command_config: (String, Vec), - process: Option, - log_path: PathBuf, - service: Box, - allocated_ports: HashMap, -} - -lazy_static! { - static ref GLOBAL_ALLOCATED_PORTS: Mutex> = Mutex::new(HashSet::new()); -} - -pub struct IntegrationFramework { - test_dir: PathBuf, - logs_dir: PathBuf, - services: HashMap, -} - -impl ServiceInstance { - pub fn new( - name: String, - command_config: (String, Vec), - logs_dir: PathBuf, - allocated_ports: HashMap, - service: Box, - ) -> Self { - let log_path = logs_dir.join(format!("{}.log", name)); - Self { - process: None, - command_config, - log_path, - allocated_ports, - service, - } - } - - pub fn start(&mut self) -> Result<(), IntegrationError> { - if self.process.is_some() { - return Err(IntegrationError::ServiceAlreadyRunning); - } - - let mut log = open_log_file(&self.log_path)?; - let stdout = log.try_clone().map_err(|_| IntegrationError::LogError)?; - let stderr = log.try_clone().map_err(|_| IntegrationError::LogError)?; - - // print the command config on the log file - log.write_all(format!("Command config: {:?}\n", self.command_config).as_bytes()) - .map_err(|_| IntegrationError::LogError)?; - - // build the command from the command config - let mut cmd = { - let command_config = self.command_config.clone(); - let mut cmd = Command::new(command_config.0.clone()); - cmd.args(&command_config.1); - cmd - }; - cmd.stdout(stdout).stderr(stderr); - - let child = match cmd.spawn() { - Ok(child) => Ok(child), - Err(e) => match e.kind() { - io::ErrorKind::NotFound => Err(IntegrationError::BinaryNotFound), - _ => Err(IntegrationError::SpawnError), - }, - }?; - - self.process = Some(child); - Ok(()) - } - - pub fn stop(&mut self) -> Result<(), IntegrationError> { - if let Some(mut process) = self.process.take() { - nix::sys::signal::kill( - nix::unistd::Pid::from_raw(process.id() as i32), - nix::sys::signal::SIGINT, - ) - .map_err(|_| IntegrationError::SpawnError)?; - - // wait for the process to exit - process.wait().unwrap(); - } - Ok(()) - } - - /// Start a service using its configuration and wait for it to be ready - pub fn start_and_ready(&mut self) -> Result<(), IntegrationError> { - self.start()?; - - let params = self.service.ready(); - self.wait_for_log(¶ms.log_pattern, params.duration)?; - - Ok(()) - } - - pub fn get_port(&self, name: &str) -> u16 { - *self.allocated_ports.get(name).unwrap_or_else(|| { - panic!("Port for {} not found", name); - }) - } - - pub fn get_endpoint(&self, name: &str) -> String { - format!("http://localhost:{}", self.get_port(name)) - } - - pub fn get_logs(&self) -> Result { - let mut file = File::open(&self.log_path).map_err(|_| IntegrationError::LogError)?; - let mut contents = String::new(); - file.read_to_string(&mut contents) - .map_err(|_| IntegrationError::LogError)?; - Ok(contents) - } - - pub fn wait_for_log( - &mut self, - pattern: &str, - timeout: Duration, - ) -> Result<(), IntegrationError> { - let start = std::time::Instant::now(); - - loop { - // Check if process has stopped - if let Some(ref mut process) = self.process { - match process.try_wait() { - Ok(None) => {} - Ok(Some(_status)) => { - // Process has exited - return Err(IntegrationError::ServiceStopped); - } - Err(_) => { - return Err(IntegrationError::ServiceStopped); - } - } - } - if start.elapsed() > timeout { - return Err(IntegrationError::SpawnError); - } - - let mut contents = self.get_logs()?; - - // Since we share the same log file for different executions of the same service during the lifespan - // of the test, we need to filter the logs and only consider the logs of the current execution. - // We can do this because we print at each service start the log "Command config: " - // So, we are going to search for the command config in the log and only consider the logs after that - if let Some(index) = contents.rfind("Command config:") { - contents = contents[index..].to_string(); - } - - if contents.contains(pattern) { - return Ok(()); - } - - std::thread::sleep(Duration::from_millis(100)); - } - } +struct LoggingConsumer { + target: String, + log_file: tokio::sync::Mutex, } -impl IntegrationFramework { - pub fn new(test_name: &str) -> Result { - let dt: OffsetDateTime = SystemTime::now().into(); - let format = format_description::parse("[year]_[month]_[day]_[hour]_[minute]_[second]") - .map_err(|_| IntegrationError::SetupError)?; - - let timestamp = dt - .format(&format) - .map_err(|_| IntegrationError::SetupError)?; - - let test_name = format!("{}_{}", timestamp, test_name); - - let mut test_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - test_dir.push("./integration_logs"); - test_dir.push(test_name); - - // Create logs subdirectory - let logs_dir = test_dir.join("logs"); - std::fs::create_dir_all(&logs_dir).map_err(|_| IntegrationError::SetupError)?; - - Ok(Self { - test_dir, - logs_dir, - services: HashMap::new(), - }) - } - - fn get_mut_service(&mut self, name: &str) -> eyre::Result<&mut ServiceInstance> { - self.services - .get_mut(name) - .ok_or(eyre::eyre!("Service not found")) - } - - fn build_command( - &mut self, - service_name: &str, - cmd: ServiceCommand, - ) -> Result<(HashMap, (String, Vec)), IntegrationError> { - let mut allocated_ports = HashMap::new(); - let mut command_args = Vec::new(); - - for arg in cmd.args { - match arg { - Arg::Port { name, preferred } => { - let port = self.find_available_port(preferred)?; - allocated_ports.insert(name, port); - command_args.push(port.to_string()); - } - Arg::Dir { name } => { - let dir_path = self.test_dir.join(service_name).join(name); - std::fs::create_dir_all(&dir_path).map_err(|_| IntegrationError::SetupError)?; - command_args.push( - dir_path - .to_str() - .expect("Failed to convert path to string") - .to_string(), - ); - } - Arg::FilePath { name, content } => { - let file_path = self.test_dir.join(service_name).join(name); - std::fs::write(&file_path, content) - .map_err(|_| IntegrationError::SetupError)?; - command_args.push( - file_path - .to_str() - .expect("Failed to convert path to string") - .to_string(), - ); +impl LogConsumer for LoggingConsumer { + fn accept<'a>(&'a self, record: &'a LogFrame) -> BoxFuture<'a, ()> { + async move { + match record { + testcontainers::core::logs::LogFrame::StdOut(bytes) => { + info!(target = self.target, "{}", String::from_utf8_lossy(bytes)); + self.log_file.lock().await.write_all(bytes).await.unwrap(); } - Arg::Value(value) => { - command_args.push(value); + testcontainers::core::logs::LogFrame::StdErr(bytes) => { + info!(target = self.target, "{}", String::from_utf8_lossy(bytes)); + self.log_file.lock().await.write_all(bytes).await.unwrap(); } } } - - Ok((allocated_ports, (cmd.program, command_args))) - } - - fn find_available_port(&self, start: u16) -> Result { - let mut global_ports = GLOBAL_ALLOCATED_PORTS - .lock() - .expect("Failed to acquire lock"); - - (start..start + 100) - .find(|&port| { - if global_ports.contains(&port) { - return false; - } - if std::net::TcpListener::bind(("127.0.0.1", port)).is_ok() { - global_ports.insert(port); - return true; + .boxed() + } +} + +pub struct TestEnv { + pub l2: ContainerAsync, + pub builder: ContainerAsync, + pub rollup_boost: RollupBoost, +} + +pub async fn wait_for_log( + container: ContainerAsync, + pattern: &str, + timeout: Duration, +) -> eyre::Result<()> { + let timeout = tokio::time::sleep(timeout); + let mut stderr = container.stderr(true).lines(); + let mut stdout = container.stdout(true).lines(); + + tokio::select! { + result = async { + loop { + tokio::select! { + line = stderr.next_line() => { + if let Ok(Some(line)) = line { + if line.contains(pattern) { + return Ok::<_, eyre::Report>(()); + } + } + } + line = stdout.next_line() => { + if let Ok(Some(line)) = line { + if line.contains(pattern) { + return Ok::<_, eyre::Report>(()); + } + } + } } - false - }) - .ok_or(IntegrationError::SetupError) - } - - pub async fn start( - &mut self, - name: &str, - config: Box, - ) -> Result<&mut ServiceInstance, IntegrationError> { - let (allocated_ports, command_config) = self.build_command(name, config.command())?; - - // Store the service instance in the framework - let service = ServiceInstance::new( - name.to_string(), - command_config, - self.logs_dir.clone(), - allocated_ports, - config, - ); - self.services.insert(name.to_string(), service); - let service = self.services.get_mut(name).unwrap(); - - service.start_and_ready()?; - Ok(service) - } - - /// Writes content to a file in the test directory and returns its absolute path - pub fn write_file( - &self, - name: &str, - content: impl AsRef<[u8]>, - ) -> Result { - let file_path = self.test_dir.join(name); - if let Some(parent) = file_path.parent() { - std::fs::create_dir_all(parent).map_err(|_| IntegrationError::SetupError)?; - } - std::fs::write(&file_path, content).map_err(|_| IntegrationError::SetupError)?; - Ok(file_path) - } -} - -fn open_log_file(path: &PathBuf) -> Result { - let prefix = path.parent().unwrap(); - std::fs::create_dir_all(prefix).map_err(|_| IntegrationError::LogError)?; - - OpenOptions::new() - .append(true) - .create(true) - .open(path) - .map_err(|_| IntegrationError::LogError) -} - -impl Drop for IntegrationFramework { - fn drop(&mut self) { - // Stop all services first - for service in &mut self.services { - let _ = service.1.stop(); - } - - // Release allocated ports from global registry - let mut global_ports = GLOBAL_ALLOCATED_PORTS - .lock() - .expect("Failed to acquire lock"); - for service in &self.services { - for port in service.1.allocated_ports.values() { - global_ports.remove(port); } + } => result, + _ = timeout => { + bail!("Timeout waiting for log message: {}", pattern); } } } @@ -474,7 +115,7 @@ pub struct EngineApi { // TODO: Use client/rpc.rs instead impl EngineApi { - pub fn new(url: &str, secret: &str) -> Result> { + pub fn new(url: &str, secret: &str) -> eyre::Result { let secret_layer = AuthClientLayer::new(JwtSecret::from_str(secret)?); let middleware = tower::ServiceBuilder::default().layer(secret_layer); let client = jsonrpsee::http_client::HttpClientBuilder::default() @@ -562,15 +203,11 @@ pub trait BlockApi { /// Test flavor that sets up one Rollup-boost instance connected to two Reth nodes pub struct RollupBoostTestHarness { - _framework: IntegrationFramework, // Keep framework alive to maintain service ownership + pub l2: ContainerAsync, + pub builder: ContainerAsync, + pub rollup_boost: RollupBoost, } -/// Test node P2P configuration (private_key, enode_address) -pub const TEST_NODE_P2P_ADDR: (&str, &str) = ( - "a11ac89899cd86e36b6fb881ec1255b8a92a688790b7d950f8b7d8dd626671fb", - "3479db4d9217fb5d7a8ed4d61ac36e120b05d36c2eefb795dc42ff2e971f251a2315f5649ea1833271e020b9adc98d5db9973c7ed92d6b2f1f2223088c3d852f", -); - const PROXY_START_PORT: u16 = 4444; pub struct RollupBoostTestHarnessBuilder { @@ -586,103 +223,105 @@ impl RollupBoostTestHarnessBuilder { } } - pub fn proxy_handler(mut self, proxy_handler: DynHandlerFn) -> Self { - self.proxy_handler = Some(proxy_handler); - self - } + pub fn file_path(&self, service_name: &str) -> eyre::Result { + let dt: OffsetDateTime = SystemTime::now().into(); + let format = format_description::parse("[year]_[month]_[day]_[hour]_[minute]_[second]")?; + let timestamp = dt.format(&format)?; - async fn build(self) -> Result { - let mut framework = IntegrationFramework::new(self.test_name.as_str())?; + // let test_name = format!("{}_{}", timestamp, test_name); - let jwt_path = framework.write_file("jwt.hex", DEFAULT_JWT_TOKEN)?; + let dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("test_logs") + .join(timestamp) + .join(self.test_name.clone()); + std::fs::create_dir_all(&dir)?; - // Write the genesis file to the test directory and update the timestamp to the current time - let genesis_path = { - // Read the template file - let template = include_str!("testdata/genesis.json"); + let file_name = format!("{service_name}.log"); + Ok(dir.join(file_name)) + } - // Parse the JSON - let mut genesis: Value = serde_json::from_str(template).unwrap(); + pub async fn async_log_file(&self, service_name: &str) -> eyre::Result { + let file_path = self.file_path(service_name)?; + Ok(tokio::fs::OpenOptions::new() + .append(true) + .create(true) + .open(file_path) + .await?) + } - // Update the timestamp field - let timestamp = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_secs(); - if let Some(config) = genesis.as_object_mut() { - // Assuming timestamp is at the root level - adjust path as needed - config["timestamp"] = Value::String(format!("0x{:x}", timestamp)); - } + pub fn log_file(&self, service_name: &str) -> eyre::Result { + let file_path = self.file_path(service_name)?; + Ok(std::fs::OpenOptions::new() + .append(true) + .create(true) + .open(file_path)?) + } - framework.write_file( - "genesis.json", - serde_json::to_string_pretty(&genesis).unwrap(), - )? - }; + pub async fn log_consumer(&self, service_name: &str) -> eyre::Result { + let file = self.async_log_file(service_name).await?; + Ok(LoggingConsumer { + target: service_name.to_string(), + log_file: tokio::sync::Mutex::new(file), + }) + } - // Start L2 Reth instance - let l2_reth_config = service_reth::RethConfig::new() - .jwt_secret_path(jwt_path.clone()) - .chain_config_path(genesis_path.clone()) - .p2p_secret_key(TEST_NODE_P2P_ADDR.0.to_string()); + pub fn proxy_handler(mut self, proxy_handler: DynHandlerFn) -> Self { + self.proxy_handler = Some(proxy_handler); + self + } - let l2_service = { - let service = framework.start("l2-reth", Box::new(l2_reth_config)).await?; - (service.get_endpoint("authrpc"), service.get_port("p2p")) - }; + async fn build(self) -> eyre::Result { + let l2_log_consumer = self.log_consumer("l2").await?; + let builder_log_consumer = self.log_consumer("builder").await?; + let rollup_boost_log_file = self.log_file("rollup_boost")?; - // Start Builder Reth instance + let l2 = OpRethConfig::default() + // .set_p2p_secret(Some(PathBuf::from("../testdata/p2p_secret.hex"))) + .with_log_consumer(l2_log_consumer) + .start() + .await?; - // The enode address depends on the p2p port of the L2 Reth instance - // TODO: We could also query the logs of the L2 Reth instance for the enode address and avoid this - let enode_address = format!( + let l2_url = format!("http://127.0.0.1:{}", l2.get_host_port_ipv4(8545).await?); + let l2_enode = format!( "enode://{}@127.0.0.1:{}", - TEST_NODE_P2P_ADDR.1, l2_service.1 + L2_P2P_ENODE, + l2.get_host_port_ipv4(30303).await? ); - let builder_reth_config = service_reth::RethConfig::new() - .jwt_secret_path(jwt_path.clone()) - .chain_config_path(genesis_path) - .trusted_peer(enode_address); - - let builder_authrpc_port = { - let service = framework - .start("builder", Box::new(builder_reth_config)) - .await?; - service.get_port("authrpc") - }; + let builder = OpRethConfig::default() + .set_trusted_peers(vec![l2_enode.clone()]) + .with_log_consumer(builder_log_consumer) + .start() + .await?; + let mut builder_authrpc_port = builder.get_host_port_ipv4(8551).await?; // run a proxy in between the builder and the rollup-boost if the proxy_handler is set - let builder_authrpc_port = if let Some(proxy_handler) = self.proxy_handler { - let proxy_port = framework.find_available_port(PROXY_START_PORT)?; + if let Some(proxy_handler) = self.proxy_handler { + let proxy_port = get_available_port().expect("no available port"); let _ = start_proxy_server(proxy_handler, proxy_port, builder_authrpc_port).await; - proxy_port - } else { - builder_authrpc_port + builder_authrpc_port = proxy_port }; - let builder_service = format!("http://127.0.0.1:{}", builder_authrpc_port); + let builder_url = format!("http://127.0.0.1:{}", builder_authrpc_port); // Start Rollup-boost instance - let rb_config = service_rb::RollupBoostConfig::new() - .jwt_path(jwt_path) - .l2_url(l2_service.0) - .builder_url(builder_service); - - let _ = framework.start("rollup-boost", Box::new(rb_config)).await?; + let mut rollup_boost = RollupBoostConfig::default(); + rollup_boost.args.l2_client.l2_url = l2_url.try_into().unwrap(); + rollup_boost.args.builder.builder_url = builder_url.try_into().unwrap(); + let rollup_boost = rollup_boost.start(rollup_boost_log_file); Ok(RollupBoostTestHarness { - _framework: framework, + l2, + builder, + rollup_boost, }) } } impl RollupBoostTestHarness { pub async fn get_block_generator(&self) -> eyre::Result { - let rb_service = self._framework.services.get("rollup-boost").unwrap(); - let validator = BlockBuilderCreatorValidator::new(rb_service.log_path.clone()); + let validator = BlockBuilderCreatorValidator::new(self.rollup_boost.log_file.clone()); - let engine_api = EngineApi::new(&rb_service.get_endpoint("rpc"), DEFAULT_JWT_TOKEN) - .map_err(|_| IntegrationError::SetupError)?; + let engine_api = EngineApi::new(&self.rollup_boost.rpc_endpoint(), JWT_SECRET)?; let mut block_creator = SimpleBlockGenerator::new(validator, engine_api); block_creator.init().await?; @@ -690,10 +329,7 @@ impl RollupBoostTestHarness { } pub async fn get_client(&self) -> DebugClient { - let rb_service = self._framework.services.get("rollup-boost").unwrap(); - let endpoint = rb_service.get_endpoint("debug"); - - DebugClient::new(&endpoint).unwrap() + DebugClient::new(&self.rollup_boost.debug_endpoint()).unwrap() } } @@ -796,7 +432,8 @@ impl SimpleBlockGenerator { // Check who built the block in the rollup-boost logs let block_creator = self .validator - .get_block_creator(new_block_hash)? + .get_block_creator(new_block_hash) + .await? .expect("block creator not found"); Ok((new_block_hash, block_creator)) @@ -804,21 +441,19 @@ impl SimpleBlockGenerator { } pub struct BlockBuilderCreatorValidator { - log_path: PathBuf, + file: Arc>, } -impl BlockBuilderCreatorValidator { - pub fn new(log_path: PathBuf) -> Self { - Self { log_path } +impl<'a> BlockBuilderCreatorValidator { + pub fn new(file: Arc>) -> Self { + Self { file } } } -impl BlockBuilderCreatorValidator { - pub fn get_block_creator(&self, block_hash: B256) -> eyre::Result> { - let mut file = File::open(&self.log_path).map_err(|_| IntegrationError::LogError)?; +impl<'a> BlockBuilderCreatorValidator { + pub async fn get_block_creator(&self, block_hash: B256) -> eyre::Result> { let mut contents = String::new(); - file.read_to_string(&mut contents) - .map_err(|_| IntegrationError::LogError)?; + self.file.lock().unwrap().read_to_string(&mut contents)?; let search_query = format!("returning block hash={:#x}", block_hash); @@ -871,3 +506,14 @@ fn create_deposit_tx() -> Bytes { buffer_without_header.to_vec().into() } + +fn get_available_port() -> Option { + (8000..9000).find(|port| port_is_available(*port)) +} + +fn port_is_available(port: u16) -> bool { + match TcpListener::bind(("127.0.0.1", port)) { + Ok(_) => true, + Err(_) => false, + } +} diff --git a/src/integration/service_rb.rs b/src/integration/service_rb.rs deleted file mode 100644 index e3cd2d47..00000000 --- a/src/integration/service_rb.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::integration::{Arg, ReadyParams, Service, ServiceCommand}; -use std::{path::PathBuf, time::Duration}; - -#[derive(Default)] -pub struct RollupBoostConfig { - jwt_path: Option, - l2_url: Option, - builder_url: Option, -} - -impl RollupBoostConfig { - pub fn new() -> Self { - Self::default() - } - - pub fn jwt_path>(mut self, path: P) -> Self { - self.jwt_path = Some(path.into()); - self - } - - pub fn l2_url(mut self, url: String) -> Self { - self.l2_url = Some(url); - self - } - - pub fn builder_url(mut self, url: String) -> Self { - self.builder_url = Some(url); - self - } -} - -impl Service for RollupBoostConfig { - fn command(&self) -> ServiceCommand { - let mut bin_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - bin_path.push("./target/debug/rollup-boost"); - - let jwt_path = self.jwt_path.as_ref().expect("jwt_path not set"); - - let cmd = ServiceCommand::new(bin_path.to_str().unwrap()) - .arg("--l2-jwt-path") - .arg(jwt_path.clone()) - .arg("--builder-jwt-path") - .arg(jwt_path.clone()) - .arg("--l2-url") - .arg(self.l2_url.as_ref().expect("l2_url not set")) - .arg("--builder-url") - .arg(self.builder_url.as_ref().expect("builder_url not set")) - .arg("--rpc-port") - .arg(Arg::Port { - name: "rpc".into(), - preferred: 8112, - }) - .arg("--debug-server-port") - .arg(Arg::Port { - name: "debug".into(), - preferred: 5555, - }) - .arg("--tracing") - .arg("--metrics") - .arg("--log-level") - .arg("trace"); - - cmd - } - - fn ready(&self) -> ReadyParams { - ReadyParams { - log_pattern: "Starting server on".to_string(), - duration: Duration::from_secs(5), - } - } -} diff --git a/src/integration/service_reth.rs b/src/integration/service_reth.rs deleted file mode 100644 index 4c7321ef..00000000 --- a/src/integration/service_reth.rs +++ /dev/null @@ -1,93 +0,0 @@ -use crate::integration::{Arg, ReadyParams, Service, ServiceCommand}; -use std::{path::PathBuf, time::Duration}; - -#[derive(Default)] -pub struct RethConfig { - jwt_secret_path: Option, - chain_config_path: Option, - p2p_secret_key: Option, - trusted_peer: Option, -} - -impl RethConfig { - pub fn new() -> Self { - Self::default() - } - - pub fn jwt_secret_path>(mut self, path: P) -> Self { - self.jwt_secret_path = Some(path.into()); - self - } - - pub fn chain_config_path>(mut self, path: P) -> Self { - self.chain_config_path = Some(path.into()); - self - } - - pub fn p2p_secret_key(mut self, key: String) -> Self { - self.p2p_secret_key = Some(key); - self - } - - pub fn trusted_peer(mut self, trusted_peer: String) -> Self { - self.trusted_peer = Some(trusted_peer); - self - } -} - -impl Service for RethConfig { - fn command(&self) -> ServiceCommand { - let mut cmd = ServiceCommand::new("op-reth") - .arg("node") - .arg("--authrpc.port") - .arg(Arg::Port { - name: "authrpc".into(), - preferred: 8551, - }) - .arg("--authrpc.jwtsecret") - .arg( - self.jwt_secret_path - .as_ref() - .expect("jwt_secret_path not set"), - ) - .arg("--chain") - .arg( - self.chain_config_path - .as_ref() - .expect("chain_config_path not set"), - ) - .arg("--datadir") - .arg(Arg::Dir { - name: "data".into(), - }) - .arg("--disable-discovery") - .arg("--port") - .arg(Arg::Port { - name: "p2p".into(), - preferred: 30303, // We do not use this port but it cannot be disabled - }) - .arg("--color") - .arg("never") - .arg("--ipcdisable"); - - if let Some(p2p_secret_key) = &self.p2p_secret_key { - cmd = cmd.arg("--p2p-secret-key").arg(Arg::FilePath { - name: "p2p_secret_key".into(), - content: p2p_secret_key.clone(), - }); - } - - if let Some(trusted_peer) = &self.trusted_peer { - cmd = cmd.arg("--trusted-peers").arg(trusted_peer); - } - - cmd - } - - fn ready(&self) -> ReadyParams { - ReadyParams { - log_pattern: "Starting consensus".to_string(), - duration: Duration::from_secs(5), - } - } -} diff --git a/src/integration/testdata/jwt_secret.hex b/src/integration/testdata/jwt_secret.hex new file mode 100644 index 00000000..6e72091c --- /dev/null +++ b/src/integration/testdata/jwt_secret.hex @@ -0,0 +1 @@ +688f5d737bad920bdfb2fc2f488d6b6209eebda1dae949a8de91398d932c517a diff --git a/src/integration/testdata/p2p_secret.hex b/src/integration/testdata/p2p_secret.hex new file mode 100644 index 00000000..499ea6cd --- /dev/null +++ b/src/integration/testdata/p2p_secret.hex @@ -0,0 +1 @@ +a11ac89899cd86e36b6fb881ec1255b8a92a688790b7d950f8b7d8dd626671fb From 8c8bbc3b1132185542843b267da51f36c6081af7 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Thu, 3 Apr 2025 12:58:16 -0700 Subject: [PATCH 02/20] wip --- src/integration/containers/op_reth.rs | 157 ++++++++++++++------- src/integration/containers/rollup_boost.rs | 35 ++--- src/integration/integration_test.rs | 15 +- src/integration/mod.rs | 62 ++++---- src/tracing.rs | 16 +++ 5 files changed, 164 insertions(+), 121 deletions(-) diff --git a/src/integration/containers/op_reth.rs b/src/integration/containers/op_reth.rs index 214d157c..cfbeec31 100644 --- a/src/integration/containers/op_reth.rs +++ b/src/integration/containers/op_reth.rs @@ -1,14 +1,20 @@ use std::{ borrow::Cow, collections::HashMap, + fs::File, + io::BufReader, path::PathBuf, time::{SystemTime, UNIX_EPOCH}, }; +use http::Uri; use serde_json::Value; -use testcontainers::{CopyToContainer, Image, core::WaitFor}; +use testcontainers::{ + ContainerAsync, CopyToContainer, Image, + core::{ContainerPort, WaitFor}, +}; -use crate::integration::JWT_SECRET; +use crate::integration::L2_P2P_ENODE; const NAME: &str = "ghcr.io/paradigmxyz/op-reth"; const TAG: &str = "v1.3.4"; @@ -25,9 +31,7 @@ pub struct OpRethConfig { pub p2p_port: u16, pub color: String, pub ipcdisable: bool, - pub env_vars: HashMap, - copy_to_sources: Vec, } impl Default for OpRethConfig { @@ -49,37 +53,33 @@ impl Default for OpRethConfig { p2p_port: 30303, color: "never".to_string(), ipcdisable: true, - env_vars: Default::default(), - copy_to_sources: vec![], } - .update_copy_to_sources() } } impl OpRethConfig { - pub fn with_env_vars( - mut self, - env_vars: impl IntoIterator, impl Into)>, - ) -> Self { - self.env_vars - .extend(env_vars.into_iter().map(|(k, v)| (k.into(), v.into()))); + pub fn set_trusted_peers(mut self, trusted_peers: Vec) -> Self { + self.trusted_peers = trusted_peers; self } - pub fn set_trusted_peers(mut self, trusted_peers: Vec) -> Self { - self.trusted_peers = trusted_peers; + pub fn set_jwt_secret(mut self, jwt_secret: PathBuf) -> Self { + self.jwt_secret = jwt_secret; self } - fn update_copy_to_sources(mut self) -> Self { + pub fn set_p2p_secret(mut self, p2p_secret: Option) -> Self { + self.p2p_secret = p2p_secret; + self + } + + pub fn build(self) -> OpRethImage { // Write the genesis file to the test directory and update the timestamp to the current time let genesis = { - // Read the template file - let template = include_str!("../testdata/genesis.json"); - - // Parse the JSON - let mut genesis: Value = serde_json::from_str(template).unwrap(); + let file = File::open(&self.chain).unwrap(); + let reader = BufReader::new(file); + let mut genesis: Value = serde_json::from_reader(reader).unwrap(); // Update the timestamp field let timestamp = SystemTime::now() @@ -95,33 +95,57 @@ impl OpRethConfig { serde_json::to_string_pretty(&genesis).unwrap() }; - self.copy_to_sources = vec![ - CopyToContainer::new(self.jwt_secret.clone(), "jwt_secret.hex".to_string()), - CopyToContainer::new(genesis.into_bytes(), "genesis.json".to_string()), + println!("jwt_path: {:?}", self.jwt_secret); + println!( + "jwt: {:?}", + std::fs::read_to_string(&self.jwt_secret).unwrap() + ); + let mut copy_to_sources = vec![ + CopyToContainer::new( + std::fs::read(&self.jwt_secret).unwrap(), + "/jwt_secret.hex".to_string(), + ), + CopyToContainer::new(genesis.into_bytes(), "/genesis.json".to_string()), ]; if let Some(p2p_secret) = &self.p2p_secret { - self.copy_to_sources.push(CopyToContainer::new( - p2p_secret.clone(), - "p2p_secret.hex".to_string(), + println!("p2p_path: {:?}", p2p_secret); + println!("p2p: {:?}", std::fs::read_to_string(p2p_secret).unwrap()); + copy_to_sources.push(CopyToContainer::new( + "a11ac89899cd86e36b6fb881ec1255b8a92a688790b7d950f8b7d8dd626671fb" + .to_string() + .into_bytes(), + "/p2p_secret.hex".to_string(), )); } - self - } + let expose_ports = vec![ + ContainerPort::Tcp(self.authrpc_port), + ContainerPort::Tcp(self.p2p_port), + ]; - pub fn set_jwt_secret(mut self, jwt_secret: PathBuf) -> Self { - self.jwt_secret = jwt_secret; - self.update_copy_to_sources() + OpRethImage { + config: self, + copy_to_sources, + expose_ports, + } } +} - pub fn set_p2p_secret(mut self, p2p_secret: Option) -> Self { - self.p2p_secret = p2p_secret; - self.update_copy_to_sources() +impl OpRethImage { + pub fn config(&self) -> &OpRethConfig { + &self.config } } -impl Image for OpRethConfig { +#[derive(Debug, Clone)] +pub struct OpRethImage { + config: OpRethConfig, + copy_to_sources: Vec, + expose_ports: Vec, +} + +impl Image for OpRethImage { fn name(&self) -> &str { NAME } @@ -137,7 +161,7 @@ impl Image for OpRethConfig { fn env_vars( &self, ) -> impl IntoIterator>, impl Into>)> { - &self.env_vars + &self.config.env_vars } fn copy_to_sources(&self) -> impl IntoIterator { @@ -148,28 +172,63 @@ impl Image for OpRethConfig { let mut cmd = vec![ "node".to_string(), "--authrpc.port".to_string(), - self.authrpc_port.to_string(), - "--authrpc.jwtsecret=jwt_secret.hex".to_string(), - "--chain=genesis.json".to_string(), + self.config.authrpc_port.to_string(), + "--authrpc.jwtsecret=/jwt_secret.hex".to_string(), + "--chain=/genesis.json".to_string(), "--datadir".to_string(), - self.datadir.clone(), + self.config.datadir.clone(), "--port".to_string(), - self.p2p_port.to_string(), + self.config.p2p_port.to_string(), "--color".to_string(), - self.color.clone(), + self.config.color.clone(), ]; - if self.p2p_secret.is_some() { - cmd.push("--p2p-secret-key=p2p_secret.hex".to_string()); + if self.config.p2p_secret.is_some() { + cmd.push("--p2p-secret-key=/p2p_secret.hex".to_string()); } - if !self.trusted_peers.is_empty() { - cmd.extend(["--trusted-peers".to_string(), self.trusted_peers.join(",")]); + if !self.config.trusted_peers.is_empty() { + cmd.extend([ + "--trusted-peers".to_string(), + self.config.trusted_peers.join(","), + ]); } - if self.disable_discovery { + if self.config.disable_discovery { cmd.push("--disable-discovery".to_string()); } - if self.ipcdisable { + if self.config.ipcdisable { cmd.push("--ipcdisable".to_string()); } cmd } + + fn expose_ports(&self) -> &[ContainerPort] { + &self.expose_ports + } +} + +pub trait OpRethMehods { + async fn auth_rpc(&self) -> eyre::Result; + async fn enode(&self) -> eyre::Result; +} + +impl OpRethMehods for ContainerAsync { + async fn auth_rpc(&self) -> eyre::Result { + Ok(format!( + "http://{}:{}", + self.get_host().await?, + self.get_host_port_ipv4(self.image().config.authrpc_port) + .await? + ) + .parse()?) + } + + async fn enode(&self) -> eyre::Result { + Ok(format!( + "enode://{}@{}:{}", + L2_P2P_ENODE, + self.get_host().await?, + self.get_host_port_ipv4(self.image().config.p2p_port) + .await? + ) + .parse()?) + } } diff --git a/src/integration/containers/rollup_boost.rs b/src/integration/containers/rollup_boost.rs index c51393b8..2bf94bf9 100644 --- a/src/integration/containers/rollup_boost.rs +++ b/src/integration/containers/rollup_boost.rs @@ -5,9 +5,7 @@ use std::{ }; use clap::Parser; -use http::Uri; use tokio::task::JoinHandle; -use tracing::{Level, instrument::WithSubscriber as _}; use tracing_subscriber::fmt::MakeWriter; #[derive(Clone)] @@ -16,26 +14,22 @@ struct SharedFileWriter { } impl<'a> MakeWriter<'a> for SharedFileWriter { - type Writer = SharedFileGuard<'a>; + type Writer = Self; fn make_writer(&'a self) -> Self::Writer { - SharedFileGuard { - guard: self.file.lock().unwrap(), // you can handle poisoning if needed - } + self.clone() } } -pub struct SharedFileGuard<'a> { - guard: std::sync::MutexGuard<'a, File>, -} - -impl Write for SharedFileGuard<'_> { +impl Write for SharedFileWriter { fn write(&mut self, buf: &[u8]) -> std::io::Result { - self.guard.write(buf) + println!("Writing to file..."); + self.file.lock().unwrap().write(buf) } fn flush(&mut self) -> std::io::Result<()> { - self.guard.flush() + println!("Flushing file..."); + self.file.lock().unwrap().flush() } } @@ -44,7 +38,6 @@ use crate::Args; #[derive(Debug)] pub struct RollupBoost { pub args: Args, - pub log_file: Arc>, pub handle: JoinHandle>, } @@ -78,21 +71,11 @@ impl Default for RollupBoostConfig { } impl RollupBoostConfig { - pub fn start(self, log_file: File) -> RollupBoost { - let log_file = Arc::new(Mutex::new(log_file)); - let writer = SharedFileWriter { - file: log_file.clone(), - }; - let subscriber = tracing_subscriber::fmt() - .with_writer(writer) - .with_env_filter(format!("rollup_boost={}", Level::TRACE)) - .finish(); - - let handle = tokio::spawn(self.args.clone().run().with_subscriber(subscriber)); + pub fn start(self) -> RollupBoost { + let handle = tokio::spawn(self.args.clone().run()); RollupBoost { args: self.args, - log_file, handle, } } diff --git a/src/integration/integration_test.rs b/src/integration/integration_test.rs index ebe5a79e..62b7fdf1 100644 --- a/src/integration/integration_test.rs +++ b/src/integration/integration_test.rs @@ -2,7 +2,6 @@ mod tests { use alloy_primitives::B256; use serde_json::Value; - use std::io::Read; use std::sync::{Arc, Mutex}; use std::time::Duration; @@ -164,7 +163,7 @@ mod tests { } // stop the builder - harness.builder.stop().await; + harness.builder.stop().await?; // create 3 new blocks that are processed by the l2 builder for _ in 0..3 { @@ -173,7 +172,7 @@ mod tests { } // start the builder again - harness.builder.start().await; + harness.builder.start().await?; // the next block is computed by the l2 builder because the builder is not synced with the previous 3 blocks // But, once the builder receives the FCU request from rollup-boost, it will sync up the blocks with the @@ -274,15 +273,9 @@ mod tests { // check that at some point we had the log "builder payload was not valid" which signals // that the builder returned a payload that was not valid and rollup-boost did not process it. // read lines - let mut buf = String::new(); - harness - .rollup_boost - .log_file - .lock() - .unwrap() - .read_to_string(&mut buf)?; + let logs = std::fs::read_to_string(harness.rollup_boost.args.log_file.unwrap())?; assert!( - buf.contains("Invalid payload"), + logs.contains("Invalid payload"), "Logs should contain the message 'builder payload was not valid'" ); diff --git a/src/integration/mod.rs b/src/integration/mod.rs index 4351ca18..5ce3720d 100644 --- a/src/integration/mod.rs +++ b/src/integration/mod.rs @@ -10,20 +10,19 @@ use alloy_rpc_types_engine::{ ForkchoiceState, ForkchoiceUpdated, PayloadAttributes, PayloadId, PayloadStatus, PayloadStatusEnum, }; -use containers::op_reth::OpRethConfig; +use containers::op_reth::{OpRethConfig, OpRethImage, OpRethMehods}; use containers::rollup_boost::{RollupBoost, RollupBoostConfig}; use eyre::bail; use futures::FutureExt; use futures::future::BoxFuture; +use http::Uri; use jsonrpsee::http_client::{HttpClient, transport::HttpBackend}; use jsonrpsee::proc_macros::rpc; use op_alloy_rpc_types_engine::{OpExecutionPayloadEnvelopeV3, OpPayloadAttributes}; use proxy::{DynHandlerFn, start_proxy_server}; -use std::io::Read; use std::net::TcpListener; use std::path::PathBuf; use std::str::FromStr; -use std::sync::Arc; use std::time::{Duration, SystemTime}; use testcontainers::core::logs::LogFrame; use testcontainers::core::logs::consumer::LogConsumer; @@ -37,14 +36,13 @@ use time::{OffsetDateTime, format_description}; /// Default JWT token for testing purposes pub const JWT_SECRET: &str = "688f5d737bad920bdfb2fc2f488d6b6209eebda1dae949a8de91398d932c517a"; -pub const L2_P2P_SECRET: &str = "a11ac89899cd86e36b6fb881ec1255b8a92a688790b7d950f8b7d8dd626671fb"; pub const L2_P2P_ENODE: &str = "3479db4d9217fb5d7a8ed4d61ac36e120b05d36c2eefb795dc42ff2e971f251a2315f5649ea1833271e020b9adc98d5db9973c7ed92d6b2f1f2223088c3d852f"; mod containers; mod integration_test; mod proxy; -struct LoggingConsumer { +pub struct LoggingConsumer { target: String, log_file: tokio::sync::Mutex, } @@ -67,12 +65,6 @@ impl LogConsumer for LoggingConsumer { } } -pub struct TestEnv { - pub l2: ContainerAsync, - pub builder: ContainerAsync, - pub rollup_boost: RollupBoost, -} - pub async fn wait_for_log( container: ContainerAsync, pattern: &str, @@ -203,8 +195,8 @@ pub trait BlockApi { /// Test flavor that sets up one Rollup-boost instance connected to two Reth nodes pub struct RollupBoostTestHarness { - pub l2: ContainerAsync, - pub builder: ContainerAsync, + pub l2: ContainerAsync, + pub builder: ContainerAsync, pub rollup_boost: RollupBoost, } @@ -273,29 +265,27 @@ impl RollupBoostTestHarnessBuilder { async fn build(self) -> eyre::Result { let l2_log_consumer = self.log_consumer("l2").await?; let builder_log_consumer = self.log_consumer("builder").await?; - let rollup_boost_log_file = self.log_file("rollup_boost")?; + let rollup_boost_log_file_path = self.file_path("rollup_boost")?; let l2 = OpRethConfig::default() - // .set_p2p_secret(Some(PathBuf::from("../testdata/p2p_secret.hex"))) + .set_p2p_secret(Some(PathBuf::from(format!( + "{}/src/integration/testdata/p2p_secret.hex", + env!("CARGO_MANIFEST_DIR") + )))) + .build() .with_log_consumer(l2_log_consumer) .start() .await?; - let l2_url = format!("http://127.0.0.1:{}", l2.get_host_port_ipv4(8545).await?); - let l2_enode = format!( - "enode://{}@127.0.0.1:{}", - L2_P2P_ENODE, - l2.get_host_port_ipv4(30303).await? - ); - let builder = OpRethConfig::default() - .set_trusted_peers(vec![l2_enode.clone()]) + .set_trusted_peers(vec![l2.enode().await?.to_string()]) + .build() .with_log_consumer(builder_log_consumer) .start() .await?; - let mut builder_authrpc_port = builder.get_host_port_ipv4(8551).await?; // run a proxy in between the builder and the rollup-boost if the proxy_handler is set + let mut builder_authrpc_port = builder.image().config().authrpc_port; if let Some(proxy_handler) = self.proxy_handler { let proxy_port = get_available_port().expect("no available port"); let _ = start_proxy_server(proxy_handler, proxy_port, builder_authrpc_port).await; @@ -305,9 +295,10 @@ impl RollupBoostTestHarnessBuilder { // Start Rollup-boost instance let mut rollup_boost = RollupBoostConfig::default(); - rollup_boost.args.l2_client.l2_url = l2_url.try_into().unwrap(); + rollup_boost.args.l2_client.l2_url = l2.auth_rpc().await?.try_into().unwrap(); rollup_boost.args.builder.builder_url = builder_url.try_into().unwrap(); - let rollup_boost = rollup_boost.start(rollup_boost_log_file); + rollup_boost.args.log_file = Some(rollup_boost_log_file_path); + let rollup_boost = rollup_boost.start(); Ok(RollupBoostTestHarness { l2, @@ -319,7 +310,8 @@ impl RollupBoostTestHarnessBuilder { impl RollupBoostTestHarness { pub async fn get_block_generator(&self) -> eyre::Result { - let validator = BlockBuilderCreatorValidator::new(self.rollup_boost.log_file.clone()); + let validator = + BlockBuilderCreatorValidator::new(self.rollup_boost.args.log_file.clone().unwrap()); let engine_api = EngineApi::new(&self.rollup_boost.rpc_endpoint(), JWT_SECRET)?; @@ -441,19 +433,18 @@ impl SimpleBlockGenerator { } pub struct BlockBuilderCreatorValidator { - file: Arc>, + file: PathBuf, } impl<'a> BlockBuilderCreatorValidator { - pub fn new(file: Arc>) -> Self { + pub fn new(file: PathBuf) -> Self { Self { file } } } impl<'a> BlockBuilderCreatorValidator { pub async fn get_block_creator(&self, block_hash: B256) -> eyre::Result> { - let mut contents = String::new(); - self.file.lock().unwrap().read_to_string(&mut contents)?; + let contents = std::fs::read_to_string(&self.file)?; let search_query = format!("returning block hash={:#x}", block_hash); @@ -507,13 +498,14 @@ fn create_deposit_tx() -> Bytes { buffer_without_header.to_vec().into() } +fn local_host(port: u16) -> Uri { + format!("http://localhost:{port}").parse::().unwrap() +} + fn get_available_port() -> Option { (8000..9000).find(|port| port_is_available(*port)) } fn port_is_available(port: u16) -> bool { - match TcpListener::bind(("127.0.0.1", port)) { - Ok(_) => true, - Err(_) => false, - } + TcpListener::bind(("127.0.0.1", port)).is_ok() } diff --git a/src/tracing.rs b/src/tracing.rs index fdd917c7..17664c48 100644 --- a/src/tracing.rs +++ b/src/tracing.rs @@ -9,6 +9,7 @@ use tracing::level_filters::LevelFilter; use tracing_opentelemetry::OpenTelemetryLayer; use tracing_subscriber::Layer; use tracing_subscriber::filter::Targets; +use tracing_subscriber::fmt::writer::BoxMakeWriter; use tracing_subscriber::layer::SubscriberExt; use crate::cli::{Args, LogFormat}; @@ -83,6 +84,17 @@ pub fn init_tracing(args: &Args) -> eyre::Result<()> { .with_default(LevelFilter::INFO) .with_target(&filter_name, args.log_level); + let writer = if let Some(path) = &args.log_file { + let file = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open(path) + .context("Failed to open log file")?; + BoxMakeWriter::new(file) + } else { + BoxMakeWriter::new(std::io::stdout) + }; + // Weird control flow here is required because of type system if args.tracing { global::set_text_map_propagator(TraceContextPropagator::new()); @@ -120,6 +132,7 @@ pub fn init_tracing(args: &Args) -> eyre::Result<()> { tracing_subscriber::fmt::layer() .json() .with_ansi(false) + .with_writer(writer) .with_filter(log_filter.clone()), ), )?; @@ -129,6 +142,7 @@ pub fn init_tracing(args: &Args) -> eyre::Result<()> { registry.with( tracing_subscriber::fmt::layer() .with_ansi(false) + .with_writer(writer) .with_filter(log_filter.clone()), ), )?; @@ -142,6 +156,7 @@ pub fn init_tracing(args: &Args) -> eyre::Result<()> { tracing_subscriber::fmt::layer() .json() .with_ansi(false) + .with_writer(writer) .with_filter(log_filter.clone()), ), )?; @@ -151,6 +166,7 @@ pub fn init_tracing(args: &Args) -> eyre::Result<()> { registry.with( tracing_subscriber::fmt::layer() .with_ansi(false) + .with_writer(writer) .with_filter(log_filter.clone()), ), )?; From d8cd87051a36b0fb2f192f4709ad74e775bfc5a8 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Thu, 3 Apr 2025 16:30:34 -0700 Subject: [PATCH 03/20] wip --- src/integration/containers/op_reth.rs | 45 +++++++------- src/integration/containers/rollup_boost.rs | 58 +++++++------------ src/integration/integration_test.rs | 2 +- src/integration/mod.rs | 11 ++-- src/integration/tests/mod.rs | 0 .../tests/test_integration_no_tx_pool.rs | 26 +++++++++ 6 files changed, 77 insertions(+), 65 deletions(-) create mode 100644 src/integration/tests/mod.rs create mode 100644 src/integration/tests/test_integration_no_tx_pool.rs diff --git a/src/integration/containers/op_reth.rs b/src/integration/containers/op_reth.rs index cfbeec31..69943b48 100644 --- a/src/integration/containers/op_reth.rs +++ b/src/integration/containers/op_reth.rs @@ -19,16 +19,17 @@ use crate::integration::L2_P2P_ENODE; const NAME: &str = "ghcr.io/paradigmxyz/op-reth"; const TAG: &str = "v1.3.4"; +const AUTH_RPC_PORT: u16 = 8551; +const P2P_PORT: u16 = 30303; + #[derive(Debug, Clone)] pub struct OpRethConfig { chain: PathBuf, jwt_secret: PathBuf, p2p_secret: Option, pub trusted_peers: Vec, - pub authrpc_port: u16, pub datadir: String, pub disable_discovery: bool, - pub p2p_port: u16, pub color: String, pub ipcdisable: bool, pub env_vars: HashMap, @@ -47,10 +48,8 @@ impl Default for OpRethConfig { )), p2p_secret: None, trusted_peers: vec![], - authrpc_port: 8551, datadir: "data".to_string(), disable_discovery: true, - p2p_port: 30303, color: "never".to_string(), ipcdisable: true, env_vars: Default::default(), @@ -95,23 +94,20 @@ impl OpRethConfig { serde_json::to_string_pretty(&genesis).unwrap() }; - println!("jwt_path: {:?}", self.jwt_secret); - println!( - "jwt: {:?}", - std::fs::read_to_string(&self.jwt_secret).unwrap() - ); let mut copy_to_sources = vec![ CopyToContainer::new( - std::fs::read(&self.jwt_secret).unwrap(), + // std::fs::read(&self.jwt_secret).unwrap(), + "688f5d737bad920bdfb2fc2f488d6b6209eebda1dae949a8de91398d932c517a" + .to_string() + .into_bytes(), "/jwt_secret.hex".to_string(), ), CopyToContainer::new(genesis.into_bytes(), "/genesis.json".to_string()), ]; if let Some(p2p_secret) = &self.p2p_secret { - println!("p2p_path: {:?}", p2p_secret); - println!("p2p: {:?}", std::fs::read_to_string(p2p_secret).unwrap()); copy_to_sources.push(CopyToContainer::new( + // std::fs::read(p2p_secret).unwrap(), "a11ac89899cd86e36b6fb881ec1255b8a92a688790b7d950f8b7d8dd626671fb" .to_string() .into_bytes(), @@ -119,10 +115,7 @@ impl OpRethConfig { )); } - let expose_ports = vec![ - ContainerPort::Tcp(self.authrpc_port), - ContainerPort::Tcp(self.p2p_port), - ]; + let expose_ports = vec![ContainerPort::Tcp(8551), ContainerPort::Tcp(30303)]; OpRethImage { config: self, @@ -171,14 +164,15 @@ impl Image for OpRethImage { fn cmd(&self) -> impl IntoIterator>> { let mut cmd = vec![ "node".to_string(), - "--authrpc.port".to_string(), - self.config.authrpc_port.to_string(), + "--http".to_string(), + "--http.addr=0.0.0.0".to_string(), + "--authrpc.port=8551".to_string(), + "--authrpc.addr=0.0.0.0".to_string(), "--authrpc.jwtsecret=/jwt_secret.hex".to_string(), "--chain=/genesis.json".to_string(), "--datadir".to_string(), self.config.datadir.clone(), - "--port".to_string(), - self.config.p2p_port.to_string(), + "--port=30303".to_string(), "--color".to_string(), self.config.color.clone(), ]; @@ -207,16 +201,20 @@ impl Image for OpRethImage { pub trait OpRethMehods { async fn auth_rpc(&self) -> eyre::Result; + async fn auth_rpc_port(&self) -> eyre::Result; async fn enode(&self) -> eyre::Result; } impl OpRethMehods for ContainerAsync { + async fn auth_rpc_port(&self) -> eyre::Result { + Ok(self.get_host_port_ipv4(AUTH_RPC_PORT).await?) + } + async fn auth_rpc(&self) -> eyre::Result { Ok(format!( "http://{}:{}", self.get_host().await?, - self.get_host_port_ipv4(self.image().config.authrpc_port) - .await? + self.get_host_port_ipv4(AUTH_RPC_PORT).await? ) .parse()?) } @@ -226,8 +224,7 @@ impl OpRethMehods for ContainerAsync { "enode://{}@{}:{}", L2_P2P_ENODE, self.get_host().await?, - self.get_host_port_ipv4(self.image().config.p2p_port) - .await? + self.get_host_port_ipv4(P2P_PORT).await? ) .parse()?) } diff --git a/src/integration/containers/rollup_boost.rs b/src/integration/containers/rollup_boost.rs index 2bf94bf9..b8327045 100644 --- a/src/integration/containers/rollup_boost.rs +++ b/src/integration/containers/rollup_boost.rs @@ -1,47 +1,19 @@ -use std::{ - fs::File, - io::Write, - sync::{Arc, Mutex}, -}; - use clap::Parser; use tokio::task::JoinHandle; -use tracing_subscriber::fmt::MakeWriter; - -#[derive(Clone)] -struct SharedFileWriter { - file: Arc>, -} - -impl<'a> MakeWriter<'a> for SharedFileWriter { - type Writer = Self; - - fn make_writer(&'a self) -> Self::Writer { - self.clone() - } -} - -impl Write for SharedFileWriter { - fn write(&mut self, buf: &[u8]) -> std::io::Result { - println!("Writing to file..."); - self.file.lock().unwrap().write(buf) - } - - fn flush(&mut self) -> std::io::Result<()> { - println!("Flushing file..."); - self.file.lock().unwrap().flush() - } -} use crate::Args; #[derive(Debug)] pub struct RollupBoost { - pub args: Args, + args: Args, pub handle: JoinHandle>, } impl RollupBoost { + pub fn args(&self) -> &Args { + &self.args + } + pub fn rpc_endpoint(&self) -> String { format!("http://localhost:{}", self.args.rpc_port) } @@ -61,8 +33,15 @@ impl Default for RollupBoostConfig { Self { args: Args::parse_from([ "rollup-boost", - "--l2-jwt-path=../testdata/jwt_secret.hex", - "--builder-jwt-path=../testdata/jwt_secret.hex", + &format!( + "--l2-jwt-path={}/src/integration/testdata/jwt_secret.hex", + env!("CARGO_MANIFEST_DIR") + ), + &format!( + "--builder-jwt-path={}/src/integration/testdata/jwt_secret.hex", + env!("CARGO_MANIFEST_DIR") + ), + "--log-level=trace", "--tracing", "--metrics", ]), @@ -72,7 +51,14 @@ impl Default for RollupBoostConfig { impl RollupBoostConfig { pub fn start(self) -> RollupBoost { - let handle = tokio::spawn(self.args.clone().run()); + let args = self.args.clone(); + let handle = tokio::spawn(async move { + let res = args.clone().run().await; + if let Err(e) = &res { + eprintln!("Error: {:?}", e); + } + res + }); RollupBoost { args: self.args, diff --git a/src/integration/integration_test.rs b/src/integration/integration_test.rs index 62b7fdf1..0d7078d9 100644 --- a/src/integration/integration_test.rs +++ b/src/integration/integration_test.rs @@ -273,7 +273,7 @@ mod tests { // check that at some point we had the log "builder payload was not valid" which signals // that the builder returned a payload that was not valid and rollup-boost did not process it. // read lines - let logs = std::fs::read_to_string(harness.rollup_boost.args.log_file.unwrap())?; + let logs = std::fs::read_to_string(harness.rollup_boost.args().log_file.clone().unwrap())?; assert!( logs.contains("Invalid payload"), "Logs should contain the message 'builder payload was not valid'" diff --git a/src/integration/mod.rs b/src/integration/mod.rs index 5ce3720d..63bcfa40 100644 --- a/src/integration/mod.rs +++ b/src/integration/mod.rs @@ -24,6 +24,7 @@ use std::net::TcpListener; use std::path::PathBuf; use std::str::FromStr; use std::time::{Duration, SystemTime}; +use testcontainers::core::ContainerPort; use testcontainers::core::logs::LogFrame; use testcontainers::core::logs::consumer::LogConsumer; use testcontainers::runners::AsyncRunner; @@ -41,6 +42,7 @@ pub const L2_P2P_ENODE: &str = "3479db4d9217fb5d7a8ed4d61ac36e120b05d36c2eefb795 mod containers; mod integration_test; mod proxy; +mod tests; pub struct LoggingConsumer { target: String, @@ -200,8 +202,6 @@ pub struct RollupBoostTestHarness { pub rollup_boost: RollupBoost, } -const PROXY_START_PORT: u16 = 4444; - pub struct RollupBoostTestHarnessBuilder { test_name: String, proxy_handler: Option, @@ -284,8 +284,11 @@ impl RollupBoostTestHarnessBuilder { .start() .await?; + println!("l2 authrpc: {}", l2.auth_rpc().await?); + println!("builder authrpc: {}", builder.auth_rpc().await?); + // run a proxy in between the builder and the rollup-boost if the proxy_handler is set - let mut builder_authrpc_port = builder.image().config().authrpc_port; + let mut builder_authrpc_port = builder.auth_rpc_port().await?; if let Some(proxy_handler) = self.proxy_handler { let proxy_port = get_available_port().expect("no available port"); let _ = start_proxy_server(proxy_handler, proxy_port, builder_authrpc_port).await; @@ -311,7 +314,7 @@ impl RollupBoostTestHarnessBuilder { impl RollupBoostTestHarness { pub async fn get_block_generator(&self) -> eyre::Result { let validator = - BlockBuilderCreatorValidator::new(self.rollup_boost.args.log_file.clone().unwrap()); + BlockBuilderCreatorValidator::new(self.rollup_boost.args().log_file.clone().unwrap()); let engine_api = EngineApi::new(&self.rollup_boost.rpc_endpoint(), JWT_SECRET)?; diff --git a/src/integration/tests/mod.rs b/src/integration/tests/mod.rs new file mode 100644 index 00000000..e69de29b diff --git a/src/integration/tests/test_integration_no_tx_pool.rs b/src/integration/tests/test_integration_no_tx_pool.rs new file mode 100644 index 00000000..09a1d912 --- /dev/null +++ b/src/integration/tests/test_integration_no_tx_pool.rs @@ -0,0 +1,26 @@ +use alloy_primitives::B256; +use serde_json::Value; +use std::sync::{Arc, Mutex}; +use std::time::Duration; + +use crate::integration::RollupBoostTestHarnessBuilder; +use crate::server::ExecutionMode; +use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; + +#[tokio::test] +async fn test_integration_simple() -> eyre::Result<()> { + let harness = RollupBoostTestHarnessBuilder::new("test_integration_simple") + .build() + .await?; + let mut block_generator = harness.get_block_generator().await?; + + for _ in 0..5 { + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!( + block_creator.is_builder(), + "Block creator should be the builder" + ); + } + + Ok(()) +} From a9e850416c4d005a5b3bc83ba2781a3eb79c7818 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Thu, 3 Apr 2025 16:57:04 -0700 Subject: [PATCH 04/20] move run to lib --- src/bin/main.rs | 133 +---------------------------------------------- src/cli.rs | 135 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 136 insertions(+), 132 deletions(-) diff --git a/src/bin/main.rs b/src/bin/main.rs index b4538743..e332e3d4 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -1,143 +1,14 @@ -#![allow(clippy::complexity)] -use ::tracing::info; use clap::Parser; -use rollup_boost::{ - Args, Commands, DebugClient, DebugCommands, PayloadSource, ProxyLayer, RollupBoostServer, - RpcClient, init_metrics, init_tracing, -}; -use std::net::SocketAddr; +use rollup_boost::Args; -use alloy_rpc_types_engine::JwtSecret; use dotenv::dotenv; -use eyre::bail; -use jsonrpsee::RpcModule; -use jsonrpsee::server::Server; - -use tokio::signal::unix::{SignalKind, signal as unix_signal}; #[tokio::main] async fn main() -> eyre::Result<()> { - // Load .env file dotenv().ok(); rustls::crypto::ring::default_provider() .install_default() .expect("Failed to install TLS ring CryptoProvider"); - let args: Args = Args::parse(); - - let debug_addr = format!("{}:{}", args.debug_host, args.debug_server_port); - - // Handle commands if present - if let Some(cmd) = args.command { - let debug_addr = format!("http://{}", debug_addr); - return match cmd { - Commands::Debug { command } => match command { - DebugCommands::SetExecutionMode { execution_mode } => { - let client = DebugClient::new(debug_addr.as_str())?; - let result = client.set_execution_mode(execution_mode).await.unwrap(); - println!("Response: {:?}", result.execution_mode); - - Ok(()) - } - DebugCommands::ExecutionMode {} => { - let client = DebugClient::new(debug_addr.as_str())?; - let result = client.get_execution_mode().await?; - println!("Execution mode: {:?}", result.execution_mode); - - Ok(()) - } - }, - }; - } - - init_tracing(&args)?; - init_metrics(&args)?; - - let l2_client_args = args.l2_client; - - let l2_auth_jwt = if let Some(secret) = l2_client_args.l2_jwt_token { - secret - } else if let Some(path) = l2_client_args.l2_jwt_path.as_ref() { - JwtSecret::from_file(path)? - } else { - bail!("Missing L2 Client JWT secret"); - }; - - let l2_client = RpcClient::new( - l2_client_args.l2_url.clone(), - l2_auth_jwt, - l2_client_args.l2_timeout, - PayloadSource::L2, - )?; - - let builder_args = args.builder; - let builder_auth_jwt = if let Some(secret) = builder_args.builder_jwt_token { - secret - } else if let Some(path) = builder_args.builder_jwt_path.as_ref() { - JwtSecret::from_file(path)? - } else { - bail!("Missing Builder JWT secret"); - }; - - let builder_client = RpcClient::new( - builder_args.builder_url.clone(), - builder_auth_jwt, - builder_args.builder_timeout, - PayloadSource::Builder, - )?; - - let boost_sync_enabled = !args.no_boost_sync; - if boost_sync_enabled { - info!("Boost sync enabled"); - } - - let rollup_boost = RollupBoostServer::new( - l2_client, - builder_client, - boost_sync_enabled, - args.execution_mode, - ); - - // Spawn the debug server - rollup_boost.start_debug_server(debug_addr.as_str()).await?; - - let module: RpcModule<()> = rollup_boost.try_into()?; - - // Build and start the server - info!("Starting server on :{}", args.rpc_port); - - let http_middleware = tower::ServiceBuilder::new().layer(ProxyLayer::new( - l2_client_args.l2_url, - l2_auth_jwt, - builder_args.builder_url, - builder_auth_jwt, - )); - - let server = Server::builder() - .set_http_middleware(http_middleware) - .build(format!("{}:{}", args.rpc_host, args.rpc_port).parse::()?) - .await?; - let handle = server.start(module); - - let stop_handle = handle.clone(); - - // Capture SIGINT and SIGTERM - let mut sigint = unix_signal(SignalKind::interrupt())?; - let mut sigterm = unix_signal(SignalKind::terminate())?; - - tokio::select! { - _ = handle.stopped() => { - // The server has already shut down by itself - info!("Server stopped"); - } - _ = sigint.recv() => { - info!("Received SIGINT, shutting down gracefully..."); - let _ = stop_handle.stop(); - } - _ = sigterm.recv() => { - info!("Received SIGTERM, shutting down gracefully..."); - let _ = stop_handle.stop(); - } - } - Ok(()) + Args::parse().run().await } diff --git a/src/cli.rs b/src/cli.rs index e973fb91..a9a0b132 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,8 +1,16 @@ +use std::{net::SocketAddr, path::PathBuf}; + +use alloy_rpc_types_engine::JwtSecret; use clap::{Parser, Subcommand}; -use tracing::Level; +use eyre::bail; +use jsonrpsee::{RpcModule, server::Server}; +use tokio::signal::unix::{SignalKind, signal as unix_signal}; +use tracing::{Level, info}; use crate::{ + DebugClient, PayloadSource, ProxyLayer, RollupBoostServer, RpcClient, client::rpc::{BuilderArgs, L2ClientArgs}, + init_metrics, init_tracing, server::ExecutionMode, }; @@ -58,6 +66,10 @@ pub struct Args { #[arg(long, env, default_value = "text")] pub log_format: LogFormat, + /// Redirect logs to a file + #[arg(long, env)] + pub log_file: Option, + /// Host to run the debug server on #[arg(long, env, default_value = "127.0.0.1")] pub debug_host: String, @@ -71,6 +83,127 @@ pub struct Args { pub execution_mode: ExecutionMode, } +impl Args { + pub async fn run(self) -> eyre::Result<()> { + let debug_addr = format!("{}:{}", self.debug_host, self.debug_server_port); + + // Handle commands if present + if let Some(cmd) = self.command { + let debug_addr = format!("http://{}", debug_addr); + return match cmd { + Commands::Debug { command } => match command { + DebugCommands::SetExecutionMode { execution_mode } => { + let client = DebugClient::new(debug_addr.as_str())?; + let result = client.set_execution_mode(execution_mode).await.unwrap(); + println!("Response: {:?}", result.execution_mode); + + Ok(()) + } + DebugCommands::ExecutionMode {} => { + let client = DebugClient::new(debug_addr.as_str())?; + let result = client.get_execution_mode().await?; + println!("Execution mode: {:?}", result.execution_mode); + + Ok(()) + } + }, + }; + } + + init_tracing(&self)?; + init_metrics(&self)?; + + let l2_client_args = self.l2_client; + + let l2_auth_jwt = if let Some(secret) = l2_client_args.l2_jwt_token { + secret + } else if let Some(path) = l2_client_args.l2_jwt_path.as_ref() { + JwtSecret::from_file(path)? + } else { + bail!("Missing L2 Client JWT secret"); + }; + + let l2_client = RpcClient::new( + l2_client_args.l2_url.clone(), + l2_auth_jwt, + l2_client_args.l2_timeout, + PayloadSource::L2, + )?; + + let builder_args = self.builder; + let builder_auth_jwt = if let Some(secret) = builder_args.builder_jwt_token { + secret + } else if let Some(path) = builder_args.builder_jwt_path.as_ref() { + JwtSecret::from_file(path)? + } else { + bail!("Missing Builder JWT secret"); + }; + + let builder_client = RpcClient::new( + builder_args.builder_url.clone(), + builder_auth_jwt, + builder_args.builder_timeout, + PayloadSource::Builder, + )?; + + let boost_sync_enabled = !self.no_boost_sync; + if boost_sync_enabled { + info!("Boost sync enabled"); + } + + let rollup_boost = RollupBoostServer::new( + l2_client, + builder_client, + boost_sync_enabled, + self.execution_mode, + ); + + // Spawn the debug server + rollup_boost.start_debug_server(debug_addr.as_str()).await?; + + let module: RpcModule<()> = rollup_boost.try_into()?; + + // Build and start the server + info!("Starting server on :{}", self.rpc_port); + + let http_middleware = tower::ServiceBuilder::new().layer(ProxyLayer::new( + l2_client_args.l2_url, + l2_auth_jwt, + builder_args.builder_url, + builder_auth_jwt, + )); + + let server = Server::builder() + .set_http_middleware(http_middleware) + .build(format!("{}:{}", self.rpc_host, self.rpc_port).parse::()?) + .await?; + let handle = server.start(module); + + let stop_handle = handle.clone(); + + // Capture SIGINT and SIGTERM + let mut sigint = unix_signal(SignalKind::interrupt())?; + let mut sigterm = unix_signal(SignalKind::terminate())?; + + tokio::select! { + _ = handle.stopped() => { + // The server has already shut down by itself + info!("Server stopped"); + } + _ = sigint.recv() => { + info!("Received SIGINT, shutting down gracefully..."); + let _ = stop_handle.stop(); + } + _ = sigterm.recv() => { + info!("Received SIGTERM, shutting down gracefully..."); + let _ = stop_handle.stop(); + } + } + + Ok(()) + } +} + #[derive(Clone, Debug)] pub enum LogFormat { Json, From 776f09bbbfa25be354c157dcb54bfe8ab24fe2d5 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Thu, 3 Apr 2025 17:10:54 -0700 Subject: [PATCH 05/20] move tests to proper dir --- Cargo.toml | 13 +- src/cli.rs | 6 +- src/lib.rs | 16 +- tests/integration/containers/mod.rs | 2 + tests/integration/containers/op_reth.rs | 231 +++++ tests/integration/containers/rollup_boost.rs | 67 ++ tests/integration/integration_test.rs | 284 ++++++ tests/integration/mod.rs | 515 +++++++++++ tests/integration/proxy.rs | 141 +++ tests/integration/testdata/genesis.json | 896 +++++++++++++++++++ tests/integration/testdata/jwt_secret.hex | 1 + tests/integration/testdata/p2p_secret.hex | 1 + tests/no_tx_pool.rs | 28 + 13 files changed, 2180 insertions(+), 21 deletions(-) create mode 100644 tests/integration/containers/mod.rs create mode 100644 tests/integration/containers/op_reth.rs create mode 100644 tests/integration/containers/rollup_boost.rs create mode 100644 tests/integration/integration_test.rs create mode 100644 tests/integration/mod.rs create mode 100644 tests/integration/proxy.rs create mode 100644 tests/integration/testdata/genesis.json create mode 100644 tests/integration/testdata/jwt_secret.hex create mode 100644 tests/integration/testdata/p2p_secret.hex create mode 100644 tests/no_tx_pool.rs diff --git a/Cargo.toml b/Cargo.toml index d1e19840..f30a8496 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ edition = "2024" [dependencies] op-alloy-rpc-types-engine = "0.12.0" alloy-rpc-types-engine = "0.13.0" -alloy-eips = { version = "0.13.0", features = ["serde"], optional = true } +alloy-eips = { version = "0.13.0", features = ["serde"] } alloy-primitives = { version = "0.8.10", features = ["rand"] } tokio = { version = "1", features = ["full"] } tracing = "0.1.4" @@ -45,14 +45,10 @@ paste = "1.0.15" # dev dependencies for integration tests parking_lot = "0.12.3" -time = { version = "0.3.36", features = [ - "macros", - "formatting", - "parsing", -], optional = true } -lazy_static = { version = "1.5.0", optional = true } +lazy_static = { version = "1.5.0" } [dev-dependencies] +time = { version = "0.3.36", features = ["macros", "formatting", "parsing"] } op-alloy-consensus = "0.12.0" alloy-rpc-types-eth = "0.13.0" chrono = { version = "0.4.40", features = ["serde"] } @@ -68,9 +64,6 @@ bytes = "1.2" reth-rpc-layer = { git = "https://github.com/paradigmxyz/reth.git", rev = "v1.3.7" } ctor = "0.4.1" -[features] -integration = ["dep:lazy_static", "dep:time", "dep:alloy-eips"] - [[bin]] name = "rollup-boost" path = "src/bin/main.rs" diff --git a/src/cli.rs b/src/cli.rs index a9a0b132..52001ccc 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -14,7 +14,7 @@ use crate::{ server::ExecutionMode, }; -#[derive(Parser, Debug)] +#[derive(Clone, Parser, Debug)] #[clap(author, version, about)] pub struct Args { #[command(subcommand)] @@ -222,7 +222,7 @@ impl std::str::FromStr for LogFormat { } } -#[derive(Subcommand, Debug)] +#[derive(Clone, Subcommand, Debug)] pub enum Commands { /// Debug commands Debug { @@ -231,7 +231,7 @@ pub enum Commands { }, } -#[derive(Subcommand, Debug)] +#[derive(Clone, Subcommand, Debug)] pub enum DebugCommands { /// Set the execution mode SetExecutionMode { execution_mode: ExecutionMode }, diff --git a/src/lib.rs b/src/lib.rs index 7cf19d5c..daf780ff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,25 +1,25 @@ -#![cfg_attr( - not(any(test, feature = "integration")), - warn(unused_crate_dependencies) -)] +// #![cfg_attr( +// not(any(test, feature = "integration")), +// warn(unused_crate_dependencies) +// )] use dotenv as _; use rustls as _; -mod client; +pub mod client; pub use client::{auth::*, http::*, rpc::*}; mod cli; pub use cli::*; -mod debug_api; +pub mod debug_api; pub use debug_api::*; mod health; pub use health::{HealthLayer, HealthService}; -#[cfg(all(feature = "integration", test))] -mod integration; +// #[cfg(all(feature = "integration", test))] +// mod integration; mod metrics; pub use metrics::*; diff --git a/tests/integration/containers/mod.rs b/tests/integration/containers/mod.rs new file mode 100644 index 00000000..41ef9049 --- /dev/null +++ b/tests/integration/containers/mod.rs @@ -0,0 +1,2 @@ +pub mod op_reth; +pub mod rollup_boost; diff --git a/tests/integration/containers/op_reth.rs b/tests/integration/containers/op_reth.rs new file mode 100644 index 00000000..69943b48 --- /dev/null +++ b/tests/integration/containers/op_reth.rs @@ -0,0 +1,231 @@ +use std::{ + borrow::Cow, + collections::HashMap, + fs::File, + io::BufReader, + path::PathBuf, + time::{SystemTime, UNIX_EPOCH}, +}; + +use http::Uri; +use serde_json::Value; +use testcontainers::{ + ContainerAsync, CopyToContainer, Image, + core::{ContainerPort, WaitFor}, +}; + +use crate::integration::L2_P2P_ENODE; + +const NAME: &str = "ghcr.io/paradigmxyz/op-reth"; +const TAG: &str = "v1.3.4"; + +const AUTH_RPC_PORT: u16 = 8551; +const P2P_PORT: u16 = 30303; + +#[derive(Debug, Clone)] +pub struct OpRethConfig { + chain: PathBuf, + jwt_secret: PathBuf, + p2p_secret: Option, + pub trusted_peers: Vec, + pub datadir: String, + pub disable_discovery: bool, + pub color: String, + pub ipcdisable: bool, + pub env_vars: HashMap, +} + +impl Default for OpRethConfig { + fn default() -> Self { + Self { + chain: PathBuf::from(format!( + "{}/src/integration/testdata/genesis.json", + env!("CARGO_MANIFEST_DIR") + )), + jwt_secret: PathBuf::from(format!( + "{}/src/integration/testdata/jwt_secret.hex", + env!("CARGO_MANIFEST_DIR") + )), + p2p_secret: None, + trusted_peers: vec![], + datadir: "data".to_string(), + disable_discovery: true, + color: "never".to_string(), + ipcdisable: true, + env_vars: Default::default(), + } + } +} + +impl OpRethConfig { + pub fn set_trusted_peers(mut self, trusted_peers: Vec) -> Self { + self.trusted_peers = trusted_peers; + self + } + + pub fn set_jwt_secret(mut self, jwt_secret: PathBuf) -> Self { + self.jwt_secret = jwt_secret; + self + } + + pub fn set_p2p_secret(mut self, p2p_secret: Option) -> Self { + self.p2p_secret = p2p_secret; + self + } + + pub fn build(self) -> OpRethImage { + // Write the genesis file to the test directory and update the timestamp to the current time + let genesis = { + let file = File::open(&self.chain).unwrap(); + let reader = BufReader::new(file); + let mut genesis: Value = serde_json::from_reader(reader).unwrap(); + + // Update the timestamp field + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs(); + + if let Some(config) = genesis.as_object_mut() { + // Assuming timestamp is at the root level - adjust path as needed + config["timestamp"] = Value::String(format!("0x{:x}", timestamp)); + } + + serde_json::to_string_pretty(&genesis).unwrap() + }; + + let mut copy_to_sources = vec![ + CopyToContainer::new( + // std::fs::read(&self.jwt_secret).unwrap(), + "688f5d737bad920bdfb2fc2f488d6b6209eebda1dae949a8de91398d932c517a" + .to_string() + .into_bytes(), + "/jwt_secret.hex".to_string(), + ), + CopyToContainer::new(genesis.into_bytes(), "/genesis.json".to_string()), + ]; + + if let Some(p2p_secret) = &self.p2p_secret { + copy_to_sources.push(CopyToContainer::new( + // std::fs::read(p2p_secret).unwrap(), + "a11ac89899cd86e36b6fb881ec1255b8a92a688790b7d950f8b7d8dd626671fb" + .to_string() + .into_bytes(), + "/p2p_secret.hex".to_string(), + )); + } + + let expose_ports = vec![ContainerPort::Tcp(8551), ContainerPort::Tcp(30303)]; + + OpRethImage { + config: self, + copy_to_sources, + expose_ports, + } + } +} + +impl OpRethImage { + pub fn config(&self) -> &OpRethConfig { + &self.config + } +} + +#[derive(Debug, Clone)] +pub struct OpRethImage { + config: OpRethConfig, + copy_to_sources: Vec, + expose_ports: Vec, +} + +impl Image for OpRethImage { + fn name(&self) -> &str { + NAME + } + + fn tag(&self) -> &str { + TAG + } + + fn ready_conditions(&self) -> Vec { + vec![WaitFor::message_on_stdout("Starting consensus")] + } + + fn env_vars( + &self, + ) -> impl IntoIterator>, impl Into>)> { + &self.config.env_vars + } + + fn copy_to_sources(&self) -> impl IntoIterator { + self.copy_to_sources.iter() + } + + fn cmd(&self) -> impl IntoIterator>> { + let mut cmd = vec![ + "node".to_string(), + "--http".to_string(), + "--http.addr=0.0.0.0".to_string(), + "--authrpc.port=8551".to_string(), + "--authrpc.addr=0.0.0.0".to_string(), + "--authrpc.jwtsecret=/jwt_secret.hex".to_string(), + "--chain=/genesis.json".to_string(), + "--datadir".to_string(), + self.config.datadir.clone(), + "--port=30303".to_string(), + "--color".to_string(), + self.config.color.clone(), + ]; + if self.config.p2p_secret.is_some() { + cmd.push("--p2p-secret-key=/p2p_secret.hex".to_string()); + } + if !self.config.trusted_peers.is_empty() { + cmd.extend([ + "--trusted-peers".to_string(), + self.config.trusted_peers.join(","), + ]); + } + if self.config.disable_discovery { + cmd.push("--disable-discovery".to_string()); + } + if self.config.ipcdisable { + cmd.push("--ipcdisable".to_string()); + } + cmd + } + + fn expose_ports(&self) -> &[ContainerPort] { + &self.expose_ports + } +} + +pub trait OpRethMehods { + async fn auth_rpc(&self) -> eyre::Result; + async fn auth_rpc_port(&self) -> eyre::Result; + async fn enode(&self) -> eyre::Result; +} + +impl OpRethMehods for ContainerAsync { + async fn auth_rpc_port(&self) -> eyre::Result { + Ok(self.get_host_port_ipv4(AUTH_RPC_PORT).await?) + } + + async fn auth_rpc(&self) -> eyre::Result { + Ok(format!( + "http://{}:{}", + self.get_host().await?, + self.get_host_port_ipv4(AUTH_RPC_PORT).await? + ) + .parse()?) + } + + async fn enode(&self) -> eyre::Result { + Ok(format!( + "enode://{}@{}:{}", + L2_P2P_ENODE, + self.get_host().await?, + self.get_host_port_ipv4(P2P_PORT).await? + ) + .parse()?) + } +} diff --git a/tests/integration/containers/rollup_boost.rs b/tests/integration/containers/rollup_boost.rs new file mode 100644 index 00000000..5b3a2daf --- /dev/null +++ b/tests/integration/containers/rollup_boost.rs @@ -0,0 +1,67 @@ +use clap::Parser; +use rollup_boost::Args; +use tokio::task::JoinHandle; + +#[derive(Debug)] +pub struct RollupBoost { + args: Args, + pub handle: JoinHandle>, +} + +impl RollupBoost { + pub fn args(&self) -> &Args { + &self.args + } + + pub fn rpc_endpoint(&self) -> String { + format!("http://localhost:{}", self.args.rpc_port) + } + + pub fn debug_endpoint(&self) -> String { + format!("http://localhost:{}", self.args.debug_server_port) + } +} + +#[derive(Clone, Debug)] +pub struct RollupBoostConfig { + pub args: Args, +} + +impl Default for RollupBoostConfig { + fn default() -> Self { + Self { + args: Args::parse_from([ + "rollup-boost", + &format!( + "--l2-jwt-path={}/src/integration/testdata/jwt_secret.hex", + env!("CARGO_MANIFEST_DIR") + ), + &format!( + "--builder-jwt-path={}/src/integration/testdata/jwt_secret.hex", + env!("CARGO_MANIFEST_DIR") + ), + "--log-level=trace", + "--tracing", + "--metrics", + ]), + } + } +} + +impl RollupBoostConfig { + pub fn start(self) -> RollupBoost { + let args = self.args.clone(); + let handle = tokio::spawn(async move { + let res = args.clone().run().await; + if let Err(e) = &res { + eprintln!("Error: {:?}", e); + } + res + }); + + RollupBoost { + args: self.args, + handle, + } + } +} diff --git a/tests/integration/integration_test.rs b/tests/integration/integration_test.rs new file mode 100644 index 00000000..1e2aa03b --- /dev/null +++ b/tests/integration/integration_test.rs @@ -0,0 +1,284 @@ +#[cfg(test)] +mod tests { + use alloy_primitives::B256; + use rollup_boost::ExecutionMode; + use serde_json::Value; + use std::sync::{Arc, Mutex}; + use std::time::Duration; + + use crate::integration::RollupBoostTestHarnessBuilder; + use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; + + #[tokio::test] + async fn test_integration_simple() -> eyre::Result<()> { + let harness = RollupBoostTestHarnessBuilder::new("test_integration_simple") + .build() + .await?; + let mut block_generator = harness.get_block_generator().await?; + + for _ in 0..5 { + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!( + block_creator.is_builder(), + "Block creator should be the builder" + ); + } + + Ok(()) + } + + #[tokio::test] + async fn test_integration_no_tx_pool() -> eyre::Result<()> { + let harness = RollupBoostTestHarnessBuilder::new("test_integration_no_tx_pool") + .build() + .await?; + let mut block_generator = harness.get_block_generator().await?; + + // start creating 5 empty blocks which are processed by the L2 builder + for _ in 0..5 { + let (_block, block_creator) = block_generator.generate_block(true).await?; + assert!(block_creator.is_l2(), "Block creator should be l2"); + } + + // process 5 more non empty blocks which are processed by the builder. + // The builder should be on sync because it has received the new payload requests from rollup-boost. + for _ in 0..5 { + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!( + block_creator.is_builder(), + "Block creator should be the builder" + ); + } + + Ok(()) + } + + #[tokio::test] + async fn test_integration_execution_mode() -> eyre::Result<()> { + // Create a counter that increases whenever we receive a new RPC call in the builder + let counter = Arc::new(Mutex::new(0)); + + let counter_for_handler = counter.clone(); + let handler = Box::new(move |_method: &str, _params: Value, _result: Value| { + let mut counter = counter_for_handler.lock().unwrap(); + + *counter += 1; + None + }); + + let harness = RollupBoostTestHarnessBuilder::new("test_integration_dry_run") + .proxy_handler(handler) + .build() + .await?; + let mut block_generator = harness.get_block_generator().await?; + + // start creating 5 empty blocks which are processed by the builder + for _ in 0..5 { + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!( + block_creator.is_builder(), + "Block creator should be the builder" + ); + } + + let client = harness.get_client().await; + + // enable dry run mode + { + let response = client + .set_execution_mode(ExecutionMode::DryRun) + .await + .unwrap(); + assert_eq!(response.execution_mode, ExecutionMode::DryRun); + + // the new valid block should be created the the l2 builder + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!(block_creator.is_l2(), "Block creator should be l2"); + } + + // toggle again dry run mode + { + let response = client + .set_execution_mode(ExecutionMode::Enabled) + .await + .unwrap(); + assert_eq!(response.execution_mode, ExecutionMode::Enabled); + + // the new valid block should be created the the builder + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!( + block_creator.is_builder(), + "Block creator should be the builder" + ); + } + + // sleep for 1 second so that it has time to send the last FCU request to the builder + // and there is not a race condition with the disable call + std::thread::sleep(Duration::from_secs(1)); + + tracing::info!("Setting execution mode to disabled"); + + // Set the execution mode to disabled and reset the counter in the proxy to 0 + // to track the number of calls to the builder during the disabled mode which + // should be 0 + { + let response = client + .set_execution_mode(ExecutionMode::Disabled) + .await + .unwrap(); + assert_eq!(response.execution_mode, ExecutionMode::Disabled); + + // reset the counter in the proxy + *counter.lock().unwrap() = 0; + + // create 5 blocks which are processed by the l2 clients + for _ in 0..5 { + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!(block_creator.is_l2(), "Block creator should be l2"); + } + + assert_eq!( + *counter.lock().unwrap(), + 0, + "Number of calls to the builder should be 0", + ); + } + + Ok(()) + } + + #[tokio::test] + async fn test_integration_remote_builder_down() -> eyre::Result<()> { + let harness = RollupBoostTestHarnessBuilder::new("test_integration_remote_builder_down") + .build() + .await?; + let mut block_generator = harness.get_block_generator().await?; + + for _ in 0..3 { + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!( + block_creator.is_builder(), + "Block creator should be the builder" + ); + } + + // stop the builder + harness.builder.stop().await?; + + // create 3 new blocks that are processed by the l2 builder + for _ in 0..3 { + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!(block_creator.is_l2(), "Block creator should be l2"); + } + + // start the builder again + harness.builder.start().await?; + + // the next block is computed by the l2 builder because the builder is not synced with the previous 3 blocks + // But, once the builder receives the FCU request from rollup-boost, it will sync up the blocks with the + // L2 block builder and be ready again. + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!(block_creator.is_l2(), "Block creator should be l2"); + + // Note: We might add some sleep here if the builder is not synced in time. I have not seen this happen yet. + + // create 3 new blocks that are processed by the l2 builder because the builder is not synced with the previous 3 blocks + for _ in 0..3 { + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!(block_creator.is_builder(), "Block creator should be l2"); + } + + Ok(()) + } + + #[tokio::test] + async fn test_integration_builder_full_delay() -> eyre::Result<()> { + // Create a dynamic handler that delays all the calls by 2 seconds + let delay = Arc::new(Mutex::new(Duration::from_secs(0))); + + let delay_for_handler = delay.clone(); + let handler = Box::new(move |_method: &str, _params: Value, _result: Value| { + let delay = delay_for_handler.lock().unwrap(); + // sleep the amount of time specified in the delay + std::thread::sleep(*delay); + None + }); + + // This integration test checks that if the builder has a general delay in processing ANY of the requests, + // rollup-boost does not stop building blocks. + let harness = RollupBoostTestHarnessBuilder::new("test_integration_builder_full_delay") + .proxy_handler(handler) + .build() + .await?; + + let mut block_generator = harness.get_block_generator().await?; + + // create 3 blocks that are processed by the builder + for _ in 0..3 { + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!( + block_creator.is_builder(), + "Block creator should be the builder" + ); + } + + // add the delay + *delay.lock().unwrap() = Duration::from_secs(2); + + // create 3 blocks that are processed by the builder + for _ in 0..3 { + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!(block_creator.is_l2(), "Block creator should be the builder"); + } + + Ok(()) + } + + #[tokio::test] + async fn test_integration_builder_returns_incorrect_block() -> eyre::Result<()> { + // Test that the builder returns a block with an incorrect state root and that rollup-boost + // does not process it. + let handler = Box::new(move |method: &str, _params: Value, _result: Value| { + if method != "engine_getPayloadV3" { + return None; + } + + let mut payload = + serde_json::from_value::(_result).unwrap(); + + // modify the state root field + payload + .execution_payload + .payload_inner + .payload_inner + .state_root = B256::ZERO; + + let result = serde_json::to_value(&payload).unwrap(); + Some(result) + }); + + let harness = + RollupBoostTestHarnessBuilder::new("test_integration_builder_returns_incorrect_block") + .proxy_handler(handler) + .build() + .await?; + + let mut block_generator = harness.get_block_generator().await?; + + // create 3 blocks that are processed by the builder + for _ in 0..3 { + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!(block_creator.is_l2(), "Block creator should be the builder"); + } + // check that at some point we had the log "builder payload was not valid" which signals + // that the builder returned a payload that was not valid and rollup-boost did not process it. + // read lines + let logs = std::fs::read_to_string(harness.rollup_boost.args().log_file.clone().unwrap())?; + assert!( + logs.contains("Invalid payload"), + "Logs should contain the message 'builder payload was not valid'" + ); + + Ok(()) + } +} diff --git a/tests/integration/mod.rs b/tests/integration/mod.rs new file mode 100644 index 00000000..c7a8cc9d --- /dev/null +++ b/tests/integration/mod.rs @@ -0,0 +1,515 @@ +use alloy_eips::Encodable2718; +use alloy_primitives::{B256, Bytes, TxKind, U256, address, hex}; +use alloy_rpc_types_engine::{ExecutionPayload, JwtSecret}; +use alloy_rpc_types_engine::{ + ForkchoiceState, ForkchoiceUpdated, PayloadAttributes, PayloadId, PayloadStatus, + PayloadStatusEnum, +}; +use alloy_rpc_types_eth::BlockNumberOrTag; +use bytes::BytesMut; +use containers::op_reth::{OpRethConfig, OpRethImage, OpRethMehods}; +use containers::rollup_boost::{RollupBoost, RollupBoostConfig}; +use eyre::bail; +use futures::FutureExt; +use futures::future::BoxFuture; +use http::Uri; +use jsonrpsee::http_client::{HttpClient, transport::HttpBackend}; +use jsonrpsee::proc_macros::rpc; +use op_alloy_consensus::TxDeposit; +use op_alloy_rpc_types_engine::{OpExecutionPayloadEnvelopeV3, OpPayloadAttributes}; +use proxy::{DynHandlerFn, start_proxy_server}; +use rollup_boost::client::auth::{AuthClientLayer, AuthClientService}; +use rollup_boost::debug_api::DebugClient; +use rollup_boost::{EngineApiClient, OpExecutionPayloadEnvelope, Version}; +use rollup_boost::{NewPayload, PayloadSource}; +use std::net::TcpListener; +use std::path::PathBuf; +use std::str::FromStr; +use std::time::{Duration, SystemTime}; +use testcontainers::core::logs::LogFrame; +use testcontainers::core::logs::consumer::LogConsumer; +use testcontainers::runners::AsyncRunner; +use testcontainers::{ContainerAsync, Image, ImageExt}; +use time::{OffsetDateTime, format_description}; +use tokio::io::{AsyncBufReadExt, AsyncWriteExt as _}; +use tracing::info; + +// use time::{OffsetDateTime, format_description}; + +/// Default JWT token for testing purposes +pub const JWT_SECRET: &str = "688f5d737bad920bdfb2fc2f488d6b6209eebda1dae949a8de91398d932c517a"; + +pub const L2_P2P_ENODE: &str = "3479db4d9217fb5d7a8ed4d61ac36e120b05d36c2eefb795dc42ff2e971f251a2315f5649ea1833271e020b9adc98d5db9973c7ed92d6b2f1f2223088c3d852f"; + +mod containers; +mod integration_test; +mod proxy; + +pub struct LoggingConsumer { + target: String, + log_file: tokio::sync::Mutex, +} + +impl LogConsumer for LoggingConsumer { + fn accept<'a>(&'a self, record: &'a LogFrame) -> BoxFuture<'a, ()> { + async move { + match record { + testcontainers::core::logs::LogFrame::StdOut(bytes) => { + info!(target = self.target, "{}", String::from_utf8_lossy(bytes)); + self.log_file.lock().await.write_all(bytes).await.unwrap(); + } + testcontainers::core::logs::LogFrame::StdErr(bytes) => { + info!(target = self.target, "{}", String::from_utf8_lossy(bytes)); + self.log_file.lock().await.write_all(bytes).await.unwrap(); + } + } + } + .boxed() + } +} + +pub async fn wait_for_log( + container: ContainerAsync, + pattern: &str, + timeout: Duration, +) -> eyre::Result<()> { + let timeout = tokio::time::sleep(timeout); + let mut stderr = container.stderr(true).lines(); + let mut stdout = container.stdout(true).lines(); + + tokio::select! { + result = async { + loop { + tokio::select! { + line = stderr.next_line() => { + if let Ok(Some(line)) = line { + if line.contains(pattern) { + return Ok::<_, eyre::Report>(()); + } + } + } + line = stdout.next_line() => { + if let Ok(Some(line)) = line { + if line.contains(pattern) { + return Ok::<_, eyre::Report>(()); + } + } + } + } + } + } => result, + _ = timeout => { + bail!("Timeout waiting for log message: {}", pattern); + } + } +} + +pub struct EngineApi { + pub engine_api_client: HttpClient>, +} + +// TODO: Use client/rpc.rs instead +impl EngineApi { + pub fn new(url: &str, secret: &str) -> eyre::Result { + let secret_layer = AuthClientLayer::new(JwtSecret::from_str(secret)?); + let middleware = tower::ServiceBuilder::default().layer(secret_layer); + let client = jsonrpsee::http_client::HttpClientBuilder::default() + .set_http_middleware(middleware) + .build(url) + .expect("Failed to create http client"); + + Ok(Self { + engine_api_client: client, + }) + } + + pub async fn get_payload( + &self, + version: Version, + payload_id: PayloadId, + ) -> eyre::Result { + match version { + Version::V3 => Ok(OpExecutionPayloadEnvelope::V3( + EngineApiClient::get_payload_v3(&self.engine_api_client, payload_id).await?, + )), + Version::V4 => Ok(OpExecutionPayloadEnvelope::V4( + EngineApiClient::get_payload_v4(&self.engine_api_client, payload_id).await?, + )), + } + } + + pub async fn new_payload(&self, payload: NewPayload) -> eyre::Result { + match payload { + NewPayload::V3(new_payload) => Ok(EngineApiClient::new_payload_v3( + &self.engine_api_client, + new_payload.payload, + new_payload.versioned_hashes, + new_payload.parent_beacon_block_root, + ) + .await?), + NewPayload::V4(new_payload) => Ok(EngineApiClient::new_payload_v4( + &self.engine_api_client, + new_payload.payload, + new_payload.versioned_hashes, + new_payload.parent_beacon_block_root, + new_payload.execution_requests, + ) + .await?), + } + } + + pub async fn update_forkchoice( + &self, + current_head: B256, + new_head: B256, + payload_attributes: Option, + ) -> eyre::Result { + Ok(EngineApiClient::fork_choice_updated_v3( + &self.engine_api_client, + ForkchoiceState { + head_block_hash: new_head, + safe_block_hash: current_head, + finalized_block_hash: current_head, + }, + payload_attributes, + ) + .await?) + } + + pub async fn latest(&self) -> eyre::Result> { + Ok(BlockApiClient::get_block_by_number( + &self.engine_api_client, + BlockNumberOrTag::Latest, + false, + ) + .await?) + } +} + +#[rpc(client, namespace = "eth")] +pub trait BlockApi { + #[method(name = "getBlockByNumber")] + async fn get_block_by_number( + &self, + block_number: BlockNumberOrTag, + include_txs: bool, + ) -> RpcResult>; +} + +/// Test flavor that sets up one Rollup-boost instance connected to two Reth nodes +pub struct RollupBoostTestHarness { + pub l2: ContainerAsync, + pub builder: ContainerAsync, + pub rollup_boost: RollupBoost, +} + +pub struct RollupBoostTestHarnessBuilder { + test_name: String, + proxy_handler: Option, +} + +impl RollupBoostTestHarnessBuilder { + pub fn new(test_name: &str) -> Self { + Self { + test_name: test_name.to_string(), + proxy_handler: None, + } + } + + pub fn file_path(&self, service_name: &str) -> eyre::Result { + let dt: OffsetDateTime = SystemTime::now().into(); + let format = format_description::parse("[year]_[month]_[day]_[hour]_[minute]_[second]")?; + let timestamp = dt.format(&format)?; + + // let test_name = format!("{}_{}", timestamp, test_name); + + let dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("test_logs") + .join(timestamp) + .join(self.test_name.clone()); + std::fs::create_dir_all(&dir)?; + + let file_name = format!("{service_name}.log"); + Ok(dir.join(file_name)) + } + + pub async fn async_log_file(&self, service_name: &str) -> eyre::Result { + let file_path = self.file_path(service_name)?; + Ok(tokio::fs::OpenOptions::new() + .append(true) + .create(true) + .open(file_path) + .await?) + } + + pub fn log_file(&self, service_name: &str) -> eyre::Result { + let file_path = self.file_path(service_name)?; + Ok(std::fs::OpenOptions::new() + .append(true) + .create(true) + .open(file_path)?) + } + + pub async fn log_consumer(&self, service_name: &str) -> eyre::Result { + let file = self.async_log_file(service_name).await?; + Ok(LoggingConsumer { + target: service_name.to_string(), + log_file: tokio::sync::Mutex::new(file), + }) + } + + pub fn proxy_handler(mut self, proxy_handler: DynHandlerFn) -> Self { + self.proxy_handler = Some(proxy_handler); + self + } + + pub async fn build(self) -> eyre::Result { + let l2_log_consumer = self.log_consumer("l2").await?; + let builder_log_consumer = self.log_consumer("builder").await?; + let rollup_boost_log_file_path = self.file_path("rollup_boost")?; + + let l2 = OpRethConfig::default() + .set_p2p_secret(Some(PathBuf::from(format!( + "{}/src/integration/testdata/p2p_secret.hex", + env!("CARGO_MANIFEST_DIR") + )))) + .build() + .with_log_consumer(l2_log_consumer) + .start() + .await?; + + let builder = OpRethConfig::default() + .set_trusted_peers(vec![l2.enode().await?.to_string()]) + .build() + .with_log_consumer(builder_log_consumer) + .start() + .await?; + + println!("l2 authrpc: {}", l2.auth_rpc().await?); + println!("builder authrpc: {}", builder.auth_rpc().await?); + + // run a proxy in between the builder and the rollup-boost if the proxy_handler is set + let mut builder_authrpc_port = builder.auth_rpc_port().await?; + if let Some(proxy_handler) = self.proxy_handler { + let proxy_port = get_available_port().expect("no available port"); + let _ = start_proxy_server(proxy_handler, proxy_port, builder_authrpc_port).await; + builder_authrpc_port = proxy_port + }; + let builder_url = format!("http://127.0.0.1:{}", builder_authrpc_port); + + // Start Rollup-boost instance + let mut rollup_boost = RollupBoostConfig::default(); + rollup_boost.args.l2_client.l2_url = l2.auth_rpc().await?.try_into().unwrap(); + rollup_boost.args.builder.builder_url = builder_url.try_into().unwrap(); + rollup_boost.args.log_file = Some(rollup_boost_log_file_path); + let rollup_boost = rollup_boost.start(); + + Ok(RollupBoostTestHarness { + l2, + builder, + rollup_boost, + }) + } +} + +impl RollupBoostTestHarness { + pub async fn get_block_generator(&self) -> eyre::Result { + let validator = + BlockBuilderCreatorValidator::new(self.rollup_boost.args().log_file.clone().unwrap()); + + let engine_api = EngineApi::new(&self.rollup_boost.rpc_endpoint(), JWT_SECRET)?; + + let mut block_creator = SimpleBlockGenerator::new(validator, engine_api); + block_creator.init().await?; + Ok(block_creator) + } + + pub async fn get_client(&self) -> DebugClient { + DebugClient::new(&self.rollup_boost.debug_endpoint()).unwrap() + } +} + +/// A simple system that continuously generates empty blocks using the engine API +pub struct SimpleBlockGenerator { + validator: BlockBuilderCreatorValidator, + engine_api: EngineApi, + latest_hash: B256, + timestamp: u64, + version: Version, +} + +impl SimpleBlockGenerator { + pub fn new(validator: BlockBuilderCreatorValidator, engine_api: EngineApi) -> Self { + Self { + validator, + engine_api, + latest_hash: B256::ZERO, // temporary value + timestamp: 0, // temporary value + version: Version::V3, + } + } + + /// Initialize the block generator by fetching the latest block + pub async fn init(&mut self) -> eyre::Result<()> { + let latest_block = self.engine_api.latest().await?.expect("block not found"); + self.latest_hash = latest_block.header.hash; + self.timestamp = latest_block.header.timestamp; + Ok(()) + } + + /// Generate a single new block and return its hash + pub async fn generate_block( + &mut self, + empty_blocks: bool, + ) -> eyre::Result<(B256, PayloadSource)> { + let txns = match self.version { + Version::V4 => { + let tx = create_deposit_tx(); + Some(vec![tx]) + } + _ => None, + }; + + // Submit forkchoice update with payload attributes for the next block + let result = self + .engine_api + .update_forkchoice( + self.latest_hash, + self.latest_hash, + Some(OpPayloadAttributes { + payload_attributes: PayloadAttributes { + withdrawals: Some(vec![]), + parent_beacon_block_root: Some(B256::ZERO), + timestamp: self.timestamp + 1000, // 1 second later + prev_randao: B256::ZERO, + suggested_fee_recipient: Default::default(), + }, + transactions: txns, + no_tx_pool: Some(empty_blocks), + gas_limit: Some(10000000000), + eip_1559_params: None, + }), + ) + .await?; + + let payload_id = result.payload_id.expect("missing payload id"); + + if !empty_blocks { + tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + } + + let payload = self + .engine_api + .get_payload(self.version, payload_id) + .await?; + + // Submit the new payload to the node + let validation_status = self + .engine_api + .new_payload(NewPayload::from(payload.clone())) + .await?; + + if validation_status.status != PayloadStatusEnum::Valid { + return Err(eyre::eyre!("Invalid payload status")); + } + + let execution_payload = ExecutionPayload::from(payload); + let new_block_hash = execution_payload.block_hash(); + + // Update the chain's head + self.engine_api + .update_forkchoice(self.latest_hash, new_block_hash, None) + .await?; + + // Update internal state + self.latest_hash = new_block_hash; + self.timestamp = execution_payload.timestamp(); + + // Check who built the block in the rollup-boost logs + let block_creator = self + .validator + .get_block_creator(new_block_hash) + .await? + .expect("block creator not found"); + + Ok((new_block_hash, block_creator)) + } +} + +pub struct BlockBuilderCreatorValidator { + file: PathBuf, +} + +impl<'a> BlockBuilderCreatorValidator { + pub fn new(file: PathBuf) -> Self { + Self { file } + } +} + +impl<'a> BlockBuilderCreatorValidator { + pub async fn get_block_creator(&self, block_hash: B256) -> eyre::Result> { + let contents = std::fs::read_to_string(&self.file)?; + + let search_query = format!("returning block hash={:#x}", block_hash); + + // Find the log line containing the block hash + for line in contents.lines() { + if line.contains(&search_query) { + // Extract the context=X part + if let Some(context_start) = line.find("context=") { + let context = line[context_start..] + .split_whitespace() + .next() + .ok_or(eyre::eyre!("no context found"))? + .split('=') + .nth(1) + .ok_or(eyre::eyre!("no context found"))?; + + match context { + "builder" => return Ok(Some(PayloadSource::Builder)), + "l2" => return Ok(Some(PayloadSource::L2)), + _ => panic!("Unknown context: {}", context), + } + } else { + panic!("no context found"); + } + } + } + + Ok(None) + } +} + +fn create_deposit_tx() -> Bytes { + const ISTHMUS_DATA: &[u8] = &hex!( + "098999be00000558000c5fc500000000000000030000000067a9f765000000000000002900000000000000000000000000000000000000000000000000000000006a6d09000000000000000000000000000000000000000000000000000000000000000172fcc8e8886636bdbe96ba0e4baab67ea7e7811633f52b52e8cf7a5123213b6f000000000000000000000000d3f2c5afb2d76f5579f326b0cd7da5f5a4126c3500004e2000000000000001f4" + ); + + let deposit_tx = TxDeposit { + source_hash: B256::default(), + from: address!("DeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001"), + to: TxKind::Call(address!("4200000000000000000000000000000000000015")), + mint: None, + value: U256::default(), + gas_limit: 210000, + is_system_transaction: true, + input: ISTHMUS_DATA.into(), + }; + + let mut buffer_without_header = BytesMut::new(); + deposit_tx.encode_2718(&mut buffer_without_header); + + buffer_without_header.to_vec().into() +} + +fn local_host(port: u16) -> Uri { + format!("http://localhost:{port}").parse::().unwrap() +} + +fn get_available_port() -> Option { + (8000..9000).find(|port| port_is_available(*port)) +} + +fn port_is_available(port: u16) -> bool { + TcpListener::bind(("127.0.0.1", port)).is_ok() +} diff --git a/tests/integration/proxy.rs b/tests/integration/proxy.rs new file mode 100644 index 00000000..cf3cce5f --- /dev/null +++ b/tests/integration/proxy.rs @@ -0,0 +1,141 @@ +use bytes::Bytes; +use http::header; +use http_body_util::{BodyExt, Full, combinators::BoxBody}; +use hyper::client::conn::http1::Builder; +use hyper::server::conn::http1; +use hyper::service::service_fn; +use hyper::{Request, Response}; +use hyper_util::rt::TokioIo; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use std::net::SocketAddr; +use std::sync::Arc; +use tokio::net::{TcpListener, TcpStream}; + +#[derive(Debug, Serialize, Deserialize, Clone)] +struct JsonRpcRequest { + jsonrpc: String, + method: String, + params: Value, + id: Value, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +struct JsonRpcResponse { + jsonrpc: String, + #[serde(default)] + result: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + error: Option, + id: Value, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct JsonRpcError { + code: i32, + message: String, + data: Option, +} + +pub type DynHandlerFn = Box Option + Send + Sync>; + +// Structure to hold the target address that we'll pass to the proxy function +#[derive(Clone)] +struct ProxyConfig { + target_addr: SocketAddr, + handler: Arc, +} + +async fn proxy( + config: ProxyConfig, + req: Request, +) -> Result>, Box> { + let (parts, body) = req.into_parts(); + let bytes = body.collect().await?.to_bytes(); + + let json_rpc_request = serde_json::from_slice::(&bytes).unwrap(); + let req = Request::from_parts(parts, Full::new(bytes)); + + let stream = TcpStream::connect(config.target_addr).await?; + let io = TokioIo::new(stream); + + let (mut sender, conn) = Builder::new() + .preserve_header_case(true) + .title_case_headers(true) + .handshake(io) + .await?; + + tokio::task::spawn(async move { + if let Err(err) = conn.await { + println!("Connection failed: {:?}", err); + } + }); + + let resp = sender.send_request(req).await?; + + let (parts, body) = resp.into_parts(); + let bytes = body.collect().await?.to_bytes(); + + let json_rpc_response = serde_json::from_slice::(&bytes).unwrap(); + let bytes = if let Some(result) = json_rpc_response.clone().result { + let value = (config.handler)(&json_rpc_request.method, json_rpc_request.params, result); + if let Some(value) = value { + // If the handler returns a value, we replace the result with the new value + // The callback only returns the result of the jsonrpc request so we have to wrap it up + // again in a JsonRpcResponse + let mut new_json_rpc_resp = json_rpc_response; + new_json_rpc_resp.result = Some(value); + Bytes::from(serde_json::to_vec(&new_json_rpc_resp).unwrap()) + } else { + // If the handler returns None, we return the original response + bytes + } + } else { + bytes + }; + + let bytes_len = bytes.len(); + let mut resp = Response::from_parts(parts, Full::new(bytes).map_err(|_| unreachable!())); + + // We have to update the content length to the new bytes length + resp.headers_mut() + .insert(header::CONTENT_LENGTH, bytes_len.into()); + + Ok(resp.map(|b| b.boxed())) +} + +pub async fn start_proxy_server( + handler: DynHandlerFn, + listen_port: u16, + target_port: u16, +) -> Result<(), Box> { + let listen_addr = SocketAddr::from(([127, 0, 0, 1], listen_port)); + let target_addr = SocketAddr::from(([127, 0, 0, 1], target_port)); + + let config = ProxyConfig { + target_addr, + handler: Arc::new(handler), + }; + let listener = TcpListener::bind(listen_addr).await?; + + tokio::spawn(async move { + loop { + let (stream, _) = listener.accept().await.unwrap(); + let io = TokioIo::new(stream); + let config = config.clone(); + + tokio::task::spawn(async move { + if let Err(err) = http1::Builder::new() + .preserve_header_case(true) + .title_case_headers(true) + .serve_connection(io, service_fn(move |req| proxy(config.clone(), req))) + .await + { + println!("Failed to serve connection: {:?}", err); + } + }); + } + }); + + Ok(()) +} diff --git a/tests/integration/testdata/genesis.json b/tests/integration/testdata/genesis.json new file mode 100644 index 00000000..8ee3a460 --- /dev/null +++ b/tests/integration/testdata/genesis.json @@ -0,0 +1,896 @@ +{ + "alloc": { + "0000000000000000000000000000000000000000": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000001": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000002": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000003": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000004": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000005": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000006": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000007": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000008": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000009": { + "balance": "0x1" + }, + "000000000000000000000000000000000000000a": { + "balance": "0x1" + }, + "000000000000000000000000000000000000000b": { + "balance": "0x1" + }, + "000000000000000000000000000000000000000c": { + "balance": "0x1" + }, + "000000000000000000000000000000000000000d": { + "balance": "0x1" + }, + "000000000000000000000000000000000000000e": { + "balance": "0x1" + }, + "000000000000000000000000000000000000000f": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000010": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000011": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000012": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000013": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000014": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000015": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000016": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000017": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000018": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000019": { + "balance": "0x1" + }, + "000000000000000000000000000000000000001a": { + "balance": "0x1" + }, + "000000000000000000000000000000000000001b": { + "balance": "0x1" + }, + "000000000000000000000000000000000000001c": { + "balance": "0x1" + }, + "000000000000000000000000000000000000001d": { + "balance": "0x1" + }, + "000000000000000000000000000000000000001e": { + "balance": "0x1" + }, + "000000000000000000000000000000000000001f": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000020": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000021": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000022": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000023": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000024": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000025": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000026": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000027": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000028": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000029": { + "balance": "0x1" + }, + "000000000000000000000000000000000000002a": { + "balance": "0x1" + }, + "000000000000000000000000000000000000002b": { + "balance": "0x1" + }, + "000000000000000000000000000000000000002c": { + "balance": "0x1" + }, + "000000000000000000000000000000000000002d": { + "balance": "0x1" + }, + "000000000000000000000000000000000000002e": { + "balance": "0x1" + }, + "000000000000000000000000000000000000002f": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000030": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000031": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000032": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000033": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000034": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000035": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000036": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000037": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000038": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000039": { + "balance": "0x1" + }, + "000000000000000000000000000000000000003a": { + "balance": "0x1" + }, + "000000000000000000000000000000000000003b": { + "balance": "0x1" + }, + "000000000000000000000000000000000000003c": { + "balance": "0x1" + }, + "000000000000000000000000000000000000003d": { + "balance": "0x1" + }, + "000000000000000000000000000000000000003e": { + "balance": "0x1" + }, + "000000000000000000000000000000000000003f": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000040": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000041": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000042": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000043": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000044": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000045": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000046": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000047": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000048": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000049": { + "balance": "0x1" + }, + "000000000000000000000000000000000000004a": { + "balance": "0x1" + }, + "000000000000000000000000000000000000004b": { + "balance": "0x1" + }, + "000000000000000000000000000000000000004c": { + "balance": "0x1" + }, + "000000000000000000000000000000000000004d": { + "balance": "0x1" + }, + "000000000000000000000000000000000000004e": { + "balance": "0x1" + }, + "000000000000000000000000000000000000004f": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000050": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000051": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000052": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000053": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000054": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000055": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000056": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000057": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000058": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000059": { + "balance": "0x1" + }, + "000000000000000000000000000000000000005a": { + "balance": "0x1" + }, + "000000000000000000000000000000000000005b": { + "balance": "0x1" + }, + "000000000000000000000000000000000000005c": { + "balance": "0x1" + }, + "000000000000000000000000000000000000005d": { + "balance": "0x1" + }, + "000000000000000000000000000000000000005e": { + "balance": "0x1" + }, + "000000000000000000000000000000000000005f": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000060": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000061": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000062": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000063": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000064": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000065": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000066": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000067": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000068": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000069": { + "balance": "0x1" + }, + "000000000000000000000000000000000000006a": { + "balance": "0x1" + }, + "000000000000000000000000000000000000006b": { + "balance": "0x1" + }, + "000000000000000000000000000000000000006c": { + "balance": "0x1" + }, + "000000000000000000000000000000000000006d": { + "balance": "0x1" + }, + "000000000000000000000000000000000000006e": { + "balance": "0x1" + }, + "000000000000000000000000000000000000006f": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000070": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000071": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000072": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000073": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000074": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000075": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000076": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000077": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000078": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000079": { + "balance": "0x1" + }, + "000000000000000000000000000000000000007a": { + "balance": "0x1" + }, + "000000000000000000000000000000000000007b": { + "balance": "0x1" + }, + "000000000000000000000000000000000000007c": { + "balance": "0x1" + }, + "000000000000000000000000000000000000007d": { + "balance": "0x1" + }, + "000000000000000000000000000000000000007e": { + "balance": "0x1" + }, + "000000000000000000000000000000000000007f": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000080": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000081": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000082": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000083": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000084": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000085": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000086": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000087": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000088": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000089": { + "balance": "0x1" + }, + "000000000000000000000000000000000000008a": { + "balance": "0x1" + }, + "000000000000000000000000000000000000008b": { + "balance": "0x1" + }, + "000000000000000000000000000000000000008c": { + "balance": "0x1" + }, + "000000000000000000000000000000000000008d": { + "balance": "0x1" + }, + "000000000000000000000000000000000000008e": { + "balance": "0x1" + }, + "000000000000000000000000000000000000008f": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000090": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000091": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000092": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000093": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000094": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000095": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000096": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000097": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000098": { + "balance": "0x1" + }, + "0000000000000000000000000000000000000099": { + "balance": "0x1" + }, + "000000000000000000000000000000000000009a": { + "balance": "0x1" + }, + "000000000000000000000000000000000000009b": { + "balance": "0x1" + }, + "000000000000000000000000000000000000009c": { + "balance": "0x1" + }, + "000000000000000000000000000000000000009d": { + "balance": "0x1" + }, + "000000000000000000000000000000000000009e": { + "balance": "0x1" + }, + "000000000000000000000000000000000000009f": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000a0": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000a1": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000a2": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000a3": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000a4": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000a5": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000a6": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000a7": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000a8": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000a9": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000aa": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000ab": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000ac": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000ad": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000ae": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000af": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000b0": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000b1": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000b2": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000b3": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000b4": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000b5": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000b6": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000b7": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000b8": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000b9": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000ba": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000bb": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000bc": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000bd": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000be": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000bf": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000c0": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000c1": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000c2": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000c3": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000c4": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000c5": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000c6": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000c7": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000c8": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000c9": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000ca": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000cb": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000cc": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000cd": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000ce": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000cf": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000d0": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000d1": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000d2": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000d3": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000d4": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000d5": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000d6": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000d7": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000d8": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000d9": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000da": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000db": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000dc": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000dd": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000de": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000df": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000e0": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000e1": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000e2": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000e3": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000e4": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000e5": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000e6": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000e7": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000e8": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000e9": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000ea": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000eb": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000ec": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000ed": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000ee": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000ef": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000f0": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000f1": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000f2": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000f3": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000f4": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000f5": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000f6": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000f7": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000f8": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000f9": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000fa": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000fb": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000fc": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000fd": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000fe": { + "balance": "0x1" + }, + "00000000000000000000000000000000000000ff": { + "balance": "0x1" + }, + "000000000022d473030f116ddee9f6b43ac78ba3": { + "balance": "0x0", + "code": "0x6040608081526004908136101561001557600080fd5b600090813560e01c80630d58b1db1461126c578063137c29fe146110755780632a2d80d114610db75780632b67b57014610bde57806330f28b7a14610ade5780633644e51514610a9d57806336c7851614610a285780633ff9dcb1146109a85780634fe02b441461093f57806365d9723c146107ac57806387517c451461067a578063927da105146105c3578063cc53287f146104a3578063edd9444b1461033a5763fe8ec1a7146100c657600080fd5b346103365760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103365767ffffffffffffffff833581811161033257610114903690860161164b565b60243582811161032e5761012b903690870161161a565b6101336114e6565b9160843585811161032a5761014b9036908a016115c1565b98909560a43590811161032657610164913691016115c1565b969095815190610173826113ff565b606b82527f5065726d697442617463685769746e6573735472616e7366657246726f6d285460208301527f6f6b656e5065726d697373696f6e735b5d207065726d69747465642c61646472838301527f657373207370656e6465722c75696e74323536206e6f6e63652c75696e74323560608301527f3620646561646c696e652c000000000000000000000000000000000000000000608083015282519a8b9181610222602085018096611f93565b918237018a8152039961025b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09b8c8101835282611437565b5190209085515161026b81611ebb565b908a5b8181106102f95750506102f6999a6102ed9183516102a081610294602082018095611f66565b03848101835282611437565b519020602089810151858b015195519182019687526040820192909252336060820152608081019190915260a081019390935260643560c08401528260e081015b03908101835282611437565b51902093611cf7565b80f35b8061031161030b610321938c5161175e565b51612054565b61031b828661175e565b52611f0a565b61026e565b8880fd5b8780fd5b8480fd5b8380fd5b5080fd5b5091346103365760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103365767ffffffffffffffff9080358281116103325761038b903690830161164b565b60243583811161032e576103a2903690840161161a565b9390926103ad6114e6565b9160643590811161049f576103c4913691016115c1565b949093835151976103d489611ebb565b98885b81811061047d5750506102f697988151610425816103f9602082018095611f66565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282611437565b5190206020860151828701519083519260208401947ffcf35f5ac6a2c28868dc44c302166470266239195f02b0ee408334829333b7668652840152336060840152608083015260a082015260a081526102ed8161141b565b808b61031b8261049461030b61049a968d5161175e565b9261175e565b6103d7565b8680fd5b5082346105bf57602090817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103325780359067ffffffffffffffff821161032e576104f49136910161161a565b929091845b848110610504578580f35b8061051a610515600193888861196c565b61197c565b61052f84610529848a8a61196c565b0161197c565b3389528385528589209173ffffffffffffffffffffffffffffffffffffffff80911692838b528652868a20911690818a5285528589207fffffffffffffffffffffffff000000000000000000000000000000000000000081541690558551918252848201527f89b1add15eff56b3dfe299ad94e01f2b52fbcb80ae1a3baea6ae8c04cb2b98a4853392a2016104f9565b8280fd5b50346103365760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033657610676816105ff6114a0565b936106086114c3565b6106106114e6565b73ffffffffffffffffffffffffffffffffffffffff968716835260016020908152848420928816845291825283832090871683528152919020549251938316845260a083901c65ffffffffffff169084015260d09190911c604083015281906060820190565b0390f35b50346103365760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610336576106b26114a0565b906106bb6114c3565b916106c46114e6565b65ffffffffffff926064358481169081810361032a5779ffffffffffff0000000000000000000000000000000000000000947fda9fa7c1b00402c17d0161b249b1ab8bbec047c5a52207b9c112deffd817036b94338a5260016020527fffffffffffff0000000000000000000000000000000000000000000000000000858b209873ffffffffffffffffffffffffffffffffffffffff809416998a8d5260205283878d209b169a8b8d52602052868c209486156000146107a457504216925b8454921697889360a01b16911617179055815193845260208401523392a480f35b905092610783565b5082346105bf5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105bf576107e56114a0565b906107ee6114c3565b9265ffffffffffff604435818116939084810361032a57338852602091600183528489209673ffffffffffffffffffffffffffffffffffffffff80911697888b528452858a20981697888a5283528489205460d01c93848711156109175761ffff9085840316116108f05750907f55eb90d810e1700b35a8e7e25395ff7f2b2259abd7415ca2284dfb1c246418f393929133895260018252838920878a528252838920888a5282528389209079ffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffff000000000000000000000000000000000000000000000000000083549260d01b16911617905582519485528401523392a480f35b84517f24d35a26000000000000000000000000000000000000000000000000000000008152fd5b5084517f756688fe000000000000000000000000000000000000000000000000000000008152fd5b503461033657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610336578060209273ffffffffffffffffffffffffffffffffffffffff61098f6114a0565b1681528084528181206024358252845220549051908152f35b5082346105bf57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105bf577f3704902f963766a4e561bbaab6e6cdc1b1dd12f6e9e99648da8843b3f46b918d90359160243533855284602052818520848652602052818520818154179055815193845260208401523392a280f35b8234610a9a5760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610a9a57610a606114a0565b610a686114c3565b610a706114e6565b6064359173ffffffffffffffffffffffffffffffffffffffff8316830361032e576102f6936117a1565b80fd5b503461033657817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033657602090610ad7611b1e565b9051908152f35b508290346105bf576101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105bf57610b1a3661152a565b90807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7c36011261033257610b4c611478565b9160e43567ffffffffffffffff8111610bda576102f694610b6f913691016115c1565b939092610b7c8351612054565b6020840151828501519083519260208401947f939c21a48a8dbe3a9a2404a1d46691e4d39f6583d6ec6b35714604c986d801068652840152336060840152608083015260a082015260a08152610bd18161141b565b51902091611c25565b8580fd5b509134610336576101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033657610c186114a0565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc360160c08112610332576080855191610c51836113e3565b1261033257845190610c6282611398565b73ffffffffffffffffffffffffffffffffffffffff91602435838116810361049f578152604435838116810361049f57602082015265ffffffffffff606435818116810361032a5788830152608435908116810361049f576060820152815260a435938285168503610bda576020820194855260c4359087830182815260e43567ffffffffffffffff811161032657610cfe90369084016115c1565b929093804211610d88575050918591610d786102f6999a610d7e95610d238851611fbe565b90898c511690519083519260208401947ff3841cd1ff0085026a6327b620b67997ce40f282c88a8e905a7a5626e310f3d086528401526060830152608082015260808152610d70816113ff565b519020611bd9565b916120c7565b519251169161199d565b602492508a51917fcd21db4f000000000000000000000000000000000000000000000000000000008352820152fd5b5091346103365760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc93818536011261033257610df36114a0565b9260249081359267ffffffffffffffff9788851161032a578590853603011261049f578051978589018981108282111761104a578252848301358181116103265785019036602383011215610326578382013591610e50836115ef565b90610e5d85519283611437565b838252602093878584019160071b83010191368311611046578801905b828210610fe9575050508a526044610e93868801611509565b96838c01978852013594838b0191868352604435908111610fe557610ebb90369087016115c1565b959096804211610fba575050508998995151610ed681611ebb565b908b5b818110610f9757505092889492610d7892610f6497958351610f02816103f98682018095611f66565b5190209073ffffffffffffffffffffffffffffffffffffffff9a8b8b51169151928551948501957faf1b0d30d2cab0380e68f0689007e3254993c596f2fdd0aaa7f4d04f794408638752850152830152608082015260808152610d70816113ff565b51169082515192845b848110610f78578580f35b80610f918585610f8b600195875161175e565b5161199d565b01610f6d565b80610311610fac8e9f9e93610fb2945161175e565b51611fbe565b9b9a9b610ed9565b8551917fcd21db4f000000000000000000000000000000000000000000000000000000008352820152fd5b8a80fd5b6080823603126110465785608091885161100281611398565b61100b85611509565b8152611018838601611509565b838201526110278a8601611607565b8a8201528d611037818701611607565b90820152815201910190610e7a565b8c80fd5b84896041867f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b5082346105bf576101407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105bf576110b03661152a565b91807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7c360112610332576110e2611478565b67ffffffffffffffff93906101043585811161049f5761110590369086016115c1565b90936101243596871161032a57611125610bd1966102f6983691016115c1565b969095825190611134826113ff565b606482527f5065726d69745769746e6573735472616e7366657246726f6d28546f6b656e5060208301527f65726d697373696f6e73207065726d69747465642c6164647265737320737065848301527f6e6465722c75696e74323536206e6f6e63652c75696e7432353620646561646c60608301527f696e652c0000000000000000000000000000000000000000000000000000000060808301528351948591816111e3602085018096611f93565b918237018b8152039361121c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe095868101835282611437565b5190209261122a8651612054565b6020878101518589015195519182019687526040820192909252336060820152608081019190915260a081019390935260e43560c08401528260e081016102e1565b5082346105bf576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033257813567ffffffffffffffff92838211610bda5736602383011215610bda5781013592831161032e576024906007368386831b8401011161049f57865b8581106112e5578780f35b80821b83019060807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc83360301126103265761139288876001946060835161132c81611398565b611368608461133c8d8601611509565b9485845261134c60448201611509565b809785015261135d60648201611509565b809885015201611509565b918291015273ffffffffffffffffffffffffffffffffffffffff80808093169516931691166117a1565b016112da565b6080810190811067ffffffffffffffff8211176113b457604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6060810190811067ffffffffffffffff8211176113b457604052565b60a0810190811067ffffffffffffffff8211176113b457604052565b60c0810190811067ffffffffffffffff8211176113b457604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176113b457604052565b60c4359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b600080fd5b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b6044359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc01906080821261149b576040805190611563826113e3565b8082941261149b57805181810181811067ffffffffffffffff8211176113b457825260043573ffffffffffffffffffffffffffffffffffffffff8116810361149b578152602435602082015282526044356020830152606435910152565b9181601f8401121561149b5782359167ffffffffffffffff831161149b576020838186019501011161149b57565b67ffffffffffffffff81116113b45760051b60200190565b359065ffffffffffff8216820361149b57565b9181601f8401121561149b5782359167ffffffffffffffff831161149b576020808501948460061b01011161149b57565b91909160608184031261149b576040805191611666836113e3565b8294813567ffffffffffffffff9081811161149b57830182601f8201121561149b578035611693816115ef565b926116a087519485611437565b818452602094858086019360061b8501019381851161149b579086899897969594939201925b8484106116e3575050505050855280820135908501520135910152565b90919293949596978483031261149b578851908982019082821085831117611730578a928992845261171487611509565b81528287013583820152815201930191908897969594936116c6565b602460007f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b80518210156117725760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b92919273ffffffffffffffffffffffffffffffffffffffff604060008284168152600160205282828220961695868252602052818120338252602052209485549565ffffffffffff8760a01c16804211611884575082871696838803611812575b5050611810955016926118b5565b565b878484161160001461184f57602488604051907ff96fb0710000000000000000000000000000000000000000000000000000000082526004820152fd5b7fffffffffffffffffffffffff000000000000000000000000000000000000000084846118109a031691161790553880611802565b602490604051907fd81b2f2e0000000000000000000000000000000000000000000000000000000082526004820152fd5b9060006064926020958295604051947f23b872dd0000000000000000000000000000000000000000000000000000000086526004860152602485015260448401525af13d15601f3d116001600051141617161561190e57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152fd5b91908110156117725760061b0190565b3573ffffffffffffffffffffffffffffffffffffffff8116810361149b5790565b9065ffffffffffff908160608401511673ffffffffffffffffffffffffffffffffffffffff908185511694826020820151169280866040809401511695169560009187835260016020528383208984526020528383209916988983526020528282209184835460d01c03611af5579185611ace94927fc6a377bfc4eb120024a8ac08eef205be16b817020812c73223e81d1bdb9708ec98979694508715600014611ad35779ffffffffffff00000000000000000000000000000000000000009042165b60a01b167fffffffffffff00000000000000000000000000000000000000000000000000006001860160d01b1617179055519384938491604091949373ffffffffffffffffffffffffffffffffffffffff606085019616845265ffffffffffff809216602085015216910152565b0390a4565b5079ffffffffffff000000000000000000000000000000000000000087611a60565b600484517f756688fe000000000000000000000000000000000000000000000000000000008152fd5b467f000000000000000000000000000000000000000000000000000000000000038503611b69577f48deb34b39fb4b41f5c195008940d5ef510cdd7853eba5807b2fa08dfd58647590565b60405160208101907f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86682527f9ac997416e8ff9d2ff6bebeb7149f65cdae5e32e2b90440b566bb3044041d36a604082015246606082015230608082015260808152611bd3816113ff565b51902090565b611be1611b1e565b906040519060208201927f190100000000000000000000000000000000000000000000000000000000000084526022830152604282015260428152611bd381611398565b9192909360a435936040840151804211611cc65750602084510151808611611c955750918591610d78611c6594611c60602088015186611e47565b611bd9565b73ffffffffffffffffffffffffffffffffffffffff809151511692608435918216820361149b57611810936118b5565b602490604051907f3728b83d0000000000000000000000000000000000000000000000000000000082526004820152fd5b602490604051907fcd21db4f0000000000000000000000000000000000000000000000000000000082526004820152fd5b959093958051519560409283830151804211611e175750848803611dee57611d2e918691610d7860209b611c608d88015186611e47565b60005b868110611d42575050505050505050565b611d4d81835161175e565b5188611d5a83878a61196c565b01359089810151808311611dbe575091818888886001968596611d84575b50505050505001611d31565b611db395611dad9273ffffffffffffffffffffffffffffffffffffffff6105159351169561196c565b916118b5565b803888888883611d78565b6024908651907f3728b83d0000000000000000000000000000000000000000000000000000000082526004820152fd5b600484517fff633a38000000000000000000000000000000000000000000000000000000008152fd5b6024908551907fcd21db4f0000000000000000000000000000000000000000000000000000000082526004820152fd5b9073ffffffffffffffffffffffffffffffffffffffff600160ff83161b9216600052600060205260406000209060081c6000526020526040600020818154188091551615611e9157565b60046040517f756688fe000000000000000000000000000000000000000000000000000000008152fd5b90611ec5826115ef565b611ed26040519182611437565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0611f0082946115ef565b0190602036910137565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114611f375760010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b805160208092019160005b828110611f7f575050505090565b835185529381019392810192600101611f71565b9081519160005b838110611fab575050016000815290565b8060208092840101518185015201611f9a565b60405160208101917f65626cad6cb96493bf6f5ebea28756c966f023ab9e8a83a7101849d5573b3678835273ffffffffffffffffffffffffffffffffffffffff8082511660408401526020820151166060830152606065ffffffffffff9182604082015116608085015201511660a082015260a0815260c0810181811067ffffffffffffffff8211176113b45760405251902090565b6040516020808201927f618358ac3db8dc274f0cd8829da7e234bd48cd73c4a740aede1adec9846d06a1845273ffffffffffffffffffffffffffffffffffffffff81511660408401520151606082015260608152611bd381611398565b919082604091031261149b576020823592013590565b6000843b61222e5750604182036121ac576120e4828201826120b1565b939092604010156117725760209360009360ff6040608095013560f81c5b60405194855216868401526040830152606082015282805260015afa156121a05773ffffffffffffffffffffffffffffffffffffffff806000511691821561217657160361214c57565b60046040517f815e1d64000000000000000000000000000000000000000000000000000000008152fd5b60046040517f8baa579f000000000000000000000000000000000000000000000000000000008152fd5b6040513d6000823e3d90fd5b60408203612204576121c0918101906120b1565b91601b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84169360ff1c019060ff8211611f375760209360009360ff608094612102565b60046040517f4be6321b000000000000000000000000000000000000000000000000000000008152fd5b929391601f928173ffffffffffffffffffffffffffffffffffffffff60646020957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0604051988997889687947f1626ba7e000000000000000000000000000000000000000000000000000000009e8f8752600487015260406024870152816044870152868601378b85828601015201168101030192165afa9081156123a857829161232a575b507fffffffff000000000000000000000000000000000000000000000000000000009150160361230057565b60046040517fb0669cbc000000000000000000000000000000000000000000000000000000008152fd5b90506020813d82116123a0575b8161234460209383611437565b810103126103365751907fffffffff0000000000000000000000000000000000000000000000000000000082168203610a9a57507fffffffff0000000000000000000000000000000000000000000000000000000090386122d4565b3d9150612337565b6040513d84823e3d90fdfea164736f6c6343000811000a", + "nonce": "0x1" + }, + "0000000071727de22e5e9d8baf0edac6f37da032": { + "balance": "0x0", + "code": "0x60806040526004361015610024575b361561001957600080fd5b61002233612748565b005b60003560e01c806242dc5314611b0057806301ffc9a7146119ae5780630396cb60146116765780630bd28e3b146115fa5780631b2e01b814611566578063205c2878146113d157806322cdde4c1461136b57806335567e1a146112b35780635287ce12146111a557806370a0823114611140578063765e827f14610e82578063850aaf6214610dc35780639b249f6914610c74578063b760faf914610c3a578063bb9fe6bf14610a68578063c23a5cea146107c4578063dbed18e0146101a15763fc7e286d0361000e573461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5773ffffffffffffffffffffffffffffffffffffffff61013a61229f565b16600052600060205260a0604060002065ffffffffffff6001825492015460405192835260ff8116151560208401526dffffffffffffffffffffffffffff8160081c16604084015263ffffffff8160781c16606084015260981c166080820152f35b600080fd5b3461019c576101af36612317565b906101b86129bd565b60009160005b82811061056f57506101d08493612588565b6000805b8481106102fc5750507fbb47ee3e183a558b1a2ff0874b079f3fc5478b7454eacf2bfc5af2ff5878f972600080a16000809360005b81811061024757610240868660007f575ff3acadd5ab348fe1855e217e0f3678f8d767d7494c9f9fefbee2e17cca4d8180a2613ba7565b6001600255005b6102a261025582848a612796565b73ffffffffffffffffffffffffffffffffffffffff6102766020830161282a565b167f575ff3acadd5ab348fe1855e217e0f3678f8d767d7494c9f9fefbee2e17cca4d600080a2806127d6565b906000915b8083106102b957505050600101610209565b909194976102f36102ed6001926102e78c8b6102e0826102da8e8b8d61269d565b9261265a565b5191613597565b90612409565b99612416565b950191906102a7565b6020610309828789612796565b61031f61031682806127d6565b9390920161282a565b9160009273ffffffffffffffffffffffffffffffffffffffff8091165b8285106103505750505050506001016101d4565b909192939561037f83610378610366848c61265a565b516103728b898b61269d565b856129f6565b9290613dd7565b9116840361050a576104a5576103958491613dd7565b9116610440576103b5576103aa600191612416565b96019392919061033c565b60a487604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602160448201527f41413332207061796d61737465722065787069726564206f72206e6f7420647560648201527f65000000000000000000000000000000000000000000000000000000000000006084820152fd5b608488604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601460448201527f41413334207369676e6174757265206572726f720000000000000000000000006064820152fd5b608488604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601760448201527f414132322065787069726564206f72206e6f74206475650000000000000000006064820152fd5b608489604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601460448201527f41413234207369676e6174757265206572726f720000000000000000000000006064820152fd5b61057a818487612796565b9361058585806127d6565b919095602073ffffffffffffffffffffffffffffffffffffffff6105aa82840161282a565b1697600192838a1461076657896105da575b5050505060019293949550906105d191612409565b939291016101be565b8060406105e892019061284b565b918a3b1561019c57929391906040519485937f2dd8113300000000000000000000000000000000000000000000000000000000855288604486016040600488015252606490818601918a60051b8701019680936000915b8c83106106e657505050505050838392610684927ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8560009803016024860152612709565b03818a5afa90816106d7575b506106c657602486604051907f86a9f7500000000000000000000000000000000000000000000000000000000082526004820152fd5b93945084936105d1600189806105bc565b6106e0906121bd565b88610690565b91939596977fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c908a9294969a0301865288357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee18336030181121561019c57836107538793858394016128ec565b9a0196019301909189979695949261063f565b606483604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601760248201527f4141393620696e76616c69642061676772656761746f720000000000000000006044820152fd5b3461019c576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c576107fc61229f565b33600052600082526001604060002001908154916dffffffffffffffffffffffffffff8360081c16928315610a0a5765ffffffffffff8160981c1680156109ac57421061094e5760009373ffffffffffffffffffffffffffffffffffffffff859485947fffffffffffffff000000000000000000000000000000000000000000000000ff86951690556040517fb7c918e0e249f999e965cafeb6c664271b3f4317d296461500e71da39f0cbda33391806108da8786836020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b0390a2165af16108e8612450565b50156108f057005b606490604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601860248201527f6661696c656420746f207769746864726177207374616b6500000000000000006044820152fd5b606485604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601b60248201527f5374616b65207769746864726177616c206973206e6f742064756500000000006044820152fd5b606486604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601d60248201527f6d7573742063616c6c20756e6c6f636b5374616b6528292066697273740000006044820152fd5b606485604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601460248201527f4e6f207374616b6520746f2077697468647261770000000000000000000000006044820152fd5b3461019c5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c573360005260006020526001604060002001805463ffffffff8160781c16908115610bdc5760ff1615610b7e5765ffffffffffff908142160191818311610b4f5780547fffffffffffffff000000000000ffffffffffffffffffffffffffffffffffff001678ffffffffffff00000000000000000000000000000000000000609885901b161790556040519116815233907ffa9b3c14cc825c412c9ed81b3ba365a5b459439403f18829e572ed53a4180f0a90602090a2005b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f616c726561647920756e7374616b696e670000000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f6e6f74207374616b6564000000000000000000000000000000000000000000006044820152fd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c57610022610c6f61229f565b612748565b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5760043567ffffffffffffffff811161019c576020610cc8610d1b9236906004016122c2565b919073ffffffffffffffffffffffffffffffffffffffff9260405194859283927f570e1a360000000000000000000000000000000000000000000000000000000084528560048501526024840191612709565b03816000857f000000000000000000000000efc2c1444ebcc4db75e7613d20c6a62ff67a167c165af1908115610db757602492600092610d86575b50604051917f6ca7b806000000000000000000000000000000000000000000000000000000008352166004820152fd5b610da991925060203d602011610db0575b610da181836121ed565b8101906126dd565b9083610d56565b503d610d97565b6040513d6000823e3d90fd5b3461019c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c57610dfa61229f565b60243567ffffffffffffffff811161019c57600091610e1e839236906004016122c2565b90816040519283928337810184815203915af4610e39612450565b90610e7e6040519283927f99410554000000000000000000000000000000000000000000000000000000008452151560048401526040602484015260448301906123c6565b0390fd5b3461019c57610e9036612317565b610e9b9291926129bd565b610ea483612588565b60005b848110610f1c57506000927fbb47ee3e183a558b1a2ff0874b079f3fc5478b7454eacf2bfc5af2ff5878f972600080a16000915b858310610eec576102408585613ba7565b909193600190610f12610f0087898761269d565b610f0a888661265a565b519088613597565b0194019190610edb565b610f47610f40610f2e8385979561265a565b51610f3a84898761269d565b846129f6565b9190613dd7565b73ffffffffffffffffffffffffffffffffffffffff929183166110db5761107657610f7190613dd7565b911661101157610f8657600101929092610ea7565b60a490604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602160448201527f41413332207061796d61737465722065787069726564206f72206e6f7420647560648201527f65000000000000000000000000000000000000000000000000000000000000006084820152fd5b608482604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601460448201527f41413334207369676e6174757265206572726f720000000000000000000000006064820152fd5b608483604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601760448201527f414132322065787069726564206f72206e6f74206475650000000000000000006064820152fd5b608484604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601460448201527f41413234207369676e6174757265206572726f720000000000000000000000006064820152fd5b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5773ffffffffffffffffffffffffffffffffffffffff61118c61229f565b1660005260006020526020604060002054604051908152f35b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5773ffffffffffffffffffffffffffffffffffffffff6111f161229f565b6000608060405161120181612155565b828152826020820152826040820152826060820152015216600052600060205260a06040600020608060405161123681612155565b6001835493848352015490602081019060ff8316151582526dffffffffffffffffffffffffffff60408201818560081c16815263ffffffff936060840193858760781c16855265ffffffffffff978891019660981c1686526040519788525115156020880152511660408601525116606084015251166080820152f35b3461019c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5760206112ec61229f565b73ffffffffffffffffffffffffffffffffffffffff6113096122f0565b911660005260018252604060002077ffffffffffffffffffffffffffffffffffffffffffffffff821660005282526040600020547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000006040519260401b16178152f35b3461019c577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60208136011261019c576004359067ffffffffffffffff821161019c5761012090823603011261019c576113c9602091600401612480565b604051908152f35b3461019c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5761140861229f565b60243590336000526000602052604060002090815491828411611508576000808573ffffffffffffffffffffffffffffffffffffffff8295839561144c848a612443565b90556040805173ffffffffffffffffffffffffffffffffffffffff831681526020810185905233917fd1c19fbcd4551a5edfb66d43d2e337c04837afda3482b42bdf569a8fccdae5fb91a2165af16114a2612450565b50156114aa57005b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f6661696c656420746f20776974686472617700000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f576974686472617720616d6f756e7420746f6f206c61726765000000000000006044820152fd5b3461019c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5761159d61229f565b73ffffffffffffffffffffffffffffffffffffffff6115ba6122f0565b9116600052600160205277ffffffffffffffffffffffffffffffffffffffffffffffff604060002091166000526020526020604060002054604051908152f35b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5760043577ffffffffffffffffffffffffffffffffffffffffffffffff811680910361019c5733600052600160205260406000209060005260205260406000206116728154612416565b9055005b6020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5760043563ffffffff9182821680920361019c5733600052600081526040600020928215611950576001840154908160781c1683106118f2576116f86dffffffffffffffffffffffffffff9182349160081c16612409565b93841561189457818511611836579065ffffffffffff61180592546040519061172082612155565b8152848101926001845260408201908816815260608201878152600160808401936000855233600052600089526040600020905181550194511515917fffffffffffffffffffffffffff0000000000000000000000000000000000000060ff72ffffffff0000000000000000000000000000006effffffffffffffffffffffffffff008954945160081b16945160781b1694169116171717835551167fffffffffffffff000000000000ffffffffffffffffffffffffffffffffffffff78ffffffffffff0000000000000000000000000000000000000083549260981b169116179055565b6040519283528201527fa5ae833d0bb1dcd632d98a8b70973e8516812898e19bf27b70071ebc8dc52c0160403392a2005b606483604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152600e60248201527f7374616b65206f766572666c6f770000000000000000000000000000000000006044820152fd5b606483604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601260248201527f6e6f207374616b652073706563696669656400000000000000000000000000006044820152fd5b606482604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601c60248201527f63616e6e6f7420646563726561736520756e7374616b652074696d65000000006044820152fd5b606482604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601a60248201527f6d757374207370656369667920756e7374616b652064656c61790000000000006044820152fd5b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c576004357fffffffff00000000000000000000000000000000000000000000000000000000811680910361019c57807f60fc6b6e0000000000000000000000000000000000000000000000000000000060209214908115611ad6575b8115611aac575b8115611a82575b8115611a58575b506040519015158152f35b7f01ffc9a70000000000000000000000000000000000000000000000000000000091501482611a4d565b7f3e84f0210000000000000000000000000000000000000000000000000000000081149150611a46565b7fcf28ef970000000000000000000000000000000000000000000000000000000081149150611a3f565b7f915074d80000000000000000000000000000000000000000000000000000000081149150611a38565b3461019c576102007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5767ffffffffffffffff60043581811161019c573660238201121561019c57611b62903690602481600401359101612268565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc36016101c0811261019c5761014060405191611b9e83612155565b1261019c5760405192611bb0846121a0565b60243573ffffffffffffffffffffffffffffffffffffffff8116810361019c578452602093604435858201526064356040820152608435606082015260a435608082015260c43560a082015260e43560c08201526101043573ffffffffffffffffffffffffffffffffffffffff8116810361019c5760e08201526101243561010082015261014435610120820152825261016435848301526101843560408301526101a43560608301526101c43560808301526101e43590811161019c57611c7c9036906004016122c2565b905a3033036120f7578351606081015195603f5a0260061c61271060a0840151890101116120ce5760009681519182611ff0575b5050505090611cca915a9003608085015101923691612268565b925a90600094845193611cdc85613ccc565b9173ffffffffffffffffffffffffffffffffffffffff60e0870151168015600014611ea957505073ffffffffffffffffffffffffffffffffffffffff855116935b5a9003019360a06060820151910151016080860151850390818111611e95575b50508302604085015192818410600014611dce5750506003811015611da157600203611d79576113c99293508093611d7481613d65565b613cf6565b5050507fdeadaa51000000000000000000000000000000000000000000000000000000008152fd5b6024857f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b81611dde92979396940390613c98565b506003841015611e6857507f49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f60808683015192519473ffffffffffffffffffffffffffffffffffffffff865116948873ffffffffffffffffffffffffffffffffffffffff60e0890151169701519160405192835215898301528760408301526060820152a46113c9565b807f4e487b7100000000000000000000000000000000000000000000000000000000602492526021600452fd5b6064919003600a0204909301928780611d3d565b8095918051611eba575b5050611d1d565b6003861015611fc1576002860315611eb35760a088015190823b1561019c57600091611f2491836040519586809581947f7c627b210000000000000000000000000000000000000000000000000000000083528d60048401526080602484015260848301906123c6565b8b8b0260448301528b60648301520393f19081611fad575b50611fa65787893d610800808211611f9e575b506040519282828501016040528184528284013e610e7e6040519283927fad7954bc000000000000000000000000000000000000000000000000000000008452600484015260248301906123c6565b905083611f4f565b8980611eb3565b611fb89199506121bd565b6000978a611f3c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b91600092918380938c73ffffffffffffffffffffffffffffffffffffffff885116910192f115612023575b808080611cb0565b611cca929195503d6108008082116120c6575b5060405190888183010160405280825260008983013e805161205f575b5050600194909161201b565b7f1c4fada7374c0a9ee8841fc38afe82932dc0f8e69012e927f061a8bae611a20188870151918973ffffffffffffffffffffffffffffffffffffffff8551169401516120bc604051928392835260408d84015260408301906123c6565b0390a38680612053565b905088612036565b877fdeaddead000000000000000000000000000000000000000000000000000000006000526000fd5b606486604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601760248201527f4141393220696e7465726e616c2063616c6c206f6e6c790000000000000000006044820152fd5b60a0810190811067ffffffffffffffff82111761217157604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610140810190811067ffffffffffffffff82111761217157604052565b67ffffffffffffffff811161217157604052565b6060810190811067ffffffffffffffff82111761217157604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761217157604052565b67ffffffffffffffff811161217157601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b9291926122748261222e565b9161228260405193846121ed565b82948184528183011161019c578281602093846000960137010152565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361019c57565b9181601f8401121561019c5782359167ffffffffffffffff831161019c576020838186019501011161019c57565b6024359077ffffffffffffffffffffffffffffffffffffffffffffffff8216820361019c57565b9060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc83011261019c5760043567ffffffffffffffff9283821161019c578060238301121561019c57816004013593841161019c5760248460051b8301011161019c57602401919060243573ffffffffffffffffffffffffffffffffffffffff8116810361019c5790565b60005b8381106123b65750506000910152565b81810151838201526020016123a6565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602093612402815180928187528780880191016123a3565b0116010190565b91908201809211610b4f57565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610b4f5760010190565b91908203918211610b4f57565b3d1561247b573d906124618261222e565b9161246f60405193846121ed565b82523d6000602084013e565b606090565b604061248e8183018361284b565b90818351918237206124a3606084018461284b565b90818451918237209260c06124bb60e083018361284b565b908186519182372091845195602087019473ffffffffffffffffffffffffffffffffffffffff833516865260208301358789015260608801526080870152608081013560a087015260a081013582870152013560e08501526101009081850152835261012083019167ffffffffffffffff918484108385111761217157838252845190206101408501908152306101608601524661018086015260608452936101a00191821183831017612171575251902090565b67ffffffffffffffff81116121715760051b60200190565b9061259282612570565b6040906125a260405191826121ed565b8381527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06125d08295612570565b019160005b8381106125e25750505050565b60209082516125f081612155565b83516125fb816121a0565b600081526000849181838201528187820152816060818184015260809282848201528260a08201528260c08201528260e082015282610100820152826101208201528652818587015281898701528501528301528286010152016125d5565b805182101561266e5760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b919081101561266e5760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee18136030182121561019c570190565b9081602091031261019c575173ffffffffffffffffffffffffffffffffffffffff8116810361019c5790565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b7f2da466a7b24304f47e87fa2e1e5a81b9831ce54fec19055ce277ca2f39ba42c4602073ffffffffffffffffffffffffffffffffffffffff61278a3485613c98565b936040519485521692a2565b919081101561266e5760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa18136030182121561019c570190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561019c570180359067ffffffffffffffff821161019c57602001918160051b3603831361019c57565b3573ffffffffffffffffffffffffffffffffffffffff8116810361019c5790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561019c570180359067ffffffffffffffff821161019c5760200191813603831361019c57565b90357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561019c57016020813591019167ffffffffffffffff821161019c57813603831361019c57565b61012091813573ffffffffffffffffffffffffffffffffffffffff811680910361019c576129626129476129ba9561299b93855260208601356020860152612937604087018761289c565b9091806040880152860191612709565b612954606086018661289c565b908583036060870152612709565b6080840135608084015260a084013560a084015260c084013560c084015261298d60e085018561289c565b9084830360e0860152612709565b916129ac610100918281019061289c565b929091818503910152612709565b90565b60028054146129cc5760028055565b60046040517f3ee5aeb5000000000000000000000000000000000000000000000000000000008152fd5b926000905a93805194843573ffffffffffffffffffffffffffffffffffffffff811680910361019c5786526020850135602087015260808501356fffffffffffffffffffffffffffffffff90818116606089015260801c604088015260a086013560c088015260c086013590811661010088015260801c610120870152612a8060e086018661284b565b801561357b576034811061351d578060141161019c578060241161019c5760341161019c57602481013560801c60a0880152601481013560801c60808801523560601c60e08701525b612ad285612480565b60208301526040860151946effffffffffffffffffffffffffffff8660c08901511760608901511760808901511760a0890151176101008901511761012089015117116134bf57604087015160608801510160808801510160a08801510160c0880151016101008801510296835173ffffffffffffffffffffffffffffffffffffffff81511690612b66604085018561284b565b806131e4575b505060e0015173ffffffffffffffffffffffffffffffffffffffff1690600082156131ac575b6020612bd7918b828a01516000868a604051978896879586937f19822f7c00000000000000000000000000000000000000000000000000000000855260048501613db5565b0393f160009181613178575b50612c8b573d8c610800808311612c83575b50604051916020818401016040528083526000602084013e610e7e6040519283927f65c8fd4d000000000000000000000000000000000000000000000000000000008452600484015260606024840152600d60648401527f4141323320726576657274656400000000000000000000000000000000000000608484015260a0604484015260a48301906123c6565b915082612bf5565b9a92939495969798999a91156130f2575b509773ffffffffffffffffffffffffffffffffffffffff835116602084015190600052600160205260406000208160401c60005260205267ffffffffffffffff604060002091825492612cee84612416565b9055160361308d575a8503116130285773ffffffffffffffffffffffffffffffffffffffff60e0606093015116612d42575b509060a09184959697986040608096015260608601520135905a900301910152565b969550505a9683519773ffffffffffffffffffffffffffffffffffffffff60e08a01511680600052600060205260406000208054848110612fc3576080612dcd9a9b9c600093878094039055015192602089015183604051809d819582947f52b7512c0000000000000000000000000000000000000000000000000000000084528c60048501613db5565b039286f1978860009160009a612f36575b50612e86573d8b610800808311612e7e575b50604051916020818401016040528083526000602084013e610e7e6040519283927f65c8fd4d000000000000000000000000000000000000000000000000000000008452600484015260606024840152600d60648401527f4141333320726576657274656400000000000000000000000000000000000000608484015260a0604484015260a48301906123c6565b915082612df0565b9991929394959697989998925a900311612eab57509096959094939291906080612d20565b60a490604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602760448201527f41413336206f766572207061796d6173746572566572696669636174696f6e4760648201527f61734c696d6974000000000000000000000000000000000000000000000000006084820152fd5b915098503d90816000823e612f4b82826121ed565b604081838101031261019c5780519067ffffffffffffffff821161019c57828101601f83830101121561019c578181015191612f868361222e565b93612f9460405195866121ed565b838552820160208483850101011161019c57602092612fba9184808701918501016123a3565b01519838612dde565b60848b604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601e60448201527f41413331207061796d6173746572206465706f73697420746f6f206c6f7700006064820152fd5b608490604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601e60448201527f41413236206f76657220766572696669636174696f6e4761734c696d697400006064820152fd5b608482604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601a60448201527f4141323520696e76616c6964206163636f756e74206e6f6e63650000000000006064820152fd5b600052600060205260406000208054808c11613113578b9003905538612c9c565b608484604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601760448201527f41413231206469646e2774207061792070726566756e640000000000000000006064820152fd5b9091506020813d6020116131a4575b81613194602093836121ed565b8101031261019c57519038612be3565b3d9150613187565b508060005260006020526040600020548a81116000146131d75750612bd7602060005b915050612b92565b6020612bd7918c036131cf565b833b61345a57604088510151602060405180927f570e1a360000000000000000000000000000000000000000000000000000000082528260048301528160008161323260248201898b612709565b039273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000efc2c1444ebcc4db75e7613d20c6a62ff67a167c1690f1908115610db75760009161343b575b5073ffffffffffffffffffffffffffffffffffffffff811680156133d6578503613371573b1561330c5760141161019c5773ffffffffffffffffffffffffffffffffffffffff9183887fd51a9c61267aa6196961883ecf5ff2da6619c37dac0fa92122513fb32c032d2d604060e0958787602086015195510151168251913560601c82526020820152a391612b6c565b60848d604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602060448201527f4141313520696e6974436f6465206d757374206372656174652073656e6465726064820152fd5b60848e604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602060448201527f4141313420696e6974436f6465206d7573742072657475726e2073656e6465726064820152fd5b60848f604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601b60448201527f4141313320696e6974436f6465206661696c6564206f72204f4f4700000000006064820152fd5b613454915060203d602011610db057610da181836121ed565b3861327c565b60848d604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601f60448201527f414131302073656e64657220616c726561647920636f6e7374727563746564006064820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f41413934206761732076616c756573206f766572666c6f7700000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4141393320696e76616c6964207061796d6173746572416e64446174610000006044820152fd5b5050600060e087015260006080870152600060a0870152612ac9565b9092915a906060810151916040928351967fffffffff00000000000000000000000000000000000000000000000000000000886135d7606084018461284b565b600060038211613b9f575b7f8dd7712f0000000000000000000000000000000000000000000000000000000094168403613a445750505061379d6000926136b292602088015161363a8a5193849360208501528b602485015260648401906128ec565b90604483015203906136727fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0928381018352826121ed565b61379189519485927e42dc5300000000000000000000000000000000000000000000000000000000602085015261020060248501526102248401906123c6565b613760604484018b60806101a091805173ffffffffffffffffffffffffffffffffffffffff808251168652602082015160208701526040820151604087015260608201516060870152838201518487015260a082015160a087015260c082015160c087015260e08201511660e0860152610100808201519086015261012080910151908501526020810151610140850152604081015161016085015260608101516101808501520151910152565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc83820301610204840152876123c6565b039081018352826121ed565b6020918183809351910182305af1600051988652156137bf575b505050505050565b909192939495965060003d8214613a3a575b7fdeaddead00000000000000000000000000000000000000000000000000000000810361385b57608487878051917f220266b600000000000000000000000000000000000000000000000000000000835260048301526024820152600f60448201527f41413935206f7574206f662067617300000000000000000000000000000000006064820152fd5b7fdeadaa510000000000000000000000000000000000000000000000000000000091929395949650146000146138c55750506138a961389e6138b8935a90612443565b608085015190612409565b9083015183611d748295613d65565b905b3880808080806137b7565b909261395290828601518651907ff62676f440ff169a3a9afdbf812e89e7f95975ee8e5c31214ffdef631c5f479273ffffffffffffffffffffffffffffffffffffffff9580878551169401516139483d610800808211613a32575b508a519084818301018c5280825260008583013e8a805194859485528401528a8301906123c6565b0390a35a90612443565b916139636080860193845190612409565b926000905a94829488519761397789613ccc565b948260e08b0151168015600014613a1857505050875116955b5a9003019560a06060820151910151019051860390818111613a04575b5050840290850151928184106000146139de57505080611e68575090816139d89293611d7481613d65565b906138ba565b6139ee9082849397950390613c98565b50611e68575090826139ff92613cf6565b6139d8565b6064919003600a02049094019338806139ad565b90919892509751613a2a575b50613990565b955038613a24565b905038613920565b8181803e516137d1565b613b97945082935090613a8c917e42dc53000000000000000000000000000000000000000000000000000000006020613b6b9501526102006024860152610224850191612709565b613b3a604484018860806101a091805173ffffffffffffffffffffffffffffffffffffffff808251168652602082015160208701526040820151604087015260608201516060870152838201518487015260a082015160a087015260c082015160c087015260e08201511660e0860152610100808201519086015261012080910151908501526020810151610140850152604081015161016085015260608101516101808501520151910152565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc83820301610204840152846123c6565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018952886121ed565b60008761379d565b5081356135e2565b73ffffffffffffffffffffffffffffffffffffffff168015613c3a57600080809381935af1613bd4612450565b5015613bdc57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f41413931206661696c65642073656e6420746f2062656e6566696369617279006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f4141393020696e76616c69642062656e656669636961727900000000000000006044820152fd5b73ffffffffffffffffffffffffffffffffffffffff166000526000602052613cc66040600020918254612409565b80915590565b610120610100820151910151808214613cf257480180821015613ced575090565b905090565b5090565b9190917f49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f6080602083015192519473ffffffffffffffffffffffffffffffffffffffff946020868851169660e089015116970151916040519283526000602084015260408301526060820152a4565b60208101519051907f67b4fa9642f42120bf031f3051d1824b0fe25627945b27b8a6a65d5761d5482e60208073ffffffffffffffffffffffffffffffffffffffff855116940151604051908152a3565b613dcd604092959493956060835260608301906128ec565b9460208201520152565b8015613e6457600060408051613dec816121d1565b828152826020820152015273ffffffffffffffffffffffffffffffffffffffff811690604065ffffffffffff91828160a01c16908115613e5c575b60d01c92825191613e37836121d1565b8583528460208401521691829101524211908115613e5457509091565b905042109091565b839150613e27565b5060009060009056fea2646970667358221220b094fd69f04977ae9458e5ba422d01cd2d20dbcfca0992ff37f19aa07deec25464736f6c63430008170033", + "nonce": "0x1" + }, + "000f3df6d732807ef1319fb7b8bb8522d0beac02": { + "balance": "0x0", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500", + "nonce": "0x1" + }, + "02484cb50aac86eae85610d6f4bf026f30f6627d": { + "balance": "0x21e19e0c9bab2400000" + }, + "08135da0a343e492fa2d4282f2ae34c6c5cc1bbe": { + "balance": "0x21e19e0c9bab2400000" + }, + "09db0a93b389bef724429898f539aeb7ac2dd55f": { + "balance": "0x21e19e0c9bab2400000" + }, + "0b799c86a49deeb90402691f1041aa3af2d3c875": { + "balance": "0x0", + "nonce": "0x1" + }, + "13b0d85ccb8bf860b6b79af3029fca081ae9bef2": { + "balance": "0x0", + "code": "0x6080604052600436106100435760003560e01c8063076c37b21461004f578063481286e61461007157806356299481146100ba57806366cfa057146100da57600080fd5b3661004a57005b600080fd5b34801561005b57600080fd5b5061006f61006a366004610327565b6100fa565b005b34801561007d57600080fd5b5061009161008c366004610327565b61014a565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b3480156100c657600080fd5b506100916100d5366004610349565b61015d565b3480156100e657600080fd5b5061006f6100f53660046103ca565b610172565b61014582826040518060200161010f9061031a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082820381018352601f90910116604052610183565b505050565b600061015683836102e7565b9392505050565b600061016a8484846102f0565b949350505050565b61017d838383610183565b50505050565b6000834710156101f4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f437265617465323a20696e73756666696369656e742062616c616e636500000060448201526064015b60405180910390fd5b815160000361025f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f437265617465323a2062797465636f6465206c656e677468206973207a65726f60448201526064016101eb565b8282516020840186f5905073ffffffffffffffffffffffffffffffffffffffff8116610156576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f437265617465323a204661696c6564206f6e206465706c6f790000000000000060448201526064016101eb565b60006101568383305b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b61014e806104ad83390190565b6000806040838503121561033a57600080fd5b50508035926020909101359150565b60008060006060848603121561035e57600080fd5b8335925060208401359150604084013573ffffffffffffffffffffffffffffffffffffffff8116811461039057600080fd5b809150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000806000606084860312156103df57600080fd5b8335925060208401359150604084013567ffffffffffffffff8082111561040557600080fd5b818601915086601f83011261041957600080fd5b81358181111561042b5761042b61039b565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156104715761047161039b565b8160405282815289602084870101111561048a57600080fd5b826020860160208301376000602084830101528095505050505050925092509256fe608060405234801561001057600080fd5b5061012e806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063249cb3fa14602d575b600080fd5b603c603836600460b1565b604e565b60405190815260200160405180910390f35b60008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff16608857600060aa565b7fa2ef4600d742022d532d4747cb3547474667d6f13804902513b2ec01c848f4b45b9392505050565b6000806040838503121560c357600080fd5b82359150602083013573ffffffffffffffffffffffffffffffffffffffff8116811460ed57600080fd5b80915050925092905056fea26469706673582212205ffd4e6cede7d06a5daf93d48d0541fc68189eeb16608c1999a82063b666eb1164736f6c63430008130033a2646970667358221220fdc4a0fe96e3b21c108ca155438d37c9143fb01278a3c1d274948bad89c564ba64736f6c63430008130033", + "nonce": "0x1" + }, + "cd3b766ccdd6ae721141f452c550ca635964ce71": { + "balance": "0x21e19e0c9bab2400000" + }, + "dd2fd4581271e230360230f9337d5c0430bf44c0": { + "balance": "0x21e19e0c9bab2400000" + }, + "df37f81daad2b0327a0a50003740e1c935c70913": { + "balance": "0x21e19e0c9bab2400000" + }, + "df3e18d64bc6a983f673ab319ccae4f1a57c7097": { + "balance": "0x21e19e0c9bab2400000" + }, + "efc2c1444ebcc4db75e7613d20c6a62ff67a167c": { + "balance": "0x0", + "code": "0x6080600436101561000f57600080fd5b6000803560e01c63570e1a361461002557600080fd5b3461018a5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261018a576004359167ffffffffffffffff9081841161018657366023850112156101865783600401358281116101825736602482870101116101825780601411610182577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec810192808411610155577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f81600b8501160116830190838210908211176101555792846024819482600c60209a968b9960405286845289840196603889018837830101525193013560601c5af1908051911561014d575b5073ffffffffffffffffffffffffffffffffffffffff60405191168152f35b90503861012e565b6024857f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b8380fd5b8280fd5b80fdfea26469706673582212207adef8895ad3393b02fab10a111d85ea80ff35366aa43995f4ea20e67f29200664736f6c63430008170033", + "nonce": "0x1" + }, + "f39fd6e51aad88f6f4ce6ab8827279cfffb92266": { + "balance": "0x21e19e0c9bab2400000" + }, + "fabb0ac9d68b0b445fb7357272ff202c5651694a": { + "balance": "0x21e19e0c9bab2400000" + }, + "fb1bffc9d739b8d520daf37df666da4c687191ea": { + "balance": "0x0", + "code": "0x6080604052600436106101dc5760003560e01c8063affed0e011610102578063e19a9dd911610095578063f08a032311610064578063f08a032314611647578063f698da2514611698578063f8dc5dd9146116c3578063ffa1ad741461173e57610231565b8063e19a9dd91461139b578063e318b52b146113ec578063e75235b81461147d578063e86637db146114a857610231565b8063cc2f8452116100d1578063cc2f8452146110e8578063d4d9bdcd146111b5578063d8d11f78146111f0578063e009cfde1461132a57610231565b8063affed0e014610d94578063b4faba0914610dbf578063b63e800d14610ea7578063c4ca3a9c1461101757610231565b80635624b25b1161017a5780636a761202116101495780636a761202146109945780637d83297414610b50578063934f3a1114610bbf578063a0e67e2b14610d2857610231565b80635624b25b146107fb5780635ae6bd37146108b9578063610b592514610908578063694e80c31461095957610231565b80632f54bf6e116101b65780632f54bf6e146104d35780633408e4701461053a578063468721a7146105655780635229073f1461067a57610231565b80630d582f131461029e57806312fb68e0146102f95780632d9ad53d1461046c57610231565b36610231573373ffffffffffffffffffffffffffffffffffffffff167f3d0ce9bfc3ed7d6862dbb28b2dea94561fe714a1b4d019aa8af39730d1ad7c3d346040518082815260200191505060405180910390a2005b34801561023d57600080fd5b5060007f6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d560001b905080548061027257600080f35b36600080373360601b365260008060143601600080855af13d6000803e80610299573d6000fd5b3d6000f35b3480156102aa57600080fd5b506102f7600480360360408110156102c157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506117ce565b005b34801561030557600080fd5b5061046a6004803603608081101561031c57600080fd5b81019080803590602001909291908035906020019064010000000081111561034357600080fd5b82018360208201111561035557600080fd5b8035906020019184600183028401116401000000008311171561037757600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803590602001906401000000008111156103da57600080fd5b8201836020820111156103ec57600080fd5b8035906020019184600183028401116401000000008311171561040e57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929080359060200190929190505050611bbe565b005b34801561047857600080fd5b506104bb6004803603602081101561048f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612440565b60405180821515815260200191505060405180910390f35b3480156104df57600080fd5b50610522600480360360208110156104f657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612512565b60405180821515815260200191505060405180910390f35b34801561054657600080fd5b5061054f6125e4565b6040518082815260200191505060405180910390f35b34801561057157600080fd5b506106626004803603608081101561058857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001906401000000008111156105cf57600080fd5b8201836020820111156105e157600080fd5b8035906020019184600183028401116401000000008311171561060357600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803560ff1690602001909291905050506125f1565b60405180821515815260200191505060405180910390f35b34801561068657600080fd5b506107776004803603608081101561069d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001906401000000008111156106e457600080fd5b8201836020820111156106f657600080fd5b8035906020019184600183028401116401000000008311171561071857600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803560ff1690602001909291905050506126fc565b60405180831515815260200180602001828103825283818151815260200191508051906020019080838360005b838110156107bf5780820151818401526020810190506107a4565b50505050905090810190601f1680156107ec5780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b34801561080757600080fd5b5061083e6004803603604081101561081e57600080fd5b810190808035906020019092919080359060200190929190505050612732565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561087e578082015181840152602081019050610863565b50505050905090810190601f1680156108ab5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156108c557600080fd5b506108f2600480360360208110156108dc57600080fd5b81019080803590602001909291905050506127b9565b6040518082815260200191505060405180910390f35b34801561091457600080fd5b506109576004803603602081101561092b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506127d1565b005b34801561096557600080fd5b506109926004803603602081101561097c57600080fd5b8101908080359060200190929190505050612b63565b005b610b3860048036036101408110156109ab57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001906401000000008111156109f257600080fd5b820183602082011115610a0457600080fd5b80359060200191846001830284011164010000000083111715610a2657600080fd5b9091929391929390803560ff169060200190929190803590602001909291908035906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190640100000000811115610ab257600080fd5b820183602082011115610ac457600080fd5b80359060200191846001830284011164010000000083111715610ae657600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050612c9d565b60405180821515815260200191505060405180910390f35b348015610b5c57600080fd5b50610ba960048036036040811015610b7357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050612edc565b6040518082815260200191505060405180910390f35b348015610bcb57600080fd5b50610d2660048036036060811015610be257600080fd5b810190808035906020019092919080359060200190640100000000811115610c0957600080fd5b820183602082011115610c1b57600080fd5b80359060200191846001830284011164010000000083111715610c3d57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929080359060200190640100000000811115610ca057600080fd5b820183602082011115610cb257600080fd5b80359060200191846001830284011164010000000083111715610cd457600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050612f01565b005b348015610d3457600080fd5b50610d3d612f90565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610d80578082015181840152602081019050610d65565b505050509050019250505060405180910390f35b348015610da057600080fd5b50610da9613139565b6040518082815260200191505060405180910390f35b348015610dcb57600080fd5b50610ea560048036036040811015610de257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190640100000000811115610e1f57600080fd5b820183602082011115610e3157600080fd5b80359060200191846001830284011164010000000083111715610e5357600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929050505061313f565b005b348015610eb357600080fd5b506110156004803603610100811015610ecb57600080fd5b8101908080359060200190640100000000811115610ee857600080fd5b820183602082011115610efa57600080fd5b80359060200191846020830284011164010000000083111715610f1c57600080fd5b909192939192939080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190640100000000811115610f6757600080fd5b820183602082011115610f7957600080fd5b80359060200191846001830284011164010000000083111715610f9b57600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613161565b005b34801561102357600080fd5b506110d26004803603608081101561103a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561108157600080fd5b82018360208201111561109357600080fd5b803590602001918460018302840111640100000000831117156110b557600080fd5b9091929391929390803560ff16906020019092919050505061331f565b6040518082815260200191505060405180910390f35b3480156110f457600080fd5b506111416004803603604081101561110b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050613447565b60405180806020018373ffffffffffffffffffffffffffffffffffffffff168152602001828103825284818151815260200191508051906020019060200280838360005b838110156111a0578082015181840152602081019050611185565b50505050905001935050505060405180910390f35b3480156111c157600080fd5b506111ee600480360360208110156111d857600080fd5b8101908080359060200190929190505050613639565b005b3480156111fc57600080fd5b50611314600480360361014081101561121457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561125b57600080fd5b82018360208201111561126d57600080fd5b8035906020019184600183028401116401000000008311171561128f57600080fd5b9091929391929390803560ff169060200190929190803590602001909291908035906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506137d8565b6040518082815260200191505060405180910390f35b34801561133657600080fd5b506113996004803603604081101561134d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613805565b005b3480156113a757600080fd5b506113ea600480360360208110156113be57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613b96565b005b3480156113f857600080fd5b5061147b6004803603606081101561140f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613c1a565b005b34801561148957600080fd5b5061149261428c565b6040518082815260200191505060405180910390f35b3480156114b457600080fd5b506115cc60048036036101408110156114cc57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561151357600080fd5b82018360208201111561152557600080fd5b8035906020019184600183028401116401000000008311171561154757600080fd5b9091929391929390803560ff169060200190929190803590602001909291908035906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050614296565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561160c5780820151818401526020810190506115f1565b50505050905090810190601f1680156116395780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561165357600080fd5b506116966004803603602081101561166a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061443e565b005b3480156116a457600080fd5b506116ad61449f565b6040518082815260200191505060405180910390f35b3480156116cf57600080fd5b5061173c600480360360608110156116e657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061451d565b005b34801561174a57600080fd5b50611753614950565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015611793578082015181840152602081019050611778565b50505050905090810190601f1680156117c05780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6117d6614989565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141580156118405750600173ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b801561187857503073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b6118ea576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146119eb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303400000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60026000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508160026000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506003600081548092919060010191905055507f9465fa0c962cc76958e6373a993326400c1c94f8be2fe3a952adfa7f60b2ea2682604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a18060045414611bba57611bb981612b63565b5b5050565b611bd2604182614a2c90919063ffffffff16565b82511015611c48576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b6000808060008060005b8681101561243457611c648882614a66565b80945081955082965050505060008460ff16141561206d578260001c9450611c96604188614a2c90919063ffffffff16565b8260001c1015611d0e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b8751611d2760208460001c614a9590919063ffffffff16565b1115611d9b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323200000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60006020838a01015190508851611dd182611dc360208760001c614a9590919063ffffffff16565b614a9590919063ffffffff16565b1115611e45576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60606020848b010190506320c13b0b60e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168773ffffffffffffffffffffffffffffffffffffffff166320c13b0b8d846040518363ffffffff1660e01b8152600401808060200180602001838103835285818151815260200191508051906020019080838360005b83811015611ee7578082015181840152602081019050611ecc565b50505050905090810190601f168015611f145780820380516001836020036101000a031916815260200191505b50838103825284818151815260200191508051906020019080838360005b83811015611f4d578082015181840152602081019050611f32565b50505050905090810190601f168015611f7a5780820380516001836020036101000a031916815260200191505b5094505050505060206040518083038186803b158015611f9957600080fd5b505afa158015611fad573d6000803e3d6000fd5b505050506040513d6020811015611fc357600080fd5b81019080805190602001909291905050507bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614612066576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323400000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b50506122b2565b60018460ff161415612181578260001c94508473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16148061210a57506000600860008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008c81526020019081526020016000205414155b61217c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323500000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b6122b1565b601e8460ff1611156122495760018a60405160200180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c018281526020019150506040516020818303038152906040528051906020012060048603858560405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015612238573d6000803e3d6000fd5b5050506020604051035194506122b0565b60018a85858560405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa1580156122a3573d6000803e3d6000fd5b5050506020604051035194505b5b5b8573ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161180156123795750600073ffffffffffffffffffffffffffffffffffffffff16600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b80156123b25750600173ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614155b612424576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323600000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b8495508080600101915050611c52565b50505050505050505050565b60008173ffffffffffffffffffffffffffffffffffffffff16600173ffffffffffffffffffffffffffffffffffffffff161415801561250b5750600073ffffffffffffffffffffffffffffffffffffffff16600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b9050919050565b6000600173ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141580156125dd5750600073ffffffffffffffffffffffffffffffffffffffff16600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b9050919050565b6000804690508091505090565b60007fb648d3644f584ed1c2232d53c46d87e693586486ad0d1175f8656013110b714e3386868686604051808673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018060200183600181111561266b57fe5b8152602001828103825284818151815260200191508051906020019080838360005b838110156126a857808201518184015260208101905061268d565b50505050905090810190601f1680156126d55780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390a16126f285858585614ab4565b9050949350505050565b6000606061270c868686866125f1565b915060405160203d0181016040523d81523d6000602083013e8091505094509492505050565b606060006020830267ffffffffffffffff8111801561275057600080fd5b506040519080825280601f01601f1916602001820160405280156127835781602001600182028036833780820191505090505b50905060005b838110156127ae57808501548060208302602085010152508080600101915050612789565b508091505092915050565b60076020528060005260406000206000915090505481565b6127d9614989565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141580156128435750600173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b6128b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146129b6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303200000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60016000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060016000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507fecdf3a3effea5783a3c4c2140e677577666428d44ed9d474a0b3a4c9943f844081604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a150565b612b6b614989565b600354811115612be3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b6001811015612c5a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303200000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b806004819055507f610f7ff2b304ae8903c3de74c60c6ab1f7d6226b3f52c5161905bb5ad4039c936004546040518082815260200191505060405180910390a150565b6000606060055433600454604051602001808481526020018373ffffffffffffffffffffffffffffffffffffffff168152602001828152602001935050505060405160208183030381529060405290507f66753cd2356569ee081232e3be8909b950e0a76c1f8460c3a5e3c2be32b11bed8d8d8d8d8d8d8d8d8d8d8d8c604051808d73ffffffffffffffffffffffffffffffffffffffff1681526020018c8152602001806020018a6001811115612d5057fe5b81526020018981526020018881526020018781526020018673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff168152602001806020018060200184810384528e8e82818152602001925080828437600081840152601f19601f820116905080830192505050848103835286818151815260200191508051906020019080838360005b83811015612e0a578082015181840152602081019050612def565b50505050905090810190601f168015612e375780820380516001836020036101000a031916815260200191505b50848103825285818151815260200191508051906020019080838360005b83811015612e70578082015181840152602081019050612e55565b50505050905090810190601f168015612e9d5780820380516001836020036101000a031916815260200191505b509f5050505050505050505050505050505060405180910390a1612eca8d8d8d8d8d8d8d8d8d8d8d614c9a565b9150509b9a5050505050505050505050565b6008602052816000526040600020602052806000526040600020600091509150505481565b6000600454905060008111612f7e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330303100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b612f8a84848484611bbe565b50505050565b6060600060035467ffffffffffffffff81118015612fad57600080fd5b50604051908082528060200260200182016040528015612fdc5781602001602082028036833780820191505090505b50905060008060026000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505b600173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614613130578083838151811061308757fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508180600101925050613046565b82935050505090565b60055481565b600080825160208401855af4806000523d6020523d600060403e60403d016000fd5b6131ac8a8a80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050896151d7565b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16146131ea576131e9846156d7565b5b6132388787878080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050615706565b60008211156132525761325082600060018685615941565b505b3373ffffffffffffffffffffffffffffffffffffffff167f141df868a6331af528e38c83b7aa03edc19be66e37ae67f9285bf4f8e3c6a1a88b8b8b8b8960405180806020018581526020018473ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1681526020018281038252878782818152602001925060200280828437600081840152601f19601f820116905080830192505050965050505050505060405180910390a250505050505050505050565b6000805a9050613376878787878080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050865a615b47565b61337f57600080fd5b60005a8203905080604051602001808281526020019150506040516020818303038152906040526040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561340c5780820151818401526020810190506133f1565b50505050905090810190601f1680156134395780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b606060008267ffffffffffffffff8111801561346257600080fd5b506040519080825280602002602001820160405280156134915781602001602082028036833780820191505090505b509150600080600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141580156135645750600173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b801561356f57508482105b1561362a578084838151811061358157fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600160008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081806001019250506134fa565b80925081845250509250929050565b600073ffffffffffffffffffffffffffffffffffffffff16600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561373b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330333000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b6001600860003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000838152602001908152602001600020819055503373ffffffffffffffffffffffffffffffffffffffff16817ff2a0eb156472d1440255b0d7c1e19cc07115d1051fe605b0dce69acfec884d9c60405160405180910390a350565b60006137ed8c8c8c8c8c8c8c8c8c8c8c614296565b8051906020012090509b9a5050505050505050505050565b61380d614989565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141580156138775750600173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b6138e9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff16600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146139e9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600160008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507faab4fa2b463f581b2b32cb3b7e3b704b9ce37cc209b5fb4d77e593ace405427681604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a15050565b613b9e614989565b60007f4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c860001b90508181557f1151116914515bc0891ff9047a6cb32cf902546f83066499bcf8ba33d2353fa282604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a15050565b613c22614989565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614158015613c8c5750600173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b8015613cc457503073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b613d36576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614613e37576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303400000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614158015613ea15750600173ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b613f13576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff16600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614614013576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303500000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507ff8d49fc529812e9a7c5c50e69c20f0dccc0db8fa95c98bc58cc9a4f1c1299eaf82604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a17f9465fa0c962cc76958e6373a993326400c1c94f8be2fe3a952adfa7f60b2ea2681604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a1505050565b6000600454905090565b606060007fbb8310d486368db6bd6f849402fdd73ad53d316b5a4b2644ad6efe0f941286d860001b8d8d8d8d60405180838380828437808301925050509250505060405180910390208c8c8c8c8c8c8c604051602001808c81526020018b73ffffffffffffffffffffffffffffffffffffffff1681526020018a815260200189815260200188600181111561432757fe5b81526020018781526020018681526020018581526020018473ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019b505050505050505050505050604051602081830303815290604052805190602001209050601960f81b600160f81b6143b361449f565b8360405160200180857effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152600101847effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526001018381526020018281526020019450505050506040516020818303038152906040529150509b9a5050505050505050505050565b614446614989565b61444f816156d7565b7f5ac6c46c93c8d0e53714ba3b53db3e7c046da994313d7ed0d192028bc7c228b081604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a150565b60007f47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a7946921860001b6144cd6125e4565b30604051602001808481526020018381526020018273ffffffffffffffffffffffffffffffffffffffff168152602001935050505060405160208183030381529060405280519060200120905090565b614525614989565b8060016003540310156145a0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415801561460a5750600173ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b61467c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff16600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461477c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303500000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600360008154809291906001900391905055507ff8d49fc529812e9a7c5c50e69c20f0dccc0db8fa95c98bc58cc9a4f1c1299eaf82604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a1806004541461494b5761494a81612b63565b5b505050565b6040518060400160405280600581526020017f312e332e3000000000000000000000000000000000000000000000000000000081525081565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614614a2a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330333100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b565b600080831415614a3f5760009050614a60565b6000828402905082848281614a5057fe5b0414614a5b57600080fd5b809150505b92915050565b60008060008360410260208101860151925060408101860151915060ff60418201870151169350509250925092565b600080828401905083811015614aaa57600080fd5b8091505092915050565b6000600173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614158015614b7f5750600073ffffffffffffffffffffffffffffffffffffffff16600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b614bf1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303400000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b614bfe858585855a615b47565b90508015614c4e573373ffffffffffffffffffffffffffffffffffffffff167f6895c13664aa4f67288b25d7a21d7aaa34916e355fb9b6fae0a139a9085becb860405160405180910390a2614c92565b3373ffffffffffffffffffffffffffffffffffffffff167facd2c8702804128fdb0db2bb49f6d127dd0181c13fd45dbfe16de0930e2bd37560405160405180910390a25b949350505050565b6000806000614cb48e8e8e8e8e8e8e8e8e8e600554614296565b905060056000815480929190600101919050555080805190602001209150614cdd828286612f01565b506000614ce8615b93565b9050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614614ece578073ffffffffffffffffffffffffffffffffffffffff166375f0bb528f8f8f8f8f8f8f8f8f8f8f336040518d63ffffffff1660e01b8152600401808d73ffffffffffffffffffffffffffffffffffffffff1681526020018c8152602001806020018a6001811115614d8b57fe5b81526020018981526020018881526020018781526020018673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff168152602001806020018473ffffffffffffffffffffffffffffffffffffffff16815260200183810383528d8d82818152602001925080828437600081840152601f19601f820116905080830192505050838103825285818151815260200191508051906020019080838360005b83811015614e5d578082015181840152602081019050614e42565b50505050905090810190601f168015614e8a5780820380516001836020036101000a031916815260200191505b509e505050505050505050505050505050600060405180830381600087803b158015614eb557600080fd5b505af1158015614ec9573d6000803e3d6000fd5b505050505b6101f4614ef56109c48b01603f60408d0281614ee657fe5b04615bc490919063ffffffff16565b015a1015614f6b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330313000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60005a9050614fd48f8f8f8f8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050508e60008d14614fc9578e614fcf565b6109c45a035b615b47565b9350614fe95a82615bde90919063ffffffff16565b90508380614ff8575060008a14155b80615004575060008814155b615076576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330313300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b6000808911156150905761508d828b8b8b8b615941565b90505b84156150da577f442e715f626346e8c54381002da614f62bee8d27386535b2521ec8540898556e8482604051808381526020018281526020019250505060405180910390a161511a565b7f23428b18acfb3ea64b08dc0c1d296ea9c09702c09083ca5272e64d115b687d238482604051808381526020018281526020019250505060405180910390a15b5050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146151c6578073ffffffffffffffffffffffffffffffffffffffff16639327136883856040518363ffffffff1660e01b815260040180838152602001821515815260200192505050600060405180830381600087803b1580156151ad57600080fd5b505af11580156151c1573d6000803e3d6000fd5b505050505b50509b9a5050505050505050505050565b60006004541461524f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b81518111156152c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600181101561533d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303200000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60006001905060005b835181101561564357600084828151811061535d57fe5b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141580156153d15750600173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b801561540957503073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b801561544157508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614155b6154b3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146155b4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303400000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b80600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550809250508080600101915050615346565b506001600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550825160038190555081600481905550505050565b60007f6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d560001b90508181555050565b600073ffffffffffffffffffffffffffffffffffffffff1660016000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614615808576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b6001806000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161461593d576158ca8260008360015a615b47565b61593c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330303000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b5b5050565b600080600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161461597e5782615980565b325b9050600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161415615a98576159ea3a86106159c7573a6159c9565b855b6159dc888a614a9590919063ffffffff16565b614a2c90919063ffffffff16565b91508073ffffffffffffffffffffffffffffffffffffffff166108fc839081150290604051600060405180830381858888f19350505050615a93576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330313100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b615b3d565b615abd85615aaf888a614a9590919063ffffffff16565b614a2c90919063ffffffff16565b9150615aca848284615bfe565b615b3c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330313200000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b5b5095945050505050565b6000600180811115615b5557fe5b836001811115615b6157fe5b1415615b7a576000808551602087018986f49050615b8a565b600080855160208701888a87f190505b95945050505050565b6000807f4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c860001b9050805491505090565b600081831015615bd45781615bd6565b825b905092915050565b600082821115615bed57600080fd5b600082840390508091505092915050565b60008063a9059cbb8484604051602401808373ffffffffffffffffffffffffffffffffffffffff168152602001828152602001925050506040516020818303038152906040529060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050602060008251602084016000896127105a03f13d60008114615ca55760208114615cad5760009350615cb8565b819350615cb8565b600051158215171593505b505050939250505056fea2646970667358221220047fac33099ca576d1c4f1ac6a8abdb0396e42ad6a397d2cb2f4dc1624cc0c5b64736f6c63430007060033", + "nonce": "0x1" + } + }, + "baseFeePerGas": "0x1", + "blobGasUsed": "0x0", + "coinbase": "0x4200000000000000000000000000000000000011", + "config": { + "arrowGlacierBlock": 0, + "bedrockBlock": 0, + "berlinBlock": 0, + "byzantiumBlock": 0, + "cancunTime": 0, + "canyonTime": 0, + "chainId": 901, + "constantinopleBlock": 0, + "depositContractAddress": "0x4242424242424242424242424242424242424242", + "ecotoneTime": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "fjordTime": 0, + "graniteTime": 0, + "grayGlacierBlock": 0, + "homesteadBlock": 0, + "istanbulBlock": 0, + "londonBlock": 0, + "mergeNetsplitBlock": 0, + "muirGlacierBlock": 0, + "blobSchedule": { + "cancun": { + "target": 3, + "max": 6, + "baseFeeUpdateFraction": 3338477 + }, + "prague": { + "target": 6, + "max": 9, + "baseFeeUpdateFraction": 5007716 + }, + "osaka": { + "target": 9, + "max": 12, + "baseFeeUpdateFraction": 5007716 + } + }, + "optimism": { + "eip1559Denominator": 50, + "eip1559DenominatorCanyon": 250, + "eip1559Elasticity": 6 + }, + "petersburgBlock": 0, + "regolithTime": 0, + "shanghaiTime": 0, + "terminalTotalDifficulty": 0, + "terminalTotalDifficultyPassed": true + }, + "difficulty": "0x0", + "excessBlobGas": "0x0", + "extraData": "0x", + "gasLimit": "0x1c9c380", + "gasUsed": "0x0", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0", + "number": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "timestamp": "" +} \ No newline at end of file diff --git a/tests/integration/testdata/jwt_secret.hex b/tests/integration/testdata/jwt_secret.hex new file mode 100644 index 00000000..6e72091c --- /dev/null +++ b/tests/integration/testdata/jwt_secret.hex @@ -0,0 +1 @@ +688f5d737bad920bdfb2fc2f488d6b6209eebda1dae949a8de91398d932c517a diff --git a/tests/integration/testdata/p2p_secret.hex b/tests/integration/testdata/p2p_secret.hex new file mode 100644 index 00000000..499ea6cd --- /dev/null +++ b/tests/integration/testdata/p2p_secret.hex @@ -0,0 +1 @@ +a11ac89899cd86e36b6fb881ec1255b8a92a688790b7d950f8b7d8dd626671fb diff --git a/tests/no_tx_pool.rs b/tests/no_tx_pool.rs new file mode 100644 index 00000000..1396ec3f --- /dev/null +++ b/tests/no_tx_pool.rs @@ -0,0 +1,28 @@ +use alloy_primitives::B256; +use serde_json::Value; +use std::sync::{Arc, Mutex}; +use std::time::Duration; + +mod integration; + +use crate::integration::RollupBoostTestHarnessBuilder; +// use crate::server::ExecutionMode; +use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; + +#[tokio::test] +async fn test_integration_simple() -> eyre::Result<()> { + let harness = RollupBoostTestHarnessBuilder::new("test_integration_simple") + .build() + .await?; + let mut block_generator = harness.get_block_generator().await?; + + for _ in 0..5 { + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!( + block_creator.is_builder(), + "Block creator should be the builder" + ); + } + + Ok(()) +} From f76a5fa16d897d19dd36e7cf32488c3b36eea70e Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Thu, 3 Apr 2025 17:19:37 -0700 Subject: [PATCH 06/20] move tests to individual files --- tests/builder_full_delay.rs | 53 +++++ tests/builder_returns_incorrect_block.rs | 57 +++++ tests/execution_mode.rs | 102 ++++++++ tests/integration/integration_test.rs | 284 ----------------------- tests/integration/mod.rs | 1 - tests/remote_builder_down.rs | 54 +++++ tests/simple.rs | 28 +++ 7 files changed, 294 insertions(+), 285 deletions(-) create mode 100644 tests/builder_full_delay.rs create mode 100644 tests/builder_returns_incorrect_block.rs create mode 100644 tests/execution_mode.rs delete mode 100644 tests/integration/integration_test.rs create mode 100644 tests/remote_builder_down.rs create mode 100644 tests/simple.rs diff --git a/tests/builder_full_delay.rs b/tests/builder_full_delay.rs new file mode 100644 index 00000000..b51f7cae --- /dev/null +++ b/tests/builder_full_delay.rs @@ -0,0 +1,53 @@ +use alloy_primitives::B256; +use integration::RollupBoostTestHarnessBuilder; +use rollup_boost::ExecutionMode; +use serde_json::Value; +use std::sync::{Arc, Mutex}; +use std::time::Duration; + +use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; + +mod integration; + +#[tokio::test] +async fn test_integration_builder_full_delay() -> eyre::Result<()> { + // Create a dynamic handler that delays all the calls by 2 seconds + let delay = Arc::new(Mutex::new(Duration::from_secs(0))); + + let delay_for_handler = delay.clone(); + let handler = Box::new(move |_method: &str, _params: Value, _result: Value| { + let delay = delay_for_handler.lock().unwrap(); + // sleep the amount of time specified in the delay + std::thread::sleep(*delay); + None + }); + + // This integration test checks that if the builder has a general delay in processing ANY of the requests, + // rollup-boost does not stop building blocks. + let harness = RollupBoostTestHarnessBuilder::new("test_integration_builder_full_delay") + .proxy_handler(handler) + .build() + .await?; + + let mut block_generator = harness.get_block_generator().await?; + + // create 3 blocks that are processed by the builder + for _ in 0..3 { + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!( + block_creator.is_builder(), + "Block creator should be the builder" + ); + } + + // add the delay + *delay.lock().unwrap() = Duration::from_secs(2); + + // create 3 blocks that are processed by the builder + for _ in 0..3 { + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!(block_creator.is_l2(), "Block creator should be the builder"); + } + + Ok(()) +} diff --git a/tests/builder_returns_incorrect_block.rs b/tests/builder_returns_incorrect_block.rs new file mode 100644 index 00000000..61574adf --- /dev/null +++ b/tests/builder_returns_incorrect_block.rs @@ -0,0 +1,57 @@ +use alloy_primitives::B256; +use integration::RollupBoostTestHarnessBuilder; +use rollup_boost::ExecutionMode; +use serde_json::Value; +use std::sync::{Arc, Mutex}; +use std::time::Duration; + +use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; + +mod integration; + +#[tokio::test] +async fn test_integration_builder_returns_incorrect_block() -> eyre::Result<()> { + // Test that the builder returns a block with an incorrect state root and that rollup-boost + // does not process it. + let handler = Box::new(move |method: &str, _params: Value, _result: Value| { + if method != "engine_getPayloadV3" { + return None; + } + + let mut payload = serde_json::from_value::(_result).unwrap(); + + // modify the state root field + payload + .execution_payload + .payload_inner + .payload_inner + .state_root = B256::ZERO; + + let result = serde_json::to_value(&payload).unwrap(); + Some(result) + }); + + let harness = + RollupBoostTestHarnessBuilder::new("test_integration_builder_returns_incorrect_block") + .proxy_handler(handler) + .build() + .await?; + + let mut block_generator = harness.get_block_generator().await?; + + // create 3 blocks that are processed by the builder + for _ in 0..3 { + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!(block_creator.is_l2(), "Block creator should be the builder"); + } + // check that at some point we had the log "builder payload was not valid" which signals + // that the builder returned a payload that was not valid and rollup-boost did not process it. + // read lines + let logs = std::fs::read_to_string(harness.rollup_boost.args().log_file.clone().unwrap())?; + assert!( + logs.contains("Invalid payload"), + "Logs should contain the message 'builder payload was not valid'" + ); + + Ok(()) +} diff --git a/tests/execution_mode.rs b/tests/execution_mode.rs new file mode 100644 index 00000000..126b8c63 --- /dev/null +++ b/tests/execution_mode.rs @@ -0,0 +1,102 @@ +use rollup_boost::ExecutionMode; +use serde_json::Value; +use std::sync::{Arc, Mutex}; +use std::time::Duration; + +mod integration; + +use crate::integration::RollupBoostTestHarnessBuilder; + +#[tokio::test] +async fn test_integration_execution_mode() -> eyre::Result<()> { + // Create a counter that increases whenever we receive a new RPC call in the builder + let counter = Arc::new(Mutex::new(0)); + + let counter_for_handler = counter.clone(); + let handler = Box::new(move |_method: &str, _params: Value, _result: Value| { + let mut counter = counter_for_handler.lock().unwrap(); + + *counter += 1; + None + }); + + let harness = RollupBoostTestHarnessBuilder::new("test_integration_dry_run") + .proxy_handler(handler) + .build() + .await?; + let mut block_generator = harness.get_block_generator().await?; + + // start creating 5 empty blocks which are processed by the builder + for _ in 0..5 { + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!( + block_creator.is_builder(), + "Block creator should be the builder" + ); + } + + let client = harness.get_client().await; + + // enable dry run mode + { + let response = client + .set_execution_mode(ExecutionMode::DryRun) + .await + .unwrap(); + assert_eq!(response.execution_mode, ExecutionMode::DryRun); + + // the new valid block should be created the the l2 builder + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!(block_creator.is_l2(), "Block creator should be l2"); + } + + // toggle again dry run mode + { + let response = client + .set_execution_mode(ExecutionMode::Enabled) + .await + .unwrap(); + assert_eq!(response.execution_mode, ExecutionMode::Enabled); + + // the new valid block should be created the the builder + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!( + block_creator.is_builder(), + "Block creator should be the builder" + ); + } + + // sleep for 1 second so that it has time to send the last FCU request to the builder + // and there is not a race condition with the disable call + std::thread::sleep(Duration::from_secs(1)); + + tracing::info!("Setting execution mode to disabled"); + + // Set the execution mode to disabled and reset the counter in the proxy to 0 + // to track the number of calls to the builder during the disabled mode which + // should be 0 + { + let response = client + .set_execution_mode(ExecutionMode::Disabled) + .await + .unwrap(); + assert_eq!(response.execution_mode, ExecutionMode::Disabled); + + // reset the counter in the proxy + *counter.lock().unwrap() = 0; + + // create 5 blocks which are processed by the l2 clients + for _ in 0..5 { + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!(block_creator.is_l2(), "Block creator should be l2"); + } + + assert_eq!( + *counter.lock().unwrap(), + 0, + "Number of calls to the builder should be 0", + ); + } + + Ok(()) +} diff --git a/tests/integration/integration_test.rs b/tests/integration/integration_test.rs deleted file mode 100644 index 1e2aa03b..00000000 --- a/tests/integration/integration_test.rs +++ /dev/null @@ -1,284 +0,0 @@ -#[cfg(test)] -mod tests { - use alloy_primitives::B256; - use rollup_boost::ExecutionMode; - use serde_json::Value; - use std::sync::{Arc, Mutex}; - use std::time::Duration; - - use crate::integration::RollupBoostTestHarnessBuilder; - use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; - - #[tokio::test] - async fn test_integration_simple() -> eyre::Result<()> { - let harness = RollupBoostTestHarnessBuilder::new("test_integration_simple") - .build() - .await?; - let mut block_generator = harness.get_block_generator().await?; - - for _ in 0..5 { - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!( - block_creator.is_builder(), - "Block creator should be the builder" - ); - } - - Ok(()) - } - - #[tokio::test] - async fn test_integration_no_tx_pool() -> eyre::Result<()> { - let harness = RollupBoostTestHarnessBuilder::new("test_integration_no_tx_pool") - .build() - .await?; - let mut block_generator = harness.get_block_generator().await?; - - // start creating 5 empty blocks which are processed by the L2 builder - for _ in 0..5 { - let (_block, block_creator) = block_generator.generate_block(true).await?; - assert!(block_creator.is_l2(), "Block creator should be l2"); - } - - // process 5 more non empty blocks which are processed by the builder. - // The builder should be on sync because it has received the new payload requests from rollup-boost. - for _ in 0..5 { - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!( - block_creator.is_builder(), - "Block creator should be the builder" - ); - } - - Ok(()) - } - - #[tokio::test] - async fn test_integration_execution_mode() -> eyre::Result<()> { - // Create a counter that increases whenever we receive a new RPC call in the builder - let counter = Arc::new(Mutex::new(0)); - - let counter_for_handler = counter.clone(); - let handler = Box::new(move |_method: &str, _params: Value, _result: Value| { - let mut counter = counter_for_handler.lock().unwrap(); - - *counter += 1; - None - }); - - let harness = RollupBoostTestHarnessBuilder::new("test_integration_dry_run") - .proxy_handler(handler) - .build() - .await?; - let mut block_generator = harness.get_block_generator().await?; - - // start creating 5 empty blocks which are processed by the builder - for _ in 0..5 { - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!( - block_creator.is_builder(), - "Block creator should be the builder" - ); - } - - let client = harness.get_client().await; - - // enable dry run mode - { - let response = client - .set_execution_mode(ExecutionMode::DryRun) - .await - .unwrap(); - assert_eq!(response.execution_mode, ExecutionMode::DryRun); - - // the new valid block should be created the the l2 builder - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!(block_creator.is_l2(), "Block creator should be l2"); - } - - // toggle again dry run mode - { - let response = client - .set_execution_mode(ExecutionMode::Enabled) - .await - .unwrap(); - assert_eq!(response.execution_mode, ExecutionMode::Enabled); - - // the new valid block should be created the the builder - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!( - block_creator.is_builder(), - "Block creator should be the builder" - ); - } - - // sleep for 1 second so that it has time to send the last FCU request to the builder - // and there is not a race condition with the disable call - std::thread::sleep(Duration::from_secs(1)); - - tracing::info!("Setting execution mode to disabled"); - - // Set the execution mode to disabled and reset the counter in the proxy to 0 - // to track the number of calls to the builder during the disabled mode which - // should be 0 - { - let response = client - .set_execution_mode(ExecutionMode::Disabled) - .await - .unwrap(); - assert_eq!(response.execution_mode, ExecutionMode::Disabled); - - // reset the counter in the proxy - *counter.lock().unwrap() = 0; - - // create 5 blocks which are processed by the l2 clients - for _ in 0..5 { - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!(block_creator.is_l2(), "Block creator should be l2"); - } - - assert_eq!( - *counter.lock().unwrap(), - 0, - "Number of calls to the builder should be 0", - ); - } - - Ok(()) - } - - #[tokio::test] - async fn test_integration_remote_builder_down() -> eyre::Result<()> { - let harness = RollupBoostTestHarnessBuilder::new("test_integration_remote_builder_down") - .build() - .await?; - let mut block_generator = harness.get_block_generator().await?; - - for _ in 0..3 { - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!( - block_creator.is_builder(), - "Block creator should be the builder" - ); - } - - // stop the builder - harness.builder.stop().await?; - - // create 3 new blocks that are processed by the l2 builder - for _ in 0..3 { - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!(block_creator.is_l2(), "Block creator should be l2"); - } - - // start the builder again - harness.builder.start().await?; - - // the next block is computed by the l2 builder because the builder is not synced with the previous 3 blocks - // But, once the builder receives the FCU request from rollup-boost, it will sync up the blocks with the - // L2 block builder and be ready again. - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!(block_creator.is_l2(), "Block creator should be l2"); - - // Note: We might add some sleep here if the builder is not synced in time. I have not seen this happen yet. - - // create 3 new blocks that are processed by the l2 builder because the builder is not synced with the previous 3 blocks - for _ in 0..3 { - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!(block_creator.is_builder(), "Block creator should be l2"); - } - - Ok(()) - } - - #[tokio::test] - async fn test_integration_builder_full_delay() -> eyre::Result<()> { - // Create a dynamic handler that delays all the calls by 2 seconds - let delay = Arc::new(Mutex::new(Duration::from_secs(0))); - - let delay_for_handler = delay.clone(); - let handler = Box::new(move |_method: &str, _params: Value, _result: Value| { - let delay = delay_for_handler.lock().unwrap(); - // sleep the amount of time specified in the delay - std::thread::sleep(*delay); - None - }); - - // This integration test checks that if the builder has a general delay in processing ANY of the requests, - // rollup-boost does not stop building blocks. - let harness = RollupBoostTestHarnessBuilder::new("test_integration_builder_full_delay") - .proxy_handler(handler) - .build() - .await?; - - let mut block_generator = harness.get_block_generator().await?; - - // create 3 blocks that are processed by the builder - for _ in 0..3 { - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!( - block_creator.is_builder(), - "Block creator should be the builder" - ); - } - - // add the delay - *delay.lock().unwrap() = Duration::from_secs(2); - - // create 3 blocks that are processed by the builder - for _ in 0..3 { - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!(block_creator.is_l2(), "Block creator should be the builder"); - } - - Ok(()) - } - - #[tokio::test] - async fn test_integration_builder_returns_incorrect_block() -> eyre::Result<()> { - // Test that the builder returns a block with an incorrect state root and that rollup-boost - // does not process it. - let handler = Box::new(move |method: &str, _params: Value, _result: Value| { - if method != "engine_getPayloadV3" { - return None; - } - - let mut payload = - serde_json::from_value::(_result).unwrap(); - - // modify the state root field - payload - .execution_payload - .payload_inner - .payload_inner - .state_root = B256::ZERO; - - let result = serde_json::to_value(&payload).unwrap(); - Some(result) - }); - - let harness = - RollupBoostTestHarnessBuilder::new("test_integration_builder_returns_incorrect_block") - .proxy_handler(handler) - .build() - .await?; - - let mut block_generator = harness.get_block_generator().await?; - - // create 3 blocks that are processed by the builder - for _ in 0..3 { - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!(block_creator.is_l2(), "Block creator should be the builder"); - } - // check that at some point we had the log "builder payload was not valid" which signals - // that the builder returned a payload that was not valid and rollup-boost did not process it. - // read lines - let logs = std::fs::read_to_string(harness.rollup_boost.args().log_file.clone().unwrap())?; - assert!( - logs.contains("Invalid payload"), - "Logs should contain the message 'builder payload was not valid'" - ); - - Ok(()) - } -} diff --git a/tests/integration/mod.rs b/tests/integration/mod.rs index c7a8cc9d..d29b2e83 100644 --- a/tests/integration/mod.rs +++ b/tests/integration/mod.rs @@ -42,7 +42,6 @@ pub const JWT_SECRET: &str = "688f5d737bad920bdfb2fc2f488d6b6209eebda1dae949a8de pub const L2_P2P_ENODE: &str = "3479db4d9217fb5d7a8ed4d61ac36e120b05d36c2eefb795dc42ff2e971f251a2315f5649ea1833271e020b9adc98d5db9973c7ed92d6b2f1f2223088c3d852f"; mod containers; -mod integration_test; mod proxy; pub struct LoggingConsumer { diff --git a/tests/remote_builder_down.rs b/tests/remote_builder_down.rs new file mode 100644 index 00000000..377266bd --- /dev/null +++ b/tests/remote_builder_down.rs @@ -0,0 +1,54 @@ +use alloy_primitives::B256; +use serde_json::Value; +use std::sync::{Arc, Mutex}; +use std::time::Duration; + +mod integration; + +use crate::integration::RollupBoostTestHarnessBuilder; +// use crate::server::ExecutionMode; +use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; + +#[tokio::test] +async fn test_integration_remote_builder_down() -> eyre::Result<()> { + let harness = RollupBoostTestHarnessBuilder::new("test_integration_remote_builder_down") + .build() + .await?; + let mut block_generator = harness.get_block_generator().await?; + + for _ in 0..3 { + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!( + block_creator.is_builder(), + "Block creator should be the builder" + ); + } + + // stop the builder + harness.builder.stop().await?; + + // create 3 new blocks that are processed by the l2 builder + for _ in 0..3 { + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!(block_creator.is_l2(), "Block creator should be l2"); + } + + // start the builder again + harness.builder.start().await?; + + // the next block is computed by the l2 builder because the builder is not synced with the previous 3 blocks + // But, once the builder receives the FCU request from rollup-boost, it will sync up the blocks with the + // L2 block builder and be ready again. + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!(block_creator.is_l2(), "Block creator should be l2"); + + // Note: We might add some sleep here if the builder is not synced in time. I have not seen this happen yet. + + // create 3 new blocks that are processed by the l2 builder because the builder is not synced with the previous 3 blocks + for _ in 0..3 { + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!(block_creator.is_builder(), "Block creator should be l2"); + } + + Ok(()) +} diff --git a/tests/simple.rs b/tests/simple.rs new file mode 100644 index 00000000..11149f48 --- /dev/null +++ b/tests/simple.rs @@ -0,0 +1,28 @@ +use alloy_primitives::B256; +use integration::RollupBoostTestHarnessBuilder; +use rollup_boost::ExecutionMode; +use serde_json::Value; +use std::sync::{Arc, Mutex}; +use std::time::Duration; + +use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; + +mod integration; + +#[tokio::test] +async fn test_integration_simple() -> eyre::Result<()> { + let harness = RollupBoostTestHarnessBuilder::new("test_integration_simple") + .build() + .await?; + let mut block_generator = harness.get_block_generator().await?; + + for _ in 0..5 { + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!( + block_creator.is_builder(), + "Block creator should be the builder" + ); + } + + Ok(()) +} From 33ce22f6caa12ce76b0a41ebe13d8ccf7f29369d Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Thu, 3 Apr 2025 21:08:53 -0700 Subject: [PATCH 07/20] wip --- Cargo.lock | 1 + Cargo.toml | 1 + src/bin/main.rs | 4 - src/cli.rs | 4 + src/integration/containers/mod.rs | 2 - src/integration/containers/op_reth.rs | 231 ----- src/integration/containers/rollup_boost.rs | 68 -- src/integration/integration_test.rs | 284 ------ src/integration/mod.rs | 514 ---------- src/integration/proxy.rs | 141 --- src/integration/tests/mod.rs | 0 .../tests/test_integration_no_tx_pool.rs | 26 - tests/builder_full_delay.rs | 8 +- tests/builder_returns_incorrect_block.rs | 14 +- tests/execution_mode.rs | 4 +- tests/integration/containers/op_reth.rs | 89 +- tests/integration/containers/rollup_boost.rs | 42 +- tests/integration/mod.rs | 54 +- .../integration/test_data}/genesis.json | 0 .../integration/test_data}/jwt_secret.hex | 0 .../integration/test_data}/p2p_secret.hex | 0 tests/integration/testdata/genesis.json | 896 ------------------ tests/integration/testdata/jwt_secret.hex | 1 - tests/integration/testdata/p2p_secret.hex | 1 - tests/no_tx_pool.rs | 18 +- tests/remote_builder_down.rs | 25 +- tests/simple.rs | 11 +- 27 files changed, 156 insertions(+), 2283 deletions(-) delete mode 100644 src/integration/containers/mod.rs delete mode 100644 src/integration/containers/op_reth.rs delete mode 100644 src/integration/containers/rollup_boost.rs delete mode 100644 src/integration/integration_test.rs delete mode 100644 src/integration/mod.rs delete mode 100644 src/integration/proxy.rs delete mode 100644 src/integration/tests/mod.rs delete mode 100644 src/integration/tests/test_integration_no_tx_pool.rs rename {src/integration/testdata => tests/integration/test_data}/genesis.json (100%) rename {src/integration/testdata => tests/integration/test_data}/jwt_secret.hex (100%) rename {src/integration/testdata => tests/integration/test_data}/p2p_secret.hex (100%) delete mode 100644 tests/integration/testdata/genesis.json delete mode 100644 tests/integration/testdata/jwt_secret.hex delete mode 100644 tests/integration/testdata/p2p_secret.hex diff --git a/Cargo.lock b/Cargo.lock index 72f99995..aa637cd9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3754,6 +3754,7 @@ dependencies = [ "parking_lot", "paste", "predicates", + "rand 0.9.0", "reth-rpc-layer", "rustls", "serde", diff --git a/Cargo.toml b/Cargo.toml index f30a8496..a56d8855 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ parking_lot = "0.12.3" lazy_static = { version = "1.5.0" } [dev-dependencies] +rand = "0.9.0" time = { version = "0.3.36", features = ["macros", "formatting", "parsing"] } op-alloy-consensus = "0.12.0" alloy-rpc-types-eth = "0.13.0" diff --git a/src/bin/main.rs b/src/bin/main.rs index e332e3d4..0208fb03 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -6,9 +6,5 @@ use dotenv::dotenv; #[tokio::main] async fn main() -> eyre::Result<()> { dotenv().ok(); - rustls::crypto::ring::default_provider() - .install_default() - .expect("Failed to install TLS ring CryptoProvider"); - Args::parse().run().await } diff --git a/src/cli.rs b/src/cli.rs index 52001ccc..54291fc1 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -85,6 +85,10 @@ pub struct Args { impl Args { pub async fn run(self) -> eyre::Result<()> { + rustls::crypto::ring::default_provider() + .install_default() + .expect("Failed to install TLS ring CryptoProvider"); + let debug_addr = format!("{}:{}", self.debug_host, self.debug_server_port); // Handle commands if present diff --git a/src/integration/containers/mod.rs b/src/integration/containers/mod.rs deleted file mode 100644 index 41ef9049..00000000 --- a/src/integration/containers/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod op_reth; -pub mod rollup_boost; diff --git a/src/integration/containers/op_reth.rs b/src/integration/containers/op_reth.rs deleted file mode 100644 index 69943b48..00000000 --- a/src/integration/containers/op_reth.rs +++ /dev/null @@ -1,231 +0,0 @@ -use std::{ - borrow::Cow, - collections::HashMap, - fs::File, - io::BufReader, - path::PathBuf, - time::{SystemTime, UNIX_EPOCH}, -}; - -use http::Uri; -use serde_json::Value; -use testcontainers::{ - ContainerAsync, CopyToContainer, Image, - core::{ContainerPort, WaitFor}, -}; - -use crate::integration::L2_P2P_ENODE; - -const NAME: &str = "ghcr.io/paradigmxyz/op-reth"; -const TAG: &str = "v1.3.4"; - -const AUTH_RPC_PORT: u16 = 8551; -const P2P_PORT: u16 = 30303; - -#[derive(Debug, Clone)] -pub struct OpRethConfig { - chain: PathBuf, - jwt_secret: PathBuf, - p2p_secret: Option, - pub trusted_peers: Vec, - pub datadir: String, - pub disable_discovery: bool, - pub color: String, - pub ipcdisable: bool, - pub env_vars: HashMap, -} - -impl Default for OpRethConfig { - fn default() -> Self { - Self { - chain: PathBuf::from(format!( - "{}/src/integration/testdata/genesis.json", - env!("CARGO_MANIFEST_DIR") - )), - jwt_secret: PathBuf::from(format!( - "{}/src/integration/testdata/jwt_secret.hex", - env!("CARGO_MANIFEST_DIR") - )), - p2p_secret: None, - trusted_peers: vec![], - datadir: "data".to_string(), - disable_discovery: true, - color: "never".to_string(), - ipcdisable: true, - env_vars: Default::default(), - } - } -} - -impl OpRethConfig { - pub fn set_trusted_peers(mut self, trusted_peers: Vec) -> Self { - self.trusted_peers = trusted_peers; - self - } - - pub fn set_jwt_secret(mut self, jwt_secret: PathBuf) -> Self { - self.jwt_secret = jwt_secret; - self - } - - pub fn set_p2p_secret(mut self, p2p_secret: Option) -> Self { - self.p2p_secret = p2p_secret; - self - } - - pub fn build(self) -> OpRethImage { - // Write the genesis file to the test directory and update the timestamp to the current time - let genesis = { - let file = File::open(&self.chain).unwrap(); - let reader = BufReader::new(file); - let mut genesis: Value = serde_json::from_reader(reader).unwrap(); - - // Update the timestamp field - let timestamp = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_secs(); - - if let Some(config) = genesis.as_object_mut() { - // Assuming timestamp is at the root level - adjust path as needed - config["timestamp"] = Value::String(format!("0x{:x}", timestamp)); - } - - serde_json::to_string_pretty(&genesis).unwrap() - }; - - let mut copy_to_sources = vec![ - CopyToContainer::new( - // std::fs::read(&self.jwt_secret).unwrap(), - "688f5d737bad920bdfb2fc2f488d6b6209eebda1dae949a8de91398d932c517a" - .to_string() - .into_bytes(), - "/jwt_secret.hex".to_string(), - ), - CopyToContainer::new(genesis.into_bytes(), "/genesis.json".to_string()), - ]; - - if let Some(p2p_secret) = &self.p2p_secret { - copy_to_sources.push(CopyToContainer::new( - // std::fs::read(p2p_secret).unwrap(), - "a11ac89899cd86e36b6fb881ec1255b8a92a688790b7d950f8b7d8dd626671fb" - .to_string() - .into_bytes(), - "/p2p_secret.hex".to_string(), - )); - } - - let expose_ports = vec![ContainerPort::Tcp(8551), ContainerPort::Tcp(30303)]; - - OpRethImage { - config: self, - copy_to_sources, - expose_ports, - } - } -} - -impl OpRethImage { - pub fn config(&self) -> &OpRethConfig { - &self.config - } -} - -#[derive(Debug, Clone)] -pub struct OpRethImage { - config: OpRethConfig, - copy_to_sources: Vec, - expose_ports: Vec, -} - -impl Image for OpRethImage { - fn name(&self) -> &str { - NAME - } - - fn tag(&self) -> &str { - TAG - } - - fn ready_conditions(&self) -> Vec { - vec![WaitFor::message_on_stdout("Starting consensus")] - } - - fn env_vars( - &self, - ) -> impl IntoIterator>, impl Into>)> { - &self.config.env_vars - } - - fn copy_to_sources(&self) -> impl IntoIterator { - self.copy_to_sources.iter() - } - - fn cmd(&self) -> impl IntoIterator>> { - let mut cmd = vec![ - "node".to_string(), - "--http".to_string(), - "--http.addr=0.0.0.0".to_string(), - "--authrpc.port=8551".to_string(), - "--authrpc.addr=0.0.0.0".to_string(), - "--authrpc.jwtsecret=/jwt_secret.hex".to_string(), - "--chain=/genesis.json".to_string(), - "--datadir".to_string(), - self.config.datadir.clone(), - "--port=30303".to_string(), - "--color".to_string(), - self.config.color.clone(), - ]; - if self.config.p2p_secret.is_some() { - cmd.push("--p2p-secret-key=/p2p_secret.hex".to_string()); - } - if !self.config.trusted_peers.is_empty() { - cmd.extend([ - "--trusted-peers".to_string(), - self.config.trusted_peers.join(","), - ]); - } - if self.config.disable_discovery { - cmd.push("--disable-discovery".to_string()); - } - if self.config.ipcdisable { - cmd.push("--ipcdisable".to_string()); - } - cmd - } - - fn expose_ports(&self) -> &[ContainerPort] { - &self.expose_ports - } -} - -pub trait OpRethMehods { - async fn auth_rpc(&self) -> eyre::Result; - async fn auth_rpc_port(&self) -> eyre::Result; - async fn enode(&self) -> eyre::Result; -} - -impl OpRethMehods for ContainerAsync { - async fn auth_rpc_port(&self) -> eyre::Result { - Ok(self.get_host_port_ipv4(AUTH_RPC_PORT).await?) - } - - async fn auth_rpc(&self) -> eyre::Result { - Ok(format!( - "http://{}:{}", - self.get_host().await?, - self.get_host_port_ipv4(AUTH_RPC_PORT).await? - ) - .parse()?) - } - - async fn enode(&self) -> eyre::Result { - Ok(format!( - "enode://{}@{}:{}", - L2_P2P_ENODE, - self.get_host().await?, - self.get_host_port_ipv4(P2P_PORT).await? - ) - .parse()?) - } -} diff --git a/src/integration/containers/rollup_boost.rs b/src/integration/containers/rollup_boost.rs deleted file mode 100644 index b8327045..00000000 --- a/src/integration/containers/rollup_boost.rs +++ /dev/null @@ -1,68 +0,0 @@ -use clap::Parser; -use tokio::task::JoinHandle; - -use crate::Args; - -#[derive(Debug)] -pub struct RollupBoost { - args: Args, - pub handle: JoinHandle>, -} - -impl RollupBoost { - pub fn args(&self) -> &Args { - &self.args - } - - pub fn rpc_endpoint(&self) -> String { - format!("http://localhost:{}", self.args.rpc_port) - } - - pub fn debug_endpoint(&self) -> String { - format!("http://localhost:{}", self.args.debug_server_port) - } -} - -#[derive(Clone, Debug)] -pub struct RollupBoostConfig { - pub args: Args, -} - -impl Default for RollupBoostConfig { - fn default() -> Self { - Self { - args: Args::parse_from([ - "rollup-boost", - &format!( - "--l2-jwt-path={}/src/integration/testdata/jwt_secret.hex", - env!("CARGO_MANIFEST_DIR") - ), - &format!( - "--builder-jwt-path={}/src/integration/testdata/jwt_secret.hex", - env!("CARGO_MANIFEST_DIR") - ), - "--log-level=trace", - "--tracing", - "--metrics", - ]), - } - } -} - -impl RollupBoostConfig { - pub fn start(self) -> RollupBoost { - let args = self.args.clone(); - let handle = tokio::spawn(async move { - let res = args.clone().run().await; - if let Err(e) = &res { - eprintln!("Error: {:?}", e); - } - res - }); - - RollupBoost { - args: self.args, - handle, - } - } -} diff --git a/src/integration/integration_test.rs b/src/integration/integration_test.rs deleted file mode 100644 index 0d7078d9..00000000 --- a/src/integration/integration_test.rs +++ /dev/null @@ -1,284 +0,0 @@ -#[cfg(test)] -mod tests { - use alloy_primitives::B256; - use serde_json::Value; - use std::sync::{Arc, Mutex}; - use std::time::Duration; - - use crate::integration::RollupBoostTestHarnessBuilder; - use crate::server::ExecutionMode; - use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; - - #[tokio::test] - async fn test_integration_simple() -> eyre::Result<()> { - let harness = RollupBoostTestHarnessBuilder::new("test_integration_simple") - .build() - .await?; - let mut block_generator = harness.get_block_generator().await?; - - for _ in 0..5 { - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!( - block_creator.is_builder(), - "Block creator should be the builder" - ); - } - - Ok(()) - } - - #[tokio::test] - async fn test_integration_no_tx_pool() -> eyre::Result<()> { - let harness = RollupBoostTestHarnessBuilder::new("test_integration_no_tx_pool") - .build() - .await?; - let mut block_generator = harness.get_block_generator().await?; - - // start creating 5 empty blocks which are processed by the L2 builder - for _ in 0..5 { - let (_block, block_creator) = block_generator.generate_block(true).await?; - assert!(block_creator.is_l2(), "Block creator should be l2"); - } - - // process 5 more non empty blocks which are processed by the builder. - // The builder should be on sync because it has received the new payload requests from rollup-boost. - for _ in 0..5 { - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!( - block_creator.is_builder(), - "Block creator should be the builder" - ); - } - - Ok(()) - } - - #[tokio::test] - async fn test_integration_execution_mode() -> eyre::Result<()> { - // Create a counter that increases whenever we receive a new RPC call in the builder - let counter = Arc::new(Mutex::new(0)); - - let counter_for_handler = counter.clone(); - let handler = Box::new(move |_method: &str, _params: Value, _result: Value| { - let mut counter = counter_for_handler.lock().unwrap(); - - *counter += 1; - None - }); - - let harness = RollupBoostTestHarnessBuilder::new("test_integration_dry_run") - .proxy_handler(handler) - .build() - .await?; - let mut block_generator = harness.get_block_generator().await?; - - // start creating 5 empty blocks which are processed by the builder - for _ in 0..5 { - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!( - block_creator.is_builder(), - "Block creator should be the builder" - ); - } - - let client = harness.get_client().await; - - // enable dry run mode - { - let response = client - .set_execution_mode(ExecutionMode::DryRun) - .await - .unwrap(); - assert_eq!(response.execution_mode, ExecutionMode::DryRun); - - // the new valid block should be created the the l2 builder - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!(block_creator.is_l2(), "Block creator should be l2"); - } - - // toggle again dry run mode - { - let response = client - .set_execution_mode(ExecutionMode::Enabled) - .await - .unwrap(); - assert_eq!(response.execution_mode, ExecutionMode::Enabled); - - // the new valid block should be created the the builder - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!( - block_creator.is_builder(), - "Block creator should be the builder" - ); - } - - // sleep for 1 second so that it has time to send the last FCU request to the builder - // and there is not a race condition with the disable call - std::thread::sleep(Duration::from_secs(1)); - - tracing::info!("Setting execution mode to disabled"); - - // Set the execution mode to disabled and reset the counter in the proxy to 0 - // to track the number of calls to the builder during the disabled mode which - // should be 0 - { - let response = client - .set_execution_mode(ExecutionMode::Disabled) - .await - .unwrap(); - assert_eq!(response.execution_mode, ExecutionMode::Disabled); - - // reset the counter in the proxy - *counter.lock().unwrap() = 0; - - // create 5 blocks which are processed by the l2 clients - for _ in 0..5 { - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!(block_creator.is_l2(), "Block creator should be l2"); - } - - assert_eq!( - *counter.lock().unwrap(), - 0, - "Number of calls to the builder should be 0", - ); - } - - Ok(()) - } - - #[tokio::test] - async fn test_integration_remote_builder_down() -> eyre::Result<()> { - let harness = RollupBoostTestHarnessBuilder::new("test_integration_remote_builder_down") - .build() - .await?; - let mut block_generator = harness.get_block_generator().await?; - - for _ in 0..3 { - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!( - block_creator.is_builder(), - "Block creator should be the builder" - ); - } - - // stop the builder - harness.builder.stop().await?; - - // create 3 new blocks that are processed by the l2 builder - for _ in 0..3 { - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!(block_creator.is_l2(), "Block creator should be l2"); - } - - // start the builder again - harness.builder.start().await?; - - // the next block is computed by the l2 builder because the builder is not synced with the previous 3 blocks - // But, once the builder receives the FCU request from rollup-boost, it will sync up the blocks with the - // L2 block builder and be ready again. - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!(block_creator.is_l2(), "Block creator should be l2"); - - // Note: We might add some sleep here if the builder is not synced in time. I have not seen this happen yet. - - // create 3 new blocks that are processed by the l2 builder because the builder is not synced with the previous 3 blocks - for _ in 0..3 { - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!(block_creator.is_builder(), "Block creator should be l2"); - } - - Ok(()) - } - - #[tokio::test] - async fn test_integration_builder_full_delay() -> eyre::Result<()> { - // Create a dynamic handler that delays all the calls by 2 seconds - let delay = Arc::new(Mutex::new(Duration::from_secs(0))); - - let delay_for_handler = delay.clone(); - let handler = Box::new(move |_method: &str, _params: Value, _result: Value| { - let delay = delay_for_handler.lock().unwrap(); - // sleep the amount of time specified in the delay - std::thread::sleep(*delay); - None - }); - - // This integration test checks that if the builder has a general delay in processing ANY of the requests, - // rollup-boost does not stop building blocks. - let harness = RollupBoostTestHarnessBuilder::new("test_integration_builder_full_delay") - .proxy_handler(handler) - .build() - .await?; - - let mut block_generator = harness.get_block_generator().await?; - - // create 3 blocks that are processed by the builder - for _ in 0..3 { - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!( - block_creator.is_builder(), - "Block creator should be the builder" - ); - } - - // add the delay - *delay.lock().unwrap() = Duration::from_secs(2); - - // create 3 blocks that are processed by the builder - for _ in 0..3 { - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!(block_creator.is_l2(), "Block creator should be the builder"); - } - - Ok(()) - } - - #[tokio::test] - async fn test_integration_builder_returns_incorrect_block() -> eyre::Result<()> { - // Test that the builder returns a block with an incorrect state root and that rollup-boost - // does not process it. - let handler = Box::new(move |method: &str, _params: Value, _result: Value| { - if method != "engine_getPayloadV3" { - return None; - } - - let mut payload = - serde_json::from_value::(_result).unwrap(); - - // modify the state root field - payload - .execution_payload - .payload_inner - .payload_inner - .state_root = B256::ZERO; - - let result = serde_json::to_value(&payload).unwrap(); - Some(result) - }); - - let harness = - RollupBoostTestHarnessBuilder::new("test_integration_builder_returns_incorrect_block") - .proxy_handler(handler) - .build() - .await?; - - let mut block_generator = harness.get_block_generator().await?; - - // create 3 blocks that are processed by the builder - for _ in 0..3 { - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!(block_creator.is_l2(), "Block creator should be the builder"); - } - // check that at some point we had the log "builder payload was not valid" which signals - // that the builder returned a payload that was not valid and rollup-boost did not process it. - // read lines - let logs = std::fs::read_to_string(harness.rollup_boost.args().log_file.clone().unwrap())?; - assert!( - logs.contains("Invalid payload"), - "Logs should contain the message 'builder payload was not valid'" - ); - - Ok(()) - } -} diff --git a/src/integration/mod.rs b/src/integration/mod.rs deleted file mode 100644 index 63bcfa40..00000000 --- a/src/integration/mod.rs +++ /dev/null @@ -1,514 +0,0 @@ -use crate::client::auth::{AuthClientLayer, AuthClientService}; -use crate::debug_api::DebugClient; -use crate::server::{EngineApiClient, OpExecutionPayloadEnvelope, Version}; -use crate::server::{NewPayload, PayloadSource}; -use alloy_eips::BlockNumberOrTag; -use alloy_eips::eip2718::Encodable2718; -use alloy_primitives::{B256, Bytes, TxKind, U256, address, hex}; -use alloy_rpc_types_engine::{ExecutionPayload, JwtSecret}; -use alloy_rpc_types_engine::{ - ForkchoiceState, ForkchoiceUpdated, PayloadAttributes, PayloadId, PayloadStatus, - PayloadStatusEnum, -}; -use containers::op_reth::{OpRethConfig, OpRethImage, OpRethMehods}; -use containers::rollup_boost::{RollupBoost, RollupBoostConfig}; -use eyre::bail; -use futures::FutureExt; -use futures::future::BoxFuture; -use http::Uri; -use jsonrpsee::http_client::{HttpClient, transport::HttpBackend}; -use jsonrpsee::proc_macros::rpc; -use op_alloy_rpc_types_engine::{OpExecutionPayloadEnvelopeV3, OpPayloadAttributes}; -use proxy::{DynHandlerFn, start_proxy_server}; -use std::net::TcpListener; -use std::path::PathBuf; -use std::str::FromStr; -use std::time::{Duration, SystemTime}; -use testcontainers::core::ContainerPort; -use testcontainers::core::logs::LogFrame; -use testcontainers::core::logs::consumer::LogConsumer; -use testcontainers::runners::AsyncRunner; -use testcontainers::{ContainerAsync, Image, ImageExt}; -use tokio::io::{AsyncBufReadExt, AsyncWriteExt as _}; -use tracing::info; - -use time::{OffsetDateTime, format_description}; - -/// Default JWT token for testing purposes -pub const JWT_SECRET: &str = "688f5d737bad920bdfb2fc2f488d6b6209eebda1dae949a8de91398d932c517a"; - -pub const L2_P2P_ENODE: &str = "3479db4d9217fb5d7a8ed4d61ac36e120b05d36c2eefb795dc42ff2e971f251a2315f5649ea1833271e020b9adc98d5db9973c7ed92d6b2f1f2223088c3d852f"; - -mod containers; -mod integration_test; -mod proxy; -mod tests; - -pub struct LoggingConsumer { - target: String, - log_file: tokio::sync::Mutex, -} - -impl LogConsumer for LoggingConsumer { - fn accept<'a>(&'a self, record: &'a LogFrame) -> BoxFuture<'a, ()> { - async move { - match record { - testcontainers::core::logs::LogFrame::StdOut(bytes) => { - info!(target = self.target, "{}", String::from_utf8_lossy(bytes)); - self.log_file.lock().await.write_all(bytes).await.unwrap(); - } - testcontainers::core::logs::LogFrame::StdErr(bytes) => { - info!(target = self.target, "{}", String::from_utf8_lossy(bytes)); - self.log_file.lock().await.write_all(bytes).await.unwrap(); - } - } - } - .boxed() - } -} - -pub async fn wait_for_log( - container: ContainerAsync, - pattern: &str, - timeout: Duration, -) -> eyre::Result<()> { - let timeout = tokio::time::sleep(timeout); - let mut stderr = container.stderr(true).lines(); - let mut stdout = container.stdout(true).lines(); - - tokio::select! { - result = async { - loop { - tokio::select! { - line = stderr.next_line() => { - if let Ok(Some(line)) = line { - if line.contains(pattern) { - return Ok::<_, eyre::Report>(()); - } - } - } - line = stdout.next_line() => { - if let Ok(Some(line)) = line { - if line.contains(pattern) { - return Ok::<_, eyre::Report>(()); - } - } - } - } - } - } => result, - _ = timeout => { - bail!("Timeout waiting for log message: {}", pattern); - } - } -} - -pub struct EngineApi { - pub engine_api_client: HttpClient>, -} - -// TODO: Use client/rpc.rs instead -impl EngineApi { - pub fn new(url: &str, secret: &str) -> eyre::Result { - let secret_layer = AuthClientLayer::new(JwtSecret::from_str(secret)?); - let middleware = tower::ServiceBuilder::default().layer(secret_layer); - let client = jsonrpsee::http_client::HttpClientBuilder::default() - .set_http_middleware(middleware) - .build(url) - .expect("Failed to create http client"); - - Ok(Self { - engine_api_client: client, - }) - } - - pub async fn get_payload( - &self, - version: Version, - payload_id: PayloadId, - ) -> eyre::Result { - match version { - Version::V3 => Ok(OpExecutionPayloadEnvelope::V3( - EngineApiClient::get_payload_v3(&self.engine_api_client, payload_id).await?, - )), - Version::V4 => Ok(OpExecutionPayloadEnvelope::V4( - EngineApiClient::get_payload_v4(&self.engine_api_client, payload_id).await?, - )), - } - } - - pub async fn new_payload(&self, payload: NewPayload) -> eyre::Result { - match payload { - NewPayload::V3(new_payload) => Ok(EngineApiClient::new_payload_v3( - &self.engine_api_client, - new_payload.payload, - new_payload.versioned_hashes, - new_payload.parent_beacon_block_root, - ) - .await?), - NewPayload::V4(new_payload) => Ok(EngineApiClient::new_payload_v4( - &self.engine_api_client, - new_payload.payload, - new_payload.versioned_hashes, - new_payload.parent_beacon_block_root, - new_payload.execution_requests, - ) - .await?), - } - } - - pub async fn update_forkchoice( - &self, - current_head: B256, - new_head: B256, - payload_attributes: Option, - ) -> eyre::Result { - Ok(EngineApiClient::fork_choice_updated_v3( - &self.engine_api_client, - ForkchoiceState { - head_block_hash: new_head, - safe_block_hash: current_head, - finalized_block_hash: current_head, - }, - payload_attributes, - ) - .await?) - } - - pub async fn latest(&self) -> eyre::Result> { - Ok(BlockApiClient::get_block_by_number( - &self.engine_api_client, - BlockNumberOrTag::Latest, - false, - ) - .await?) - } -} - -#[rpc(client, namespace = "eth")] -pub trait BlockApi { - #[method(name = "getBlockByNumber")] - async fn get_block_by_number( - &self, - block_number: BlockNumberOrTag, - include_txs: bool, - ) -> RpcResult>; -} - -/// Test flavor that sets up one Rollup-boost instance connected to two Reth nodes -pub struct RollupBoostTestHarness { - pub l2: ContainerAsync, - pub builder: ContainerAsync, - pub rollup_boost: RollupBoost, -} - -pub struct RollupBoostTestHarnessBuilder { - test_name: String, - proxy_handler: Option, -} - -impl RollupBoostTestHarnessBuilder { - pub fn new(test_name: &str) -> Self { - Self { - test_name: test_name.to_string(), - proxy_handler: None, - } - } - - pub fn file_path(&self, service_name: &str) -> eyre::Result { - let dt: OffsetDateTime = SystemTime::now().into(); - let format = format_description::parse("[year]_[month]_[day]_[hour]_[minute]_[second]")?; - let timestamp = dt.format(&format)?; - - // let test_name = format!("{}_{}", timestamp, test_name); - - let dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("test_logs") - .join(timestamp) - .join(self.test_name.clone()); - std::fs::create_dir_all(&dir)?; - - let file_name = format!("{service_name}.log"); - Ok(dir.join(file_name)) - } - - pub async fn async_log_file(&self, service_name: &str) -> eyre::Result { - let file_path = self.file_path(service_name)?; - Ok(tokio::fs::OpenOptions::new() - .append(true) - .create(true) - .open(file_path) - .await?) - } - - pub fn log_file(&self, service_name: &str) -> eyre::Result { - let file_path = self.file_path(service_name)?; - Ok(std::fs::OpenOptions::new() - .append(true) - .create(true) - .open(file_path)?) - } - - pub async fn log_consumer(&self, service_name: &str) -> eyre::Result { - let file = self.async_log_file(service_name).await?; - Ok(LoggingConsumer { - target: service_name.to_string(), - log_file: tokio::sync::Mutex::new(file), - }) - } - - pub fn proxy_handler(mut self, proxy_handler: DynHandlerFn) -> Self { - self.proxy_handler = Some(proxy_handler); - self - } - - async fn build(self) -> eyre::Result { - let l2_log_consumer = self.log_consumer("l2").await?; - let builder_log_consumer = self.log_consumer("builder").await?; - let rollup_boost_log_file_path = self.file_path("rollup_boost")?; - - let l2 = OpRethConfig::default() - .set_p2p_secret(Some(PathBuf::from(format!( - "{}/src/integration/testdata/p2p_secret.hex", - env!("CARGO_MANIFEST_DIR") - )))) - .build() - .with_log_consumer(l2_log_consumer) - .start() - .await?; - - let builder = OpRethConfig::default() - .set_trusted_peers(vec![l2.enode().await?.to_string()]) - .build() - .with_log_consumer(builder_log_consumer) - .start() - .await?; - - println!("l2 authrpc: {}", l2.auth_rpc().await?); - println!("builder authrpc: {}", builder.auth_rpc().await?); - - // run a proxy in between the builder and the rollup-boost if the proxy_handler is set - let mut builder_authrpc_port = builder.auth_rpc_port().await?; - if let Some(proxy_handler) = self.proxy_handler { - let proxy_port = get_available_port().expect("no available port"); - let _ = start_proxy_server(proxy_handler, proxy_port, builder_authrpc_port).await; - builder_authrpc_port = proxy_port - }; - let builder_url = format!("http://127.0.0.1:{}", builder_authrpc_port); - - // Start Rollup-boost instance - let mut rollup_boost = RollupBoostConfig::default(); - rollup_boost.args.l2_client.l2_url = l2.auth_rpc().await?.try_into().unwrap(); - rollup_boost.args.builder.builder_url = builder_url.try_into().unwrap(); - rollup_boost.args.log_file = Some(rollup_boost_log_file_path); - let rollup_boost = rollup_boost.start(); - - Ok(RollupBoostTestHarness { - l2, - builder, - rollup_boost, - }) - } -} - -impl RollupBoostTestHarness { - pub async fn get_block_generator(&self) -> eyre::Result { - let validator = - BlockBuilderCreatorValidator::new(self.rollup_boost.args().log_file.clone().unwrap()); - - let engine_api = EngineApi::new(&self.rollup_boost.rpc_endpoint(), JWT_SECRET)?; - - let mut block_creator = SimpleBlockGenerator::new(validator, engine_api); - block_creator.init().await?; - Ok(block_creator) - } - - pub async fn get_client(&self) -> DebugClient { - DebugClient::new(&self.rollup_boost.debug_endpoint()).unwrap() - } -} - -/// A simple system that continuously generates empty blocks using the engine API -pub struct SimpleBlockGenerator { - validator: BlockBuilderCreatorValidator, - engine_api: EngineApi, - latest_hash: B256, - timestamp: u64, - version: Version, -} - -impl SimpleBlockGenerator { - pub fn new(validator: BlockBuilderCreatorValidator, engine_api: EngineApi) -> Self { - Self { - validator, - engine_api, - latest_hash: B256::ZERO, // temporary value - timestamp: 0, // temporary value - version: Version::V3, - } - } - - /// Initialize the block generator by fetching the latest block - pub async fn init(&mut self) -> eyre::Result<()> { - let latest_block = self.engine_api.latest().await?.expect("block not found"); - self.latest_hash = latest_block.header.hash; - self.timestamp = latest_block.header.timestamp; - Ok(()) - } - - /// Generate a single new block and return its hash - pub async fn generate_block( - &mut self, - empty_blocks: bool, - ) -> eyre::Result<(B256, PayloadSource)> { - let txns = match self.version { - Version::V4 => { - let tx = create_deposit_tx(); - Some(vec![tx]) - } - _ => None, - }; - - // Submit forkchoice update with payload attributes for the next block - let result = self - .engine_api - .update_forkchoice( - self.latest_hash, - self.latest_hash, - Some(OpPayloadAttributes { - payload_attributes: PayloadAttributes { - withdrawals: Some(vec![]), - parent_beacon_block_root: Some(B256::ZERO), - timestamp: self.timestamp + 1000, // 1 second later - prev_randao: B256::ZERO, - suggested_fee_recipient: Default::default(), - }, - transactions: txns, - no_tx_pool: Some(empty_blocks), - gas_limit: Some(10000000000), - eip_1559_params: None, - }), - ) - .await?; - - let payload_id = result.payload_id.expect("missing payload id"); - - if !empty_blocks { - tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; - } - - let payload = self - .engine_api - .get_payload(self.version, payload_id) - .await?; - - // Submit the new payload to the node - let validation_status = self - .engine_api - .new_payload(NewPayload::from(payload.clone())) - .await?; - - if validation_status.status != PayloadStatusEnum::Valid { - return Err(eyre::eyre!("Invalid payload status")); - } - - let execution_payload = ExecutionPayload::from(payload); - let new_block_hash = execution_payload.block_hash(); - - // Update the chain's head - self.engine_api - .update_forkchoice(self.latest_hash, new_block_hash, None) - .await?; - - // Update internal state - self.latest_hash = new_block_hash; - self.timestamp = execution_payload.timestamp(); - - // Check who built the block in the rollup-boost logs - let block_creator = self - .validator - .get_block_creator(new_block_hash) - .await? - .expect("block creator not found"); - - Ok((new_block_hash, block_creator)) - } -} - -pub struct BlockBuilderCreatorValidator { - file: PathBuf, -} - -impl<'a> BlockBuilderCreatorValidator { - pub fn new(file: PathBuf) -> Self { - Self { file } - } -} - -impl<'a> BlockBuilderCreatorValidator { - pub async fn get_block_creator(&self, block_hash: B256) -> eyre::Result> { - let contents = std::fs::read_to_string(&self.file)?; - - let search_query = format!("returning block hash={:#x}", block_hash); - - // Find the log line containing the block hash - for line in contents.lines() { - if line.contains(&search_query) { - // Extract the context=X part - if let Some(context_start) = line.find("context=") { - let context = line[context_start..] - .split_whitespace() - .next() - .ok_or(eyre::eyre!("no context found"))? - .split('=') - .nth(1) - .ok_or(eyre::eyre!("no context found"))?; - - match context { - "builder" => return Ok(Some(PayloadSource::Builder)), - "l2" => return Ok(Some(PayloadSource::L2)), - _ => panic!("Unknown context: {}", context), - } - } else { - panic!("no context found"); - } - } - } - - Ok(None) - } -} - -fn create_deposit_tx() -> Bytes { - const ISTHMUS_DATA: &[u8] = &hex!( - "098999be00000558000c5fc500000000000000030000000067a9f765000000000000002900000000000000000000000000000000000000000000000000000000006a6d09000000000000000000000000000000000000000000000000000000000000000172fcc8e8886636bdbe96ba0e4baab67ea7e7811633f52b52e8cf7a5123213b6f000000000000000000000000d3f2c5afb2d76f5579f326b0cd7da5f5a4126c3500004e2000000000000001f4" - ); - - let deposit_tx = TxDeposit { - source_hash: B256::default(), - from: address!("DeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001"), - to: TxKind::Call(address!("4200000000000000000000000000000000000015")), - mint: None, - value: U256::default(), - gas_limit: 210000, - is_system_transaction: true, - input: ISTHMUS_DATA.into(), - }; - - let mut buffer_without_header = BytesMut::new(); - deposit_tx.encode_2718(&mut buffer_without_header); - - buffer_without_header.to_vec().into() -} - -fn local_host(port: u16) -> Uri { - format!("http://localhost:{port}").parse::().unwrap() -} - -fn get_available_port() -> Option { - (8000..9000).find(|port| port_is_available(*port)) -} - -fn port_is_available(port: u16) -> bool { - TcpListener::bind(("127.0.0.1", port)).is_ok() -} diff --git a/src/integration/proxy.rs b/src/integration/proxy.rs deleted file mode 100644 index cf3cce5f..00000000 --- a/src/integration/proxy.rs +++ /dev/null @@ -1,141 +0,0 @@ -use bytes::Bytes; -use http::header; -use http_body_util::{BodyExt, Full, combinators::BoxBody}; -use hyper::client::conn::http1::Builder; -use hyper::server::conn::http1; -use hyper::service::service_fn; -use hyper::{Request, Response}; -use hyper_util::rt::TokioIo; -use serde::{Deserialize, Serialize}; -use serde_json::Value; -use std::net::SocketAddr; -use std::sync::Arc; -use tokio::net::{TcpListener, TcpStream}; - -#[derive(Debug, Serialize, Deserialize, Clone)] -struct JsonRpcRequest { - jsonrpc: String, - method: String, - params: Value, - id: Value, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -struct JsonRpcResponse { - jsonrpc: String, - #[serde(default)] - result: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - error: Option, - id: Value, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct JsonRpcError { - code: i32, - message: String, - data: Option, -} - -pub type DynHandlerFn = Box Option + Send + Sync>; - -// Structure to hold the target address that we'll pass to the proxy function -#[derive(Clone)] -struct ProxyConfig { - target_addr: SocketAddr, - handler: Arc, -} - -async fn proxy( - config: ProxyConfig, - req: Request, -) -> Result>, Box> { - let (parts, body) = req.into_parts(); - let bytes = body.collect().await?.to_bytes(); - - let json_rpc_request = serde_json::from_slice::(&bytes).unwrap(); - let req = Request::from_parts(parts, Full::new(bytes)); - - let stream = TcpStream::connect(config.target_addr).await?; - let io = TokioIo::new(stream); - - let (mut sender, conn) = Builder::new() - .preserve_header_case(true) - .title_case_headers(true) - .handshake(io) - .await?; - - tokio::task::spawn(async move { - if let Err(err) = conn.await { - println!("Connection failed: {:?}", err); - } - }); - - let resp = sender.send_request(req).await?; - - let (parts, body) = resp.into_parts(); - let bytes = body.collect().await?.to_bytes(); - - let json_rpc_response = serde_json::from_slice::(&bytes).unwrap(); - let bytes = if let Some(result) = json_rpc_response.clone().result { - let value = (config.handler)(&json_rpc_request.method, json_rpc_request.params, result); - if let Some(value) = value { - // If the handler returns a value, we replace the result with the new value - // The callback only returns the result of the jsonrpc request so we have to wrap it up - // again in a JsonRpcResponse - let mut new_json_rpc_resp = json_rpc_response; - new_json_rpc_resp.result = Some(value); - Bytes::from(serde_json::to_vec(&new_json_rpc_resp).unwrap()) - } else { - // If the handler returns None, we return the original response - bytes - } - } else { - bytes - }; - - let bytes_len = bytes.len(); - let mut resp = Response::from_parts(parts, Full::new(bytes).map_err(|_| unreachable!())); - - // We have to update the content length to the new bytes length - resp.headers_mut() - .insert(header::CONTENT_LENGTH, bytes_len.into()); - - Ok(resp.map(|b| b.boxed())) -} - -pub async fn start_proxy_server( - handler: DynHandlerFn, - listen_port: u16, - target_port: u16, -) -> Result<(), Box> { - let listen_addr = SocketAddr::from(([127, 0, 0, 1], listen_port)); - let target_addr = SocketAddr::from(([127, 0, 0, 1], target_port)); - - let config = ProxyConfig { - target_addr, - handler: Arc::new(handler), - }; - let listener = TcpListener::bind(listen_addr).await?; - - tokio::spawn(async move { - loop { - let (stream, _) = listener.accept().await.unwrap(); - let io = TokioIo::new(stream); - let config = config.clone(); - - tokio::task::spawn(async move { - if let Err(err) = http1::Builder::new() - .preserve_header_case(true) - .title_case_headers(true) - .serve_connection(io, service_fn(move |req| proxy(config.clone(), req))) - .await - { - println!("Failed to serve connection: {:?}", err); - } - }); - } - }); - - Ok(()) -} diff --git a/src/integration/tests/mod.rs b/src/integration/tests/mod.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/src/integration/tests/test_integration_no_tx_pool.rs b/src/integration/tests/test_integration_no_tx_pool.rs deleted file mode 100644 index 09a1d912..00000000 --- a/src/integration/tests/test_integration_no_tx_pool.rs +++ /dev/null @@ -1,26 +0,0 @@ -use alloy_primitives::B256; -use serde_json::Value; -use std::sync::{Arc, Mutex}; -use std::time::Duration; - -use crate::integration::RollupBoostTestHarnessBuilder; -use crate::server::ExecutionMode; -use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; - -#[tokio::test] -async fn test_integration_simple() -> eyre::Result<()> { - let harness = RollupBoostTestHarnessBuilder::new("test_integration_simple") - .build() - .await?; - let mut block_generator = harness.get_block_generator().await?; - - for _ in 0..5 { - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!( - block_creator.is_builder(), - "Block creator should be the builder" - ); - } - - Ok(()) -} diff --git a/tests/builder_full_delay.rs b/tests/builder_full_delay.rs index b51f7cae..7ffd3747 100644 --- a/tests/builder_full_delay.rs +++ b/tests/builder_full_delay.rs @@ -1,16 +1,12 @@ -use alloy_primitives::B256; use integration::RollupBoostTestHarnessBuilder; -use rollup_boost::ExecutionMode; use serde_json::Value; use std::sync::{Arc, Mutex}; use std::time::Duration; -use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; - mod integration; #[tokio::test] -async fn test_integration_builder_full_delay() -> eyre::Result<()> { +async fn builder_full_delay() -> eyre::Result<()> { // Create a dynamic handler that delays all the calls by 2 seconds let delay = Arc::new(Mutex::new(Duration::from_secs(0))); @@ -24,7 +20,7 @@ async fn test_integration_builder_full_delay() -> eyre::Result<()> { // This integration test checks that if the builder has a general delay in processing ANY of the requests, // rollup-boost does not stop building blocks. - let harness = RollupBoostTestHarnessBuilder::new("test_integration_builder_full_delay") + let harness = RollupBoostTestHarnessBuilder::new("builder_full_delay") .proxy_handler(handler) .build() .await?; diff --git a/tests/builder_returns_incorrect_block.rs b/tests/builder_returns_incorrect_block.rs index 61574adf..9cc49861 100644 --- a/tests/builder_returns_incorrect_block.rs +++ b/tests/builder_returns_incorrect_block.rs @@ -1,16 +1,13 @@ use alloy_primitives::B256; use integration::RollupBoostTestHarnessBuilder; -use rollup_boost::ExecutionMode; use serde_json::Value; -use std::sync::{Arc, Mutex}; -use std::time::Duration; use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; mod integration; #[tokio::test] -async fn test_integration_builder_returns_incorrect_block() -> eyre::Result<()> { +async fn builder_returns_incorrect_block() -> eyre::Result<()> { // Test that the builder returns a block with an incorrect state root and that rollup-boost // does not process it. let handler = Box::new(move |method: &str, _params: Value, _result: Value| { @@ -31,11 +28,10 @@ async fn test_integration_builder_returns_incorrect_block() -> eyre::Result<()> Some(result) }); - let harness = - RollupBoostTestHarnessBuilder::new("test_integration_builder_returns_incorrect_block") - .proxy_handler(handler) - .build() - .await?; + let harness = RollupBoostTestHarnessBuilder::new("builder_returns_incorrect_block") + .proxy_handler(handler) + .build() + .await?; let mut block_generator = harness.get_block_generator().await?; diff --git a/tests/execution_mode.rs b/tests/execution_mode.rs index 126b8c63..60fd7204 100644 --- a/tests/execution_mode.rs +++ b/tests/execution_mode.rs @@ -8,7 +8,7 @@ mod integration; use crate::integration::RollupBoostTestHarnessBuilder; #[tokio::test] -async fn test_integration_execution_mode() -> eyre::Result<()> { +async fn execution_mode() -> eyre::Result<()> { // Create a counter that increases whenever we receive a new RPC call in the builder let counter = Arc::new(Mutex::new(0)); @@ -20,7 +20,7 @@ async fn test_integration_execution_mode() -> eyre::Result<()> { None }); - let harness = RollupBoostTestHarnessBuilder::new("test_integration_dry_run") + let harness = RollupBoostTestHarnessBuilder::new("execution_mode") .proxy_handler(handler) .build() .await?; diff --git a/tests/integration/containers/op_reth.rs b/tests/integration/containers/op_reth.rs index 69943b48..0e68d4e8 100644 --- a/tests/integration/containers/op_reth.rs +++ b/tests/integration/containers/op_reth.rs @@ -4,6 +4,7 @@ use std::{ fs::File, io::BufReader, path::PathBuf, + sync::LazyLock, time::{SystemTime, UNIX_EPOCH}, }; @@ -14,7 +15,7 @@ use testcontainers::{ core::{ContainerPort, WaitFor}, }; -use crate::integration::L2_P2P_ENODE; +use crate::integration::{L2_P2P_ENODE, TEST_DATA}; const NAME: &str = "ghcr.io/paradigmxyz/op-reth"; const TAG: &str = "v1.3.4"; @@ -22,14 +23,33 @@ const TAG: &str = "v1.3.4"; const AUTH_RPC_PORT: u16 = 8551; const P2P_PORT: u16 = 30303; +// Genesis should only be created once and reused for +// each instance +static GENESIS: LazyLock = LazyLock::new(|| { + let file = File::open(PathBuf::from(format!("{}/genesis.json", *TEST_DATA))).unwrap(); + let reader = BufReader::new(file); + let mut genesis: Value = serde_json::from_reader(reader).unwrap(); + + // Update the timestamp field + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs(); + + if let Some(config) = genesis.as_object_mut() { + // Assuming timestamp is at the root level - adjust path as needed + config["timestamp"] = Value::String(format!("0x{:x}", timestamp)); + } + + serde_json::to_string_pretty(&genesis).unwrap() +}); + #[derive(Debug, Clone)] pub struct OpRethConfig { - chain: PathBuf, jwt_secret: PathBuf, p2p_secret: Option, pub trusted_peers: Vec, pub datadir: String, - pub disable_discovery: bool, pub color: String, pub ipcdisable: bool, pub env_vars: HashMap, @@ -38,18 +58,10 @@ pub struct OpRethConfig { impl Default for OpRethConfig { fn default() -> Self { Self { - chain: PathBuf::from(format!( - "{}/src/integration/testdata/genesis.json", - env!("CARGO_MANIFEST_DIR") - )), - jwt_secret: PathBuf::from(format!( - "{}/src/integration/testdata/jwt_secret.hex", - env!("CARGO_MANIFEST_DIR") - )), + jwt_secret: PathBuf::from(format!("{}/jwt_secret.hex", *TEST_DATA)), p2p_secret: None, trusted_peers: vec![], datadir: "data".to_string(), - disable_discovery: true, color: "never".to_string(), ipcdisable: true, env_vars: Default::default(), @@ -75,24 +87,6 @@ impl OpRethConfig { pub fn build(self) -> OpRethImage { // Write the genesis file to the test directory and update the timestamp to the current time - let genesis = { - let file = File::open(&self.chain).unwrap(); - let reader = BufReader::new(file); - let mut genesis: Value = serde_json::from_reader(reader).unwrap(); - - // Update the timestamp field - let timestamp = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_secs(); - - if let Some(config) = genesis.as_object_mut() { - // Assuming timestamp is at the root level - adjust path as needed - config["timestamp"] = Value::String(format!("0x{:x}", timestamp)); - } - - serde_json::to_string_pretty(&genesis).unwrap() - }; let mut copy_to_sources = vec![ CopyToContainer::new( @@ -102,7 +96,7 @@ impl OpRethConfig { .into_bytes(), "/jwt_secret.hex".to_string(), ), - CopyToContainer::new(genesis.into_bytes(), "/genesis.json".to_string()), + CopyToContainer::new(GENESIS.clone().into_bytes(), "/genesis.json".to_string()), ]; if let Some(p2p_secret) = &self.p2p_secret { @@ -115,7 +109,11 @@ impl OpRethConfig { )); } - let expose_ports = vec![ContainerPort::Tcp(8551), ContainerPort::Tcp(30303)]; + let expose_ports = vec![ + ContainerPort::Tcp(8551), + // ContainerPort::Tcp(30303), + // ContainerPort::Udp(30303), + ]; OpRethImage { config: self, @@ -164,15 +162,19 @@ impl Image for OpRethImage { fn cmd(&self) -> impl IntoIterator>> { let mut cmd = vec![ "node".to_string(), + "--port=30303".to_string(), + "--addr=0.0.0.0".to_string(), "--http".to_string(), "--http.addr=0.0.0.0".to_string(), "--authrpc.port=8551".to_string(), "--authrpc.addr=0.0.0.0".to_string(), "--authrpc.jwtsecret=/jwt_secret.hex".to_string(), "--chain=/genesis.json".to_string(), + "--log.stdout.filter=trace".to_string(), + "-vvvvv".to_string(), "--datadir".to_string(), self.config.datadir.clone(), - "--port=30303".to_string(), + "--disable-discovery".to_string(), "--color".to_string(), self.config.color.clone(), ]; @@ -180,13 +182,17 @@ impl Image for OpRethImage { cmd.push("--p2p-secret-key=/p2p_secret.hex".to_string()); } if !self.config.trusted_peers.is_empty() { + println!("Trusted peers: {:?}", self.config.trusted_peers); cmd.extend([ "--trusted-peers".to_string(), self.config.trusted_peers.join(","), ]); } - if self.config.disable_discovery { - cmd.push("--disable-discovery".to_string()); + if !self.config.trusted_peers.is_empty() { + cmd.extend([ + "--bootnodes".to_string(), + self.config.trusted_peers.join(","), + ]); } if self.config.ipcdisable { cmd.push("--ipcdisable".to_string()); @@ -202,7 +208,7 @@ impl Image for OpRethImage { pub trait OpRethMehods { async fn auth_rpc(&self) -> eyre::Result; async fn auth_rpc_port(&self) -> eyre::Result; - async fn enode(&self) -> eyre::Result; + async fn enode(&self, port: u16) -> eyre::Result; } impl OpRethMehods for ContainerAsync { @@ -219,13 +225,12 @@ impl OpRethMehods for ContainerAsync { .parse()?) } - async fn enode(&self) -> eyre::Result { + async fn enode(&self, port: u16) -> eyre::Result { Ok(format!( - "enode://{}@{}:{}", + "enode://{}@l2:{}", L2_P2P_ENODE, - self.get_host().await?, - self.get_host_port_ipv4(P2P_PORT).await? - ) - .parse()?) + // self.get_host().await?, + port // self.get_host_port_ipv4(P2P_PORT).await? + )) } } diff --git a/tests/integration/containers/rollup_boost.rs b/tests/integration/containers/rollup_boost.rs index 5b3a2daf..45e81e73 100644 --- a/tests/integration/containers/rollup_boost.rs +++ b/tests/integration/containers/rollup_boost.rs @@ -2,10 +2,12 @@ use clap::Parser; use rollup_boost::Args; use tokio::task::JoinHandle; +use crate::integration::{TEST_DATA, get_available_port}; + #[derive(Debug)] pub struct RollupBoost { args: Args, - pub handle: JoinHandle>, + pub _handle: JoinHandle>, } impl RollupBoost { @@ -17,6 +19,10 @@ impl RollupBoost { format!("http://localhost:{}", self.args.rpc_port) } + pub fn metrics_endpoint(&self) -> String { + format!("http://localhost:{}", self.args.metrics_port) + } + pub fn debug_endpoint(&self) -> String { format!("http://localhost:{}", self.args.debug_server_port) } @@ -29,29 +35,27 @@ pub struct RollupBoostConfig { impl Default for RollupBoostConfig { fn default() -> Self { - Self { - args: Args::parse_from([ - "rollup-boost", - &format!( - "--l2-jwt-path={}/src/integration/testdata/jwt_secret.hex", - env!("CARGO_MANIFEST_DIR") - ), - &format!( - "--builder-jwt-path={}/src/integration/testdata/jwt_secret.hex", - env!("CARGO_MANIFEST_DIR") - ), - "--log-level=trace", - "--tracing", - "--metrics", - ]), - } + let mut args = Args::parse_from([ + "rollup-boost", + &format!("--l2-jwt-path={}/jwt_secret.hex", *TEST_DATA), + &format!("--builder-jwt-path={}/jwt_secret.hex", *TEST_DATA), + "--log-level=trace", + "--tracing", + "--metrics", + ]); + + args.rpc_port = get_available_port().unwrap(); + args.metrics_port = get_available_port().unwrap(); + args.debug_server_port = get_available_port().unwrap(); + + Self { args } } } impl RollupBoostConfig { pub fn start(self) -> RollupBoost { let args = self.args.clone(); - let handle = tokio::spawn(async move { + let _handle = tokio::spawn(async move { let res = args.clone().run().await; if let Err(e) = &res { eprintln!("Error: {:?}", e); @@ -61,7 +65,7 @@ impl RollupBoostConfig { RollupBoost { args: self.args, - handle, + _handle, } } } diff --git a/tests/integration/mod.rs b/tests/integration/mod.rs index d29b2e83..5354d949 100644 --- a/tests/integration/mod.rs +++ b/tests/integration/mod.rs @@ -16,7 +16,7 @@ use http::Uri; use jsonrpsee::http_client::{HttpClient, transport::HttpBackend}; use jsonrpsee::proc_macros::rpc; use op_alloy_consensus::TxDeposit; -use op_alloy_rpc_types_engine::{OpExecutionPayloadEnvelopeV3, OpPayloadAttributes}; +use op_alloy_rpc_types_engine::OpPayloadAttributes; use proxy::{DynHandlerFn, start_proxy_server}; use rollup_boost::client::auth::{AuthClientLayer, AuthClientService}; use rollup_boost::debug_api::DebugClient; @@ -25,7 +25,10 @@ use rollup_boost::{NewPayload, PayloadSource}; use std::net::TcpListener; use std::path::PathBuf; use std::str::FromStr; +use std::sync::LazyLock; use std::time::{Duration, SystemTime}; +use testcontainers::core::ContainerPort; +use testcontainers::core::client::docker_client_instance; use testcontainers::core::logs::LogFrame; use testcontainers::core::logs::consumer::LogConsumer; use testcontainers::runners::AsyncRunner; @@ -38,8 +41,9 @@ use tracing::info; /// Default JWT token for testing purposes pub const JWT_SECRET: &str = "688f5d737bad920bdfb2fc2f488d6b6209eebda1dae949a8de91398d932c517a"; - pub const L2_P2P_ENODE: &str = "3479db4d9217fb5d7a8ed4d61ac36e120b05d36c2eefb795dc42ff2e971f251a2315f5649ea1833271e020b9adc98d5db9973c7ed92d6b2f1f2223088c3d852f"; +pub static TEST_DATA: LazyLock = + LazyLock::new(|| format!("{}/tests/integration/test_data", env!("CARGO_MANIFEST_DIR"))); mod containers; mod proxy; @@ -267,19 +271,42 @@ impl RollupBoostTestHarnessBuilder { let builder_log_consumer = self.log_consumer("builder").await?; let rollup_boost_log_file_path = self.file_path("rollup_boost")?; + let l2_p2p_port = get_available_port().expect("no available port"); let l2 = OpRethConfig::default() .set_p2p_secret(Some(PathBuf::from(format!( - "{}/src/integration/testdata/p2p_secret.hex", - env!("CARGO_MANIFEST_DIR") + "{}/p2p_secret.hex", + *TEST_DATA )))) .build() + .with_mapped_port(l2_p2p_port, ContainerPort::Tcp(30303)) + .with_mapped_port(l2_p2p_port, ContainerPort::Udp(30303)) + .with_mapped_port(get_available_port().unwrap(), ContainerPort::Tcp(8551)) + .with_network("devnet") .with_log_consumer(l2_log_consumer) .start() .await?; + let client = docker_client_instance().await?; + let res = client.inspect_container(l2.id(), None).await?; + let name = res.name.unwrap(); + let name = name[1..].to_string(); // remove the leading '/' + + let l2_enode = format!( + "enode://{}@{}:{}", + L2_P2P_ENODE, + name, + 30303 // l2_p2p_port // self.get_host_port_ipv4(P2P_PORT).await? + ); + tokio::time::sleep(Duration::from_secs(2)).await; + + let builder_p2p_port = get_available_port().expect("no available port"); let builder = OpRethConfig::default() - .set_trusted_peers(vec![l2.enode().await?.to_string()]) + .set_trusted_peers(vec![l2_enode]) .build() + .with_mapped_port(builder_p2p_port, ContainerPort::Tcp(30303)) + .with_mapped_port(builder_p2p_port, ContainerPort::Udp(30303)) + .with_mapped_port(get_available_port().unwrap(), ContainerPort::Tcp(8551)) + .with_network("devnet") .with_log_consumer(builder_log_consumer) .start() .await?; @@ -290,18 +317,24 @@ impl RollupBoostTestHarnessBuilder { // run a proxy in between the builder and the rollup-boost if the proxy_handler is set let mut builder_authrpc_port = builder.auth_rpc_port().await?; if let Some(proxy_handler) = self.proxy_handler { + println!("starting proxy server"); let proxy_port = get_available_port().expect("no available port"); let _ = start_proxy_server(proxy_handler, proxy_port, builder_authrpc_port).await; builder_authrpc_port = proxy_port }; - let builder_url = format!("http://127.0.0.1:{}", builder_authrpc_port); + let builder_url = format!("http://localhost:{}/", builder_authrpc_port); + println!("proxy authrpc: {}", builder_url); // Start Rollup-boost instance let mut rollup_boost = RollupBoostConfig::default(); - rollup_boost.args.l2_client.l2_url = l2.auth_rpc().await?.try_into().unwrap(); + rollup_boost.args.l2_client.l2_url = l2.auth_rpc().await?; rollup_boost.args.builder.builder_url = builder_url.try_into().unwrap(); rollup_boost.args.log_file = Some(rollup_boost_log_file_path); let rollup_boost = rollup_boost.start(); + println!("rollup-boost authrpc: {}", rollup_boost.rpc_endpoint()); + println!("rollup-boost metrics: {}", rollup_boost.metrics_endpoint()); + + tokio::time::sleep(Duration::from_secs(2)).await; Ok(RollupBoostTestHarness { l2, @@ -506,7 +539,12 @@ fn local_host(port: u16) -> Uri { } fn get_available_port() -> Option { - (8000..9000).find(|port| port_is_available(*port)) + loop { + let port = rand::random::() % 20000 + 1000; + if port_is_available(port) { + return Some(port); + } + } } fn port_is_available(port: u16) -> bool { diff --git a/src/integration/testdata/genesis.json b/tests/integration/test_data/genesis.json similarity index 100% rename from src/integration/testdata/genesis.json rename to tests/integration/test_data/genesis.json diff --git a/src/integration/testdata/jwt_secret.hex b/tests/integration/test_data/jwt_secret.hex similarity index 100% rename from src/integration/testdata/jwt_secret.hex rename to tests/integration/test_data/jwt_secret.hex diff --git a/src/integration/testdata/p2p_secret.hex b/tests/integration/test_data/p2p_secret.hex similarity index 100% rename from src/integration/testdata/p2p_secret.hex rename to tests/integration/test_data/p2p_secret.hex diff --git a/tests/integration/testdata/genesis.json b/tests/integration/testdata/genesis.json deleted file mode 100644 index 8ee3a460..00000000 --- a/tests/integration/testdata/genesis.json +++ /dev/null @@ -1,896 +0,0 @@ -{ - "alloc": { - "0000000000000000000000000000000000000000": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000001": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000002": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000003": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000004": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000005": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000006": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000007": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000008": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000009": { - "balance": "0x1" - }, - "000000000000000000000000000000000000000a": { - "balance": "0x1" - }, - "000000000000000000000000000000000000000b": { - "balance": "0x1" - }, - "000000000000000000000000000000000000000c": { - "balance": "0x1" - }, - "000000000000000000000000000000000000000d": { - "balance": "0x1" - }, - "000000000000000000000000000000000000000e": { - "balance": "0x1" - }, - "000000000000000000000000000000000000000f": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000010": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000011": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000012": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000013": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000014": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000015": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000016": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000017": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000018": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000019": { - "balance": "0x1" - }, - "000000000000000000000000000000000000001a": { - "balance": "0x1" - }, - "000000000000000000000000000000000000001b": { - "balance": "0x1" - }, - "000000000000000000000000000000000000001c": { - "balance": "0x1" - }, - "000000000000000000000000000000000000001d": { - "balance": "0x1" - }, - "000000000000000000000000000000000000001e": { - "balance": "0x1" - }, - "000000000000000000000000000000000000001f": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000020": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000021": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000022": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000023": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000024": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000025": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000026": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000027": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000028": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000029": { - "balance": "0x1" - }, - "000000000000000000000000000000000000002a": { - "balance": "0x1" - }, - "000000000000000000000000000000000000002b": { - "balance": "0x1" - }, - "000000000000000000000000000000000000002c": { - "balance": "0x1" - }, - "000000000000000000000000000000000000002d": { - "balance": "0x1" - }, - "000000000000000000000000000000000000002e": { - "balance": "0x1" - }, - "000000000000000000000000000000000000002f": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000030": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000031": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000032": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000033": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000034": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000035": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000036": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000037": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000038": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000039": { - "balance": "0x1" - }, - "000000000000000000000000000000000000003a": { - "balance": "0x1" - }, - "000000000000000000000000000000000000003b": { - "balance": "0x1" - }, - "000000000000000000000000000000000000003c": { - "balance": "0x1" - }, - "000000000000000000000000000000000000003d": { - "balance": "0x1" - }, - "000000000000000000000000000000000000003e": { - "balance": "0x1" - }, - "000000000000000000000000000000000000003f": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000040": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000041": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000042": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000043": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000044": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000045": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000046": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000047": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000048": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000049": { - "balance": "0x1" - }, - "000000000000000000000000000000000000004a": { - "balance": "0x1" - }, - "000000000000000000000000000000000000004b": { - "balance": "0x1" - }, - "000000000000000000000000000000000000004c": { - "balance": "0x1" - }, - "000000000000000000000000000000000000004d": { - "balance": "0x1" - }, - "000000000000000000000000000000000000004e": { - "balance": "0x1" - }, - "000000000000000000000000000000000000004f": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000050": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000051": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000052": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000053": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000054": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000055": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000056": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000057": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000058": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000059": { - "balance": "0x1" - }, - "000000000000000000000000000000000000005a": { - "balance": "0x1" - }, - "000000000000000000000000000000000000005b": { - "balance": "0x1" - }, - "000000000000000000000000000000000000005c": { - "balance": "0x1" - }, - "000000000000000000000000000000000000005d": { - "balance": "0x1" - }, - "000000000000000000000000000000000000005e": { - "balance": "0x1" - }, - "000000000000000000000000000000000000005f": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000060": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000061": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000062": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000063": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000064": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000065": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000066": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000067": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000068": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000069": { - "balance": "0x1" - }, - "000000000000000000000000000000000000006a": { - "balance": "0x1" - }, - "000000000000000000000000000000000000006b": { - "balance": "0x1" - }, - "000000000000000000000000000000000000006c": { - "balance": "0x1" - }, - "000000000000000000000000000000000000006d": { - "balance": "0x1" - }, - "000000000000000000000000000000000000006e": { - "balance": "0x1" - }, - "000000000000000000000000000000000000006f": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000070": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000071": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000072": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000073": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000074": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000075": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000076": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000077": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000078": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000079": { - "balance": "0x1" - }, - "000000000000000000000000000000000000007a": { - "balance": "0x1" - }, - "000000000000000000000000000000000000007b": { - "balance": "0x1" - }, - "000000000000000000000000000000000000007c": { - "balance": "0x1" - }, - "000000000000000000000000000000000000007d": { - "balance": "0x1" - }, - "000000000000000000000000000000000000007e": { - "balance": "0x1" - }, - "000000000000000000000000000000000000007f": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000080": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000081": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000082": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000083": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000084": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000085": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000086": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000087": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000088": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000089": { - "balance": "0x1" - }, - "000000000000000000000000000000000000008a": { - "balance": "0x1" - }, - "000000000000000000000000000000000000008b": { - "balance": "0x1" - }, - "000000000000000000000000000000000000008c": { - "balance": "0x1" - }, - "000000000000000000000000000000000000008d": { - "balance": "0x1" - }, - "000000000000000000000000000000000000008e": { - "balance": "0x1" - }, - "000000000000000000000000000000000000008f": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000090": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000091": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000092": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000093": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000094": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000095": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000096": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000097": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000098": { - "balance": "0x1" - }, - "0000000000000000000000000000000000000099": { - "balance": "0x1" - }, - "000000000000000000000000000000000000009a": { - "balance": "0x1" - }, - "000000000000000000000000000000000000009b": { - "balance": "0x1" - }, - "000000000000000000000000000000000000009c": { - "balance": "0x1" - }, - "000000000000000000000000000000000000009d": { - "balance": "0x1" - }, - "000000000000000000000000000000000000009e": { - "balance": "0x1" - }, - "000000000000000000000000000000000000009f": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000a0": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000a1": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000a2": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000a3": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000a4": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000a5": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000a6": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000a7": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000a8": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000a9": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000aa": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000ab": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000ac": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000ad": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000ae": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000af": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000b0": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000b1": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000b2": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000b3": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000b4": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000b5": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000b6": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000b7": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000b8": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000b9": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000ba": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000bb": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000bc": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000bd": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000be": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000bf": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000c0": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000c1": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000c2": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000c3": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000c4": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000c5": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000c6": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000c7": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000c8": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000c9": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000ca": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000cb": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000cc": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000cd": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000ce": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000cf": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000d0": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000d1": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000d2": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000d3": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000d4": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000d5": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000d6": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000d7": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000d8": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000d9": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000da": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000db": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000dc": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000dd": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000de": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000df": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000e0": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000e1": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000e2": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000e3": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000e4": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000e5": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000e6": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000e7": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000e8": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000e9": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000ea": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000eb": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000ec": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000ed": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000ee": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000ef": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000f0": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000f1": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000f2": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000f3": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000f4": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000f5": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000f6": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000f7": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000f8": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000f9": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000fa": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000fb": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000fc": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000fd": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000fe": { - "balance": "0x1" - }, - "00000000000000000000000000000000000000ff": { - "balance": "0x1" - }, - "000000000022d473030f116ddee9f6b43ac78ba3": { - "balance": "0x0", - "code": "0x6040608081526004908136101561001557600080fd5b600090813560e01c80630d58b1db1461126c578063137c29fe146110755780632a2d80d114610db75780632b67b57014610bde57806330f28b7a14610ade5780633644e51514610a9d57806336c7851614610a285780633ff9dcb1146109a85780634fe02b441461093f57806365d9723c146107ac57806387517c451461067a578063927da105146105c3578063cc53287f146104a3578063edd9444b1461033a5763fe8ec1a7146100c657600080fd5b346103365760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103365767ffffffffffffffff833581811161033257610114903690860161164b565b60243582811161032e5761012b903690870161161a565b6101336114e6565b9160843585811161032a5761014b9036908a016115c1565b98909560a43590811161032657610164913691016115c1565b969095815190610173826113ff565b606b82527f5065726d697442617463685769746e6573735472616e7366657246726f6d285460208301527f6f6b656e5065726d697373696f6e735b5d207065726d69747465642c61646472838301527f657373207370656e6465722c75696e74323536206e6f6e63652c75696e74323560608301527f3620646561646c696e652c000000000000000000000000000000000000000000608083015282519a8b9181610222602085018096611f93565b918237018a8152039961025b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09b8c8101835282611437565b5190209085515161026b81611ebb565b908a5b8181106102f95750506102f6999a6102ed9183516102a081610294602082018095611f66565b03848101835282611437565b519020602089810151858b015195519182019687526040820192909252336060820152608081019190915260a081019390935260643560c08401528260e081015b03908101835282611437565b51902093611cf7565b80f35b8061031161030b610321938c5161175e565b51612054565b61031b828661175e565b52611f0a565b61026e565b8880fd5b8780fd5b8480fd5b8380fd5b5080fd5b5091346103365760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103365767ffffffffffffffff9080358281116103325761038b903690830161164b565b60243583811161032e576103a2903690840161161a565b9390926103ad6114e6565b9160643590811161049f576103c4913691016115c1565b949093835151976103d489611ebb565b98885b81811061047d5750506102f697988151610425816103f9602082018095611f66565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282611437565b5190206020860151828701519083519260208401947ffcf35f5ac6a2c28868dc44c302166470266239195f02b0ee408334829333b7668652840152336060840152608083015260a082015260a081526102ed8161141b565b808b61031b8261049461030b61049a968d5161175e565b9261175e565b6103d7565b8680fd5b5082346105bf57602090817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103325780359067ffffffffffffffff821161032e576104f49136910161161a565b929091845b848110610504578580f35b8061051a610515600193888861196c565b61197c565b61052f84610529848a8a61196c565b0161197c565b3389528385528589209173ffffffffffffffffffffffffffffffffffffffff80911692838b528652868a20911690818a5285528589207fffffffffffffffffffffffff000000000000000000000000000000000000000081541690558551918252848201527f89b1add15eff56b3dfe299ad94e01f2b52fbcb80ae1a3baea6ae8c04cb2b98a4853392a2016104f9565b8280fd5b50346103365760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033657610676816105ff6114a0565b936106086114c3565b6106106114e6565b73ffffffffffffffffffffffffffffffffffffffff968716835260016020908152848420928816845291825283832090871683528152919020549251938316845260a083901c65ffffffffffff169084015260d09190911c604083015281906060820190565b0390f35b50346103365760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610336576106b26114a0565b906106bb6114c3565b916106c46114e6565b65ffffffffffff926064358481169081810361032a5779ffffffffffff0000000000000000000000000000000000000000947fda9fa7c1b00402c17d0161b249b1ab8bbec047c5a52207b9c112deffd817036b94338a5260016020527fffffffffffff0000000000000000000000000000000000000000000000000000858b209873ffffffffffffffffffffffffffffffffffffffff809416998a8d5260205283878d209b169a8b8d52602052868c209486156000146107a457504216925b8454921697889360a01b16911617179055815193845260208401523392a480f35b905092610783565b5082346105bf5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105bf576107e56114a0565b906107ee6114c3565b9265ffffffffffff604435818116939084810361032a57338852602091600183528489209673ffffffffffffffffffffffffffffffffffffffff80911697888b528452858a20981697888a5283528489205460d01c93848711156109175761ffff9085840316116108f05750907f55eb90d810e1700b35a8e7e25395ff7f2b2259abd7415ca2284dfb1c246418f393929133895260018252838920878a528252838920888a5282528389209079ffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffff000000000000000000000000000000000000000000000000000083549260d01b16911617905582519485528401523392a480f35b84517f24d35a26000000000000000000000000000000000000000000000000000000008152fd5b5084517f756688fe000000000000000000000000000000000000000000000000000000008152fd5b503461033657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610336578060209273ffffffffffffffffffffffffffffffffffffffff61098f6114a0565b1681528084528181206024358252845220549051908152f35b5082346105bf57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105bf577f3704902f963766a4e561bbaab6e6cdc1b1dd12f6e9e99648da8843b3f46b918d90359160243533855284602052818520848652602052818520818154179055815193845260208401523392a280f35b8234610a9a5760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610a9a57610a606114a0565b610a686114c3565b610a706114e6565b6064359173ffffffffffffffffffffffffffffffffffffffff8316830361032e576102f6936117a1565b80fd5b503461033657817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033657602090610ad7611b1e565b9051908152f35b508290346105bf576101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105bf57610b1a3661152a565b90807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7c36011261033257610b4c611478565b9160e43567ffffffffffffffff8111610bda576102f694610b6f913691016115c1565b939092610b7c8351612054565b6020840151828501519083519260208401947f939c21a48a8dbe3a9a2404a1d46691e4d39f6583d6ec6b35714604c986d801068652840152336060840152608083015260a082015260a08152610bd18161141b565b51902091611c25565b8580fd5b509134610336576101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033657610c186114a0565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc360160c08112610332576080855191610c51836113e3565b1261033257845190610c6282611398565b73ffffffffffffffffffffffffffffffffffffffff91602435838116810361049f578152604435838116810361049f57602082015265ffffffffffff606435818116810361032a5788830152608435908116810361049f576060820152815260a435938285168503610bda576020820194855260c4359087830182815260e43567ffffffffffffffff811161032657610cfe90369084016115c1565b929093804211610d88575050918591610d786102f6999a610d7e95610d238851611fbe565b90898c511690519083519260208401947ff3841cd1ff0085026a6327b620b67997ce40f282c88a8e905a7a5626e310f3d086528401526060830152608082015260808152610d70816113ff565b519020611bd9565b916120c7565b519251169161199d565b602492508a51917fcd21db4f000000000000000000000000000000000000000000000000000000008352820152fd5b5091346103365760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc93818536011261033257610df36114a0565b9260249081359267ffffffffffffffff9788851161032a578590853603011261049f578051978589018981108282111761104a578252848301358181116103265785019036602383011215610326578382013591610e50836115ef565b90610e5d85519283611437565b838252602093878584019160071b83010191368311611046578801905b828210610fe9575050508a526044610e93868801611509565b96838c01978852013594838b0191868352604435908111610fe557610ebb90369087016115c1565b959096804211610fba575050508998995151610ed681611ebb565b908b5b818110610f9757505092889492610d7892610f6497958351610f02816103f98682018095611f66565b5190209073ffffffffffffffffffffffffffffffffffffffff9a8b8b51169151928551948501957faf1b0d30d2cab0380e68f0689007e3254993c596f2fdd0aaa7f4d04f794408638752850152830152608082015260808152610d70816113ff565b51169082515192845b848110610f78578580f35b80610f918585610f8b600195875161175e565b5161199d565b01610f6d565b80610311610fac8e9f9e93610fb2945161175e565b51611fbe565b9b9a9b610ed9565b8551917fcd21db4f000000000000000000000000000000000000000000000000000000008352820152fd5b8a80fd5b6080823603126110465785608091885161100281611398565b61100b85611509565b8152611018838601611509565b838201526110278a8601611607565b8a8201528d611037818701611607565b90820152815201910190610e7a565b8c80fd5b84896041867f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b5082346105bf576101407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105bf576110b03661152a565b91807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7c360112610332576110e2611478565b67ffffffffffffffff93906101043585811161049f5761110590369086016115c1565b90936101243596871161032a57611125610bd1966102f6983691016115c1565b969095825190611134826113ff565b606482527f5065726d69745769746e6573735472616e7366657246726f6d28546f6b656e5060208301527f65726d697373696f6e73207065726d69747465642c6164647265737320737065848301527f6e6465722c75696e74323536206e6f6e63652c75696e7432353620646561646c60608301527f696e652c0000000000000000000000000000000000000000000000000000000060808301528351948591816111e3602085018096611f93565b918237018b8152039361121c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe095868101835282611437565b5190209261122a8651612054565b6020878101518589015195519182019687526040820192909252336060820152608081019190915260a081019390935260e43560c08401528260e081016102e1565b5082346105bf576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033257813567ffffffffffffffff92838211610bda5736602383011215610bda5781013592831161032e576024906007368386831b8401011161049f57865b8581106112e5578780f35b80821b83019060807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc83360301126103265761139288876001946060835161132c81611398565b611368608461133c8d8601611509565b9485845261134c60448201611509565b809785015261135d60648201611509565b809885015201611509565b918291015273ffffffffffffffffffffffffffffffffffffffff80808093169516931691166117a1565b016112da565b6080810190811067ffffffffffffffff8211176113b457604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6060810190811067ffffffffffffffff8211176113b457604052565b60a0810190811067ffffffffffffffff8211176113b457604052565b60c0810190811067ffffffffffffffff8211176113b457604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176113b457604052565b60c4359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b600080fd5b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b6044359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc01906080821261149b576040805190611563826113e3565b8082941261149b57805181810181811067ffffffffffffffff8211176113b457825260043573ffffffffffffffffffffffffffffffffffffffff8116810361149b578152602435602082015282526044356020830152606435910152565b9181601f8401121561149b5782359167ffffffffffffffff831161149b576020838186019501011161149b57565b67ffffffffffffffff81116113b45760051b60200190565b359065ffffffffffff8216820361149b57565b9181601f8401121561149b5782359167ffffffffffffffff831161149b576020808501948460061b01011161149b57565b91909160608184031261149b576040805191611666836113e3565b8294813567ffffffffffffffff9081811161149b57830182601f8201121561149b578035611693816115ef565b926116a087519485611437565b818452602094858086019360061b8501019381851161149b579086899897969594939201925b8484106116e3575050505050855280820135908501520135910152565b90919293949596978483031261149b578851908982019082821085831117611730578a928992845261171487611509565b81528287013583820152815201930191908897969594936116c6565b602460007f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b80518210156117725760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b92919273ffffffffffffffffffffffffffffffffffffffff604060008284168152600160205282828220961695868252602052818120338252602052209485549565ffffffffffff8760a01c16804211611884575082871696838803611812575b5050611810955016926118b5565b565b878484161160001461184f57602488604051907ff96fb0710000000000000000000000000000000000000000000000000000000082526004820152fd5b7fffffffffffffffffffffffff000000000000000000000000000000000000000084846118109a031691161790553880611802565b602490604051907fd81b2f2e0000000000000000000000000000000000000000000000000000000082526004820152fd5b9060006064926020958295604051947f23b872dd0000000000000000000000000000000000000000000000000000000086526004860152602485015260448401525af13d15601f3d116001600051141617161561190e57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152fd5b91908110156117725760061b0190565b3573ffffffffffffffffffffffffffffffffffffffff8116810361149b5790565b9065ffffffffffff908160608401511673ffffffffffffffffffffffffffffffffffffffff908185511694826020820151169280866040809401511695169560009187835260016020528383208984526020528383209916988983526020528282209184835460d01c03611af5579185611ace94927fc6a377bfc4eb120024a8ac08eef205be16b817020812c73223e81d1bdb9708ec98979694508715600014611ad35779ffffffffffff00000000000000000000000000000000000000009042165b60a01b167fffffffffffff00000000000000000000000000000000000000000000000000006001860160d01b1617179055519384938491604091949373ffffffffffffffffffffffffffffffffffffffff606085019616845265ffffffffffff809216602085015216910152565b0390a4565b5079ffffffffffff000000000000000000000000000000000000000087611a60565b600484517f756688fe000000000000000000000000000000000000000000000000000000008152fd5b467f000000000000000000000000000000000000000000000000000000000000038503611b69577f48deb34b39fb4b41f5c195008940d5ef510cdd7853eba5807b2fa08dfd58647590565b60405160208101907f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86682527f9ac997416e8ff9d2ff6bebeb7149f65cdae5e32e2b90440b566bb3044041d36a604082015246606082015230608082015260808152611bd3816113ff565b51902090565b611be1611b1e565b906040519060208201927f190100000000000000000000000000000000000000000000000000000000000084526022830152604282015260428152611bd381611398565b9192909360a435936040840151804211611cc65750602084510151808611611c955750918591610d78611c6594611c60602088015186611e47565b611bd9565b73ffffffffffffffffffffffffffffffffffffffff809151511692608435918216820361149b57611810936118b5565b602490604051907f3728b83d0000000000000000000000000000000000000000000000000000000082526004820152fd5b602490604051907fcd21db4f0000000000000000000000000000000000000000000000000000000082526004820152fd5b959093958051519560409283830151804211611e175750848803611dee57611d2e918691610d7860209b611c608d88015186611e47565b60005b868110611d42575050505050505050565b611d4d81835161175e565b5188611d5a83878a61196c565b01359089810151808311611dbe575091818888886001968596611d84575b50505050505001611d31565b611db395611dad9273ffffffffffffffffffffffffffffffffffffffff6105159351169561196c565b916118b5565b803888888883611d78565b6024908651907f3728b83d0000000000000000000000000000000000000000000000000000000082526004820152fd5b600484517fff633a38000000000000000000000000000000000000000000000000000000008152fd5b6024908551907fcd21db4f0000000000000000000000000000000000000000000000000000000082526004820152fd5b9073ffffffffffffffffffffffffffffffffffffffff600160ff83161b9216600052600060205260406000209060081c6000526020526040600020818154188091551615611e9157565b60046040517f756688fe000000000000000000000000000000000000000000000000000000008152fd5b90611ec5826115ef565b611ed26040519182611437565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0611f0082946115ef565b0190602036910137565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114611f375760010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b805160208092019160005b828110611f7f575050505090565b835185529381019392810192600101611f71565b9081519160005b838110611fab575050016000815290565b8060208092840101518185015201611f9a565b60405160208101917f65626cad6cb96493bf6f5ebea28756c966f023ab9e8a83a7101849d5573b3678835273ffffffffffffffffffffffffffffffffffffffff8082511660408401526020820151166060830152606065ffffffffffff9182604082015116608085015201511660a082015260a0815260c0810181811067ffffffffffffffff8211176113b45760405251902090565b6040516020808201927f618358ac3db8dc274f0cd8829da7e234bd48cd73c4a740aede1adec9846d06a1845273ffffffffffffffffffffffffffffffffffffffff81511660408401520151606082015260608152611bd381611398565b919082604091031261149b576020823592013590565b6000843b61222e5750604182036121ac576120e4828201826120b1565b939092604010156117725760209360009360ff6040608095013560f81c5b60405194855216868401526040830152606082015282805260015afa156121a05773ffffffffffffffffffffffffffffffffffffffff806000511691821561217657160361214c57565b60046040517f815e1d64000000000000000000000000000000000000000000000000000000008152fd5b60046040517f8baa579f000000000000000000000000000000000000000000000000000000008152fd5b6040513d6000823e3d90fd5b60408203612204576121c0918101906120b1565b91601b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84169360ff1c019060ff8211611f375760209360009360ff608094612102565b60046040517f4be6321b000000000000000000000000000000000000000000000000000000008152fd5b929391601f928173ffffffffffffffffffffffffffffffffffffffff60646020957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0604051988997889687947f1626ba7e000000000000000000000000000000000000000000000000000000009e8f8752600487015260406024870152816044870152868601378b85828601015201168101030192165afa9081156123a857829161232a575b507fffffffff000000000000000000000000000000000000000000000000000000009150160361230057565b60046040517fb0669cbc000000000000000000000000000000000000000000000000000000008152fd5b90506020813d82116123a0575b8161234460209383611437565b810103126103365751907fffffffff0000000000000000000000000000000000000000000000000000000082168203610a9a57507fffffffff0000000000000000000000000000000000000000000000000000000090386122d4565b3d9150612337565b6040513d84823e3d90fdfea164736f6c6343000811000a", - "nonce": "0x1" - }, - "0000000071727de22e5e9d8baf0edac6f37da032": { - "balance": "0x0", - "code": "0x60806040526004361015610024575b361561001957600080fd5b61002233612748565b005b60003560e01c806242dc5314611b0057806301ffc9a7146119ae5780630396cb60146116765780630bd28e3b146115fa5780631b2e01b814611566578063205c2878146113d157806322cdde4c1461136b57806335567e1a146112b35780635287ce12146111a557806370a0823114611140578063765e827f14610e82578063850aaf6214610dc35780639b249f6914610c74578063b760faf914610c3a578063bb9fe6bf14610a68578063c23a5cea146107c4578063dbed18e0146101a15763fc7e286d0361000e573461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5773ffffffffffffffffffffffffffffffffffffffff61013a61229f565b16600052600060205260a0604060002065ffffffffffff6001825492015460405192835260ff8116151560208401526dffffffffffffffffffffffffffff8160081c16604084015263ffffffff8160781c16606084015260981c166080820152f35b600080fd5b3461019c576101af36612317565b906101b86129bd565b60009160005b82811061056f57506101d08493612588565b6000805b8481106102fc5750507fbb47ee3e183a558b1a2ff0874b079f3fc5478b7454eacf2bfc5af2ff5878f972600080a16000809360005b81811061024757610240868660007f575ff3acadd5ab348fe1855e217e0f3678f8d767d7494c9f9fefbee2e17cca4d8180a2613ba7565b6001600255005b6102a261025582848a612796565b73ffffffffffffffffffffffffffffffffffffffff6102766020830161282a565b167f575ff3acadd5ab348fe1855e217e0f3678f8d767d7494c9f9fefbee2e17cca4d600080a2806127d6565b906000915b8083106102b957505050600101610209565b909194976102f36102ed6001926102e78c8b6102e0826102da8e8b8d61269d565b9261265a565b5191613597565b90612409565b99612416565b950191906102a7565b6020610309828789612796565b61031f61031682806127d6565b9390920161282a565b9160009273ffffffffffffffffffffffffffffffffffffffff8091165b8285106103505750505050506001016101d4565b909192939561037f83610378610366848c61265a565b516103728b898b61269d565b856129f6565b9290613dd7565b9116840361050a576104a5576103958491613dd7565b9116610440576103b5576103aa600191612416565b96019392919061033c565b60a487604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602160448201527f41413332207061796d61737465722065787069726564206f72206e6f7420647560648201527f65000000000000000000000000000000000000000000000000000000000000006084820152fd5b608488604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601460448201527f41413334207369676e6174757265206572726f720000000000000000000000006064820152fd5b608488604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601760448201527f414132322065787069726564206f72206e6f74206475650000000000000000006064820152fd5b608489604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601460448201527f41413234207369676e6174757265206572726f720000000000000000000000006064820152fd5b61057a818487612796565b9361058585806127d6565b919095602073ffffffffffffffffffffffffffffffffffffffff6105aa82840161282a565b1697600192838a1461076657896105da575b5050505060019293949550906105d191612409565b939291016101be565b8060406105e892019061284b565b918a3b1561019c57929391906040519485937f2dd8113300000000000000000000000000000000000000000000000000000000855288604486016040600488015252606490818601918a60051b8701019680936000915b8c83106106e657505050505050838392610684927ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8560009803016024860152612709565b03818a5afa90816106d7575b506106c657602486604051907f86a9f7500000000000000000000000000000000000000000000000000000000082526004820152fd5b93945084936105d1600189806105bc565b6106e0906121bd565b88610690565b91939596977fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c908a9294969a0301865288357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee18336030181121561019c57836107538793858394016128ec565b9a0196019301909189979695949261063f565b606483604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601760248201527f4141393620696e76616c69642061676772656761746f720000000000000000006044820152fd5b3461019c576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c576107fc61229f565b33600052600082526001604060002001908154916dffffffffffffffffffffffffffff8360081c16928315610a0a5765ffffffffffff8160981c1680156109ac57421061094e5760009373ffffffffffffffffffffffffffffffffffffffff859485947fffffffffffffff000000000000000000000000000000000000000000000000ff86951690556040517fb7c918e0e249f999e965cafeb6c664271b3f4317d296461500e71da39f0cbda33391806108da8786836020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b0390a2165af16108e8612450565b50156108f057005b606490604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601860248201527f6661696c656420746f207769746864726177207374616b6500000000000000006044820152fd5b606485604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601b60248201527f5374616b65207769746864726177616c206973206e6f742064756500000000006044820152fd5b606486604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601d60248201527f6d7573742063616c6c20756e6c6f636b5374616b6528292066697273740000006044820152fd5b606485604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601460248201527f4e6f207374616b6520746f2077697468647261770000000000000000000000006044820152fd5b3461019c5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c573360005260006020526001604060002001805463ffffffff8160781c16908115610bdc5760ff1615610b7e5765ffffffffffff908142160191818311610b4f5780547fffffffffffffff000000000000ffffffffffffffffffffffffffffffffffff001678ffffffffffff00000000000000000000000000000000000000609885901b161790556040519116815233907ffa9b3c14cc825c412c9ed81b3ba365a5b459439403f18829e572ed53a4180f0a90602090a2005b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f616c726561647920756e7374616b696e670000000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f6e6f74207374616b6564000000000000000000000000000000000000000000006044820152fd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c57610022610c6f61229f565b612748565b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5760043567ffffffffffffffff811161019c576020610cc8610d1b9236906004016122c2565b919073ffffffffffffffffffffffffffffffffffffffff9260405194859283927f570e1a360000000000000000000000000000000000000000000000000000000084528560048501526024840191612709565b03816000857f000000000000000000000000efc2c1444ebcc4db75e7613d20c6a62ff67a167c165af1908115610db757602492600092610d86575b50604051917f6ca7b806000000000000000000000000000000000000000000000000000000008352166004820152fd5b610da991925060203d602011610db0575b610da181836121ed565b8101906126dd565b9083610d56565b503d610d97565b6040513d6000823e3d90fd5b3461019c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c57610dfa61229f565b60243567ffffffffffffffff811161019c57600091610e1e839236906004016122c2565b90816040519283928337810184815203915af4610e39612450565b90610e7e6040519283927f99410554000000000000000000000000000000000000000000000000000000008452151560048401526040602484015260448301906123c6565b0390fd5b3461019c57610e9036612317565b610e9b9291926129bd565b610ea483612588565b60005b848110610f1c57506000927fbb47ee3e183a558b1a2ff0874b079f3fc5478b7454eacf2bfc5af2ff5878f972600080a16000915b858310610eec576102408585613ba7565b909193600190610f12610f0087898761269d565b610f0a888661265a565b519088613597565b0194019190610edb565b610f47610f40610f2e8385979561265a565b51610f3a84898761269d565b846129f6565b9190613dd7565b73ffffffffffffffffffffffffffffffffffffffff929183166110db5761107657610f7190613dd7565b911661101157610f8657600101929092610ea7565b60a490604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602160448201527f41413332207061796d61737465722065787069726564206f72206e6f7420647560648201527f65000000000000000000000000000000000000000000000000000000000000006084820152fd5b608482604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601460448201527f41413334207369676e6174757265206572726f720000000000000000000000006064820152fd5b608483604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601760448201527f414132322065787069726564206f72206e6f74206475650000000000000000006064820152fd5b608484604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601460448201527f41413234207369676e6174757265206572726f720000000000000000000000006064820152fd5b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5773ffffffffffffffffffffffffffffffffffffffff61118c61229f565b1660005260006020526020604060002054604051908152f35b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5773ffffffffffffffffffffffffffffffffffffffff6111f161229f565b6000608060405161120181612155565b828152826020820152826040820152826060820152015216600052600060205260a06040600020608060405161123681612155565b6001835493848352015490602081019060ff8316151582526dffffffffffffffffffffffffffff60408201818560081c16815263ffffffff936060840193858760781c16855265ffffffffffff978891019660981c1686526040519788525115156020880152511660408601525116606084015251166080820152f35b3461019c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5760206112ec61229f565b73ffffffffffffffffffffffffffffffffffffffff6113096122f0565b911660005260018252604060002077ffffffffffffffffffffffffffffffffffffffffffffffff821660005282526040600020547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000006040519260401b16178152f35b3461019c577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60208136011261019c576004359067ffffffffffffffff821161019c5761012090823603011261019c576113c9602091600401612480565b604051908152f35b3461019c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5761140861229f565b60243590336000526000602052604060002090815491828411611508576000808573ffffffffffffffffffffffffffffffffffffffff8295839561144c848a612443565b90556040805173ffffffffffffffffffffffffffffffffffffffff831681526020810185905233917fd1c19fbcd4551a5edfb66d43d2e337c04837afda3482b42bdf569a8fccdae5fb91a2165af16114a2612450565b50156114aa57005b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f6661696c656420746f20776974686472617700000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f576974686472617720616d6f756e7420746f6f206c61726765000000000000006044820152fd5b3461019c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5761159d61229f565b73ffffffffffffffffffffffffffffffffffffffff6115ba6122f0565b9116600052600160205277ffffffffffffffffffffffffffffffffffffffffffffffff604060002091166000526020526020604060002054604051908152f35b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5760043577ffffffffffffffffffffffffffffffffffffffffffffffff811680910361019c5733600052600160205260406000209060005260205260406000206116728154612416565b9055005b6020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5760043563ffffffff9182821680920361019c5733600052600081526040600020928215611950576001840154908160781c1683106118f2576116f86dffffffffffffffffffffffffffff9182349160081c16612409565b93841561189457818511611836579065ffffffffffff61180592546040519061172082612155565b8152848101926001845260408201908816815260608201878152600160808401936000855233600052600089526040600020905181550194511515917fffffffffffffffffffffffffff0000000000000000000000000000000000000060ff72ffffffff0000000000000000000000000000006effffffffffffffffffffffffffff008954945160081b16945160781b1694169116171717835551167fffffffffffffff000000000000ffffffffffffffffffffffffffffffffffffff78ffffffffffff0000000000000000000000000000000000000083549260981b169116179055565b6040519283528201527fa5ae833d0bb1dcd632d98a8b70973e8516812898e19bf27b70071ebc8dc52c0160403392a2005b606483604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152600e60248201527f7374616b65206f766572666c6f770000000000000000000000000000000000006044820152fd5b606483604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601260248201527f6e6f207374616b652073706563696669656400000000000000000000000000006044820152fd5b606482604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601c60248201527f63616e6e6f7420646563726561736520756e7374616b652074696d65000000006044820152fd5b606482604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601a60248201527f6d757374207370656369667920756e7374616b652064656c61790000000000006044820152fd5b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c576004357fffffffff00000000000000000000000000000000000000000000000000000000811680910361019c57807f60fc6b6e0000000000000000000000000000000000000000000000000000000060209214908115611ad6575b8115611aac575b8115611a82575b8115611a58575b506040519015158152f35b7f01ffc9a70000000000000000000000000000000000000000000000000000000091501482611a4d565b7f3e84f0210000000000000000000000000000000000000000000000000000000081149150611a46565b7fcf28ef970000000000000000000000000000000000000000000000000000000081149150611a3f565b7f915074d80000000000000000000000000000000000000000000000000000000081149150611a38565b3461019c576102007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5767ffffffffffffffff60043581811161019c573660238201121561019c57611b62903690602481600401359101612268565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc36016101c0811261019c5761014060405191611b9e83612155565b1261019c5760405192611bb0846121a0565b60243573ffffffffffffffffffffffffffffffffffffffff8116810361019c578452602093604435858201526064356040820152608435606082015260a435608082015260c43560a082015260e43560c08201526101043573ffffffffffffffffffffffffffffffffffffffff8116810361019c5760e08201526101243561010082015261014435610120820152825261016435848301526101843560408301526101a43560608301526101c43560808301526101e43590811161019c57611c7c9036906004016122c2565b905a3033036120f7578351606081015195603f5a0260061c61271060a0840151890101116120ce5760009681519182611ff0575b5050505090611cca915a9003608085015101923691612268565b925a90600094845193611cdc85613ccc565b9173ffffffffffffffffffffffffffffffffffffffff60e0870151168015600014611ea957505073ffffffffffffffffffffffffffffffffffffffff855116935b5a9003019360a06060820151910151016080860151850390818111611e95575b50508302604085015192818410600014611dce5750506003811015611da157600203611d79576113c99293508093611d7481613d65565b613cf6565b5050507fdeadaa51000000000000000000000000000000000000000000000000000000008152fd5b6024857f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b81611dde92979396940390613c98565b506003841015611e6857507f49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f60808683015192519473ffffffffffffffffffffffffffffffffffffffff865116948873ffffffffffffffffffffffffffffffffffffffff60e0890151169701519160405192835215898301528760408301526060820152a46113c9565b807f4e487b7100000000000000000000000000000000000000000000000000000000602492526021600452fd5b6064919003600a0204909301928780611d3d565b8095918051611eba575b5050611d1d565b6003861015611fc1576002860315611eb35760a088015190823b1561019c57600091611f2491836040519586809581947f7c627b210000000000000000000000000000000000000000000000000000000083528d60048401526080602484015260848301906123c6565b8b8b0260448301528b60648301520393f19081611fad575b50611fa65787893d610800808211611f9e575b506040519282828501016040528184528284013e610e7e6040519283927fad7954bc000000000000000000000000000000000000000000000000000000008452600484015260248301906123c6565b905083611f4f565b8980611eb3565b611fb89199506121bd565b6000978a611f3c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b91600092918380938c73ffffffffffffffffffffffffffffffffffffffff885116910192f115612023575b808080611cb0565b611cca929195503d6108008082116120c6575b5060405190888183010160405280825260008983013e805161205f575b5050600194909161201b565b7f1c4fada7374c0a9ee8841fc38afe82932dc0f8e69012e927f061a8bae611a20188870151918973ffffffffffffffffffffffffffffffffffffffff8551169401516120bc604051928392835260408d84015260408301906123c6565b0390a38680612053565b905088612036565b877fdeaddead000000000000000000000000000000000000000000000000000000006000526000fd5b606486604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601760248201527f4141393220696e7465726e616c2063616c6c206f6e6c790000000000000000006044820152fd5b60a0810190811067ffffffffffffffff82111761217157604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610140810190811067ffffffffffffffff82111761217157604052565b67ffffffffffffffff811161217157604052565b6060810190811067ffffffffffffffff82111761217157604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761217157604052565b67ffffffffffffffff811161217157601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b9291926122748261222e565b9161228260405193846121ed565b82948184528183011161019c578281602093846000960137010152565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361019c57565b9181601f8401121561019c5782359167ffffffffffffffff831161019c576020838186019501011161019c57565b6024359077ffffffffffffffffffffffffffffffffffffffffffffffff8216820361019c57565b9060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc83011261019c5760043567ffffffffffffffff9283821161019c578060238301121561019c57816004013593841161019c5760248460051b8301011161019c57602401919060243573ffffffffffffffffffffffffffffffffffffffff8116810361019c5790565b60005b8381106123b65750506000910152565b81810151838201526020016123a6565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602093612402815180928187528780880191016123a3565b0116010190565b91908201809211610b4f57565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610b4f5760010190565b91908203918211610b4f57565b3d1561247b573d906124618261222e565b9161246f60405193846121ed565b82523d6000602084013e565b606090565b604061248e8183018361284b565b90818351918237206124a3606084018461284b565b90818451918237209260c06124bb60e083018361284b565b908186519182372091845195602087019473ffffffffffffffffffffffffffffffffffffffff833516865260208301358789015260608801526080870152608081013560a087015260a081013582870152013560e08501526101009081850152835261012083019167ffffffffffffffff918484108385111761217157838252845190206101408501908152306101608601524661018086015260608452936101a00191821183831017612171575251902090565b67ffffffffffffffff81116121715760051b60200190565b9061259282612570565b6040906125a260405191826121ed565b8381527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06125d08295612570565b019160005b8381106125e25750505050565b60209082516125f081612155565b83516125fb816121a0565b600081526000849181838201528187820152816060818184015260809282848201528260a08201528260c08201528260e082015282610100820152826101208201528652818587015281898701528501528301528286010152016125d5565b805182101561266e5760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b919081101561266e5760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee18136030182121561019c570190565b9081602091031261019c575173ffffffffffffffffffffffffffffffffffffffff8116810361019c5790565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b7f2da466a7b24304f47e87fa2e1e5a81b9831ce54fec19055ce277ca2f39ba42c4602073ffffffffffffffffffffffffffffffffffffffff61278a3485613c98565b936040519485521692a2565b919081101561266e5760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa18136030182121561019c570190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561019c570180359067ffffffffffffffff821161019c57602001918160051b3603831361019c57565b3573ffffffffffffffffffffffffffffffffffffffff8116810361019c5790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561019c570180359067ffffffffffffffff821161019c5760200191813603831361019c57565b90357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561019c57016020813591019167ffffffffffffffff821161019c57813603831361019c57565b61012091813573ffffffffffffffffffffffffffffffffffffffff811680910361019c576129626129476129ba9561299b93855260208601356020860152612937604087018761289c565b9091806040880152860191612709565b612954606086018661289c565b908583036060870152612709565b6080840135608084015260a084013560a084015260c084013560c084015261298d60e085018561289c565b9084830360e0860152612709565b916129ac610100918281019061289c565b929091818503910152612709565b90565b60028054146129cc5760028055565b60046040517f3ee5aeb5000000000000000000000000000000000000000000000000000000008152fd5b926000905a93805194843573ffffffffffffffffffffffffffffffffffffffff811680910361019c5786526020850135602087015260808501356fffffffffffffffffffffffffffffffff90818116606089015260801c604088015260a086013560c088015260c086013590811661010088015260801c610120870152612a8060e086018661284b565b801561357b576034811061351d578060141161019c578060241161019c5760341161019c57602481013560801c60a0880152601481013560801c60808801523560601c60e08701525b612ad285612480565b60208301526040860151946effffffffffffffffffffffffffffff8660c08901511760608901511760808901511760a0890151176101008901511761012089015117116134bf57604087015160608801510160808801510160a08801510160c0880151016101008801510296835173ffffffffffffffffffffffffffffffffffffffff81511690612b66604085018561284b565b806131e4575b505060e0015173ffffffffffffffffffffffffffffffffffffffff1690600082156131ac575b6020612bd7918b828a01516000868a604051978896879586937f19822f7c00000000000000000000000000000000000000000000000000000000855260048501613db5565b0393f160009181613178575b50612c8b573d8c610800808311612c83575b50604051916020818401016040528083526000602084013e610e7e6040519283927f65c8fd4d000000000000000000000000000000000000000000000000000000008452600484015260606024840152600d60648401527f4141323320726576657274656400000000000000000000000000000000000000608484015260a0604484015260a48301906123c6565b915082612bf5565b9a92939495969798999a91156130f2575b509773ffffffffffffffffffffffffffffffffffffffff835116602084015190600052600160205260406000208160401c60005260205267ffffffffffffffff604060002091825492612cee84612416565b9055160361308d575a8503116130285773ffffffffffffffffffffffffffffffffffffffff60e0606093015116612d42575b509060a09184959697986040608096015260608601520135905a900301910152565b969550505a9683519773ffffffffffffffffffffffffffffffffffffffff60e08a01511680600052600060205260406000208054848110612fc3576080612dcd9a9b9c600093878094039055015192602089015183604051809d819582947f52b7512c0000000000000000000000000000000000000000000000000000000084528c60048501613db5565b039286f1978860009160009a612f36575b50612e86573d8b610800808311612e7e575b50604051916020818401016040528083526000602084013e610e7e6040519283927f65c8fd4d000000000000000000000000000000000000000000000000000000008452600484015260606024840152600d60648401527f4141333320726576657274656400000000000000000000000000000000000000608484015260a0604484015260a48301906123c6565b915082612df0565b9991929394959697989998925a900311612eab57509096959094939291906080612d20565b60a490604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602760448201527f41413336206f766572207061796d6173746572566572696669636174696f6e4760648201527f61734c696d6974000000000000000000000000000000000000000000000000006084820152fd5b915098503d90816000823e612f4b82826121ed565b604081838101031261019c5780519067ffffffffffffffff821161019c57828101601f83830101121561019c578181015191612f868361222e565b93612f9460405195866121ed565b838552820160208483850101011161019c57602092612fba9184808701918501016123a3565b01519838612dde565b60848b604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601e60448201527f41413331207061796d6173746572206465706f73697420746f6f206c6f7700006064820152fd5b608490604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601e60448201527f41413236206f76657220766572696669636174696f6e4761734c696d697400006064820152fd5b608482604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601a60448201527f4141323520696e76616c6964206163636f756e74206e6f6e63650000000000006064820152fd5b600052600060205260406000208054808c11613113578b9003905538612c9c565b608484604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601760448201527f41413231206469646e2774207061792070726566756e640000000000000000006064820152fd5b9091506020813d6020116131a4575b81613194602093836121ed565b8101031261019c57519038612be3565b3d9150613187565b508060005260006020526040600020548a81116000146131d75750612bd7602060005b915050612b92565b6020612bd7918c036131cf565b833b61345a57604088510151602060405180927f570e1a360000000000000000000000000000000000000000000000000000000082528260048301528160008161323260248201898b612709565b039273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000efc2c1444ebcc4db75e7613d20c6a62ff67a167c1690f1908115610db75760009161343b575b5073ffffffffffffffffffffffffffffffffffffffff811680156133d6578503613371573b1561330c5760141161019c5773ffffffffffffffffffffffffffffffffffffffff9183887fd51a9c61267aa6196961883ecf5ff2da6619c37dac0fa92122513fb32c032d2d604060e0958787602086015195510151168251913560601c82526020820152a391612b6c565b60848d604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602060448201527f4141313520696e6974436f6465206d757374206372656174652073656e6465726064820152fd5b60848e604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602060448201527f4141313420696e6974436f6465206d7573742072657475726e2073656e6465726064820152fd5b60848f604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601b60448201527f4141313320696e6974436f6465206661696c6564206f72204f4f4700000000006064820152fd5b613454915060203d602011610db057610da181836121ed565b3861327c565b60848d604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601f60448201527f414131302073656e64657220616c726561647920636f6e7374727563746564006064820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f41413934206761732076616c756573206f766572666c6f7700000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4141393320696e76616c6964207061796d6173746572416e64446174610000006044820152fd5b5050600060e087015260006080870152600060a0870152612ac9565b9092915a906060810151916040928351967fffffffff00000000000000000000000000000000000000000000000000000000886135d7606084018461284b565b600060038211613b9f575b7f8dd7712f0000000000000000000000000000000000000000000000000000000094168403613a445750505061379d6000926136b292602088015161363a8a5193849360208501528b602485015260648401906128ec565b90604483015203906136727fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0928381018352826121ed565b61379189519485927e42dc5300000000000000000000000000000000000000000000000000000000602085015261020060248501526102248401906123c6565b613760604484018b60806101a091805173ffffffffffffffffffffffffffffffffffffffff808251168652602082015160208701526040820151604087015260608201516060870152838201518487015260a082015160a087015260c082015160c087015260e08201511660e0860152610100808201519086015261012080910151908501526020810151610140850152604081015161016085015260608101516101808501520151910152565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc83820301610204840152876123c6565b039081018352826121ed565b6020918183809351910182305af1600051988652156137bf575b505050505050565b909192939495965060003d8214613a3a575b7fdeaddead00000000000000000000000000000000000000000000000000000000810361385b57608487878051917f220266b600000000000000000000000000000000000000000000000000000000835260048301526024820152600f60448201527f41413935206f7574206f662067617300000000000000000000000000000000006064820152fd5b7fdeadaa510000000000000000000000000000000000000000000000000000000091929395949650146000146138c55750506138a961389e6138b8935a90612443565b608085015190612409565b9083015183611d748295613d65565b905b3880808080806137b7565b909261395290828601518651907ff62676f440ff169a3a9afdbf812e89e7f95975ee8e5c31214ffdef631c5f479273ffffffffffffffffffffffffffffffffffffffff9580878551169401516139483d610800808211613a32575b508a519084818301018c5280825260008583013e8a805194859485528401528a8301906123c6565b0390a35a90612443565b916139636080860193845190612409565b926000905a94829488519761397789613ccc565b948260e08b0151168015600014613a1857505050875116955b5a9003019560a06060820151910151019051860390818111613a04575b5050840290850151928184106000146139de57505080611e68575090816139d89293611d7481613d65565b906138ba565b6139ee9082849397950390613c98565b50611e68575090826139ff92613cf6565b6139d8565b6064919003600a02049094019338806139ad565b90919892509751613a2a575b50613990565b955038613a24565b905038613920565b8181803e516137d1565b613b97945082935090613a8c917e42dc53000000000000000000000000000000000000000000000000000000006020613b6b9501526102006024860152610224850191612709565b613b3a604484018860806101a091805173ffffffffffffffffffffffffffffffffffffffff808251168652602082015160208701526040820151604087015260608201516060870152838201518487015260a082015160a087015260c082015160c087015260e08201511660e0860152610100808201519086015261012080910151908501526020810151610140850152604081015161016085015260608101516101808501520151910152565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc83820301610204840152846123c6565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018952886121ed565b60008761379d565b5081356135e2565b73ffffffffffffffffffffffffffffffffffffffff168015613c3a57600080809381935af1613bd4612450565b5015613bdc57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f41413931206661696c65642073656e6420746f2062656e6566696369617279006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f4141393020696e76616c69642062656e656669636961727900000000000000006044820152fd5b73ffffffffffffffffffffffffffffffffffffffff166000526000602052613cc66040600020918254612409565b80915590565b610120610100820151910151808214613cf257480180821015613ced575090565b905090565b5090565b9190917f49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f6080602083015192519473ffffffffffffffffffffffffffffffffffffffff946020868851169660e089015116970151916040519283526000602084015260408301526060820152a4565b60208101519051907f67b4fa9642f42120bf031f3051d1824b0fe25627945b27b8a6a65d5761d5482e60208073ffffffffffffffffffffffffffffffffffffffff855116940151604051908152a3565b613dcd604092959493956060835260608301906128ec565b9460208201520152565b8015613e6457600060408051613dec816121d1565b828152826020820152015273ffffffffffffffffffffffffffffffffffffffff811690604065ffffffffffff91828160a01c16908115613e5c575b60d01c92825191613e37836121d1565b8583528460208401521691829101524211908115613e5457509091565b905042109091565b839150613e27565b5060009060009056fea2646970667358221220b094fd69f04977ae9458e5ba422d01cd2d20dbcfca0992ff37f19aa07deec25464736f6c63430008170033", - "nonce": "0x1" - }, - "000f3df6d732807ef1319fb7b8bb8522d0beac02": { - "balance": "0x0", - "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500", - "nonce": "0x1" - }, - "02484cb50aac86eae85610d6f4bf026f30f6627d": { - "balance": "0x21e19e0c9bab2400000" - }, - "08135da0a343e492fa2d4282f2ae34c6c5cc1bbe": { - "balance": "0x21e19e0c9bab2400000" - }, - "09db0a93b389bef724429898f539aeb7ac2dd55f": { - "balance": "0x21e19e0c9bab2400000" - }, - "0b799c86a49deeb90402691f1041aa3af2d3c875": { - "balance": "0x0", - "nonce": "0x1" - }, - "13b0d85ccb8bf860b6b79af3029fca081ae9bef2": { - "balance": "0x0", - "code": "0x6080604052600436106100435760003560e01c8063076c37b21461004f578063481286e61461007157806356299481146100ba57806366cfa057146100da57600080fd5b3661004a57005b600080fd5b34801561005b57600080fd5b5061006f61006a366004610327565b6100fa565b005b34801561007d57600080fd5b5061009161008c366004610327565b61014a565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b3480156100c657600080fd5b506100916100d5366004610349565b61015d565b3480156100e657600080fd5b5061006f6100f53660046103ca565b610172565b61014582826040518060200161010f9061031a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082820381018352601f90910116604052610183565b505050565b600061015683836102e7565b9392505050565b600061016a8484846102f0565b949350505050565b61017d838383610183565b50505050565b6000834710156101f4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f437265617465323a20696e73756666696369656e742062616c616e636500000060448201526064015b60405180910390fd5b815160000361025f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f437265617465323a2062797465636f6465206c656e677468206973207a65726f60448201526064016101eb565b8282516020840186f5905073ffffffffffffffffffffffffffffffffffffffff8116610156576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f437265617465323a204661696c6564206f6e206465706c6f790000000000000060448201526064016101eb565b60006101568383305b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b61014e806104ad83390190565b6000806040838503121561033a57600080fd5b50508035926020909101359150565b60008060006060848603121561035e57600080fd5b8335925060208401359150604084013573ffffffffffffffffffffffffffffffffffffffff8116811461039057600080fd5b809150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000806000606084860312156103df57600080fd5b8335925060208401359150604084013567ffffffffffffffff8082111561040557600080fd5b818601915086601f83011261041957600080fd5b81358181111561042b5761042b61039b565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156104715761047161039b565b8160405282815289602084870101111561048a57600080fd5b826020860160208301376000602084830101528095505050505050925092509256fe608060405234801561001057600080fd5b5061012e806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063249cb3fa14602d575b600080fd5b603c603836600460b1565b604e565b60405190815260200160405180910390f35b60008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff16608857600060aa565b7fa2ef4600d742022d532d4747cb3547474667d6f13804902513b2ec01c848f4b45b9392505050565b6000806040838503121560c357600080fd5b82359150602083013573ffffffffffffffffffffffffffffffffffffffff8116811460ed57600080fd5b80915050925092905056fea26469706673582212205ffd4e6cede7d06a5daf93d48d0541fc68189eeb16608c1999a82063b666eb1164736f6c63430008130033a2646970667358221220fdc4a0fe96e3b21c108ca155438d37c9143fb01278a3c1d274948bad89c564ba64736f6c63430008130033", - "nonce": "0x1" - }, - "cd3b766ccdd6ae721141f452c550ca635964ce71": { - "balance": "0x21e19e0c9bab2400000" - }, - "dd2fd4581271e230360230f9337d5c0430bf44c0": { - "balance": "0x21e19e0c9bab2400000" - }, - "df37f81daad2b0327a0a50003740e1c935c70913": { - "balance": "0x21e19e0c9bab2400000" - }, - "df3e18d64bc6a983f673ab319ccae4f1a57c7097": { - "balance": "0x21e19e0c9bab2400000" - }, - "efc2c1444ebcc4db75e7613d20c6a62ff67a167c": { - "balance": "0x0", - "code": "0x6080600436101561000f57600080fd5b6000803560e01c63570e1a361461002557600080fd5b3461018a5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261018a576004359167ffffffffffffffff9081841161018657366023850112156101865783600401358281116101825736602482870101116101825780601411610182577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec810192808411610155577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f81600b8501160116830190838210908211176101555792846024819482600c60209a968b9960405286845289840196603889018837830101525193013560601c5af1908051911561014d575b5073ffffffffffffffffffffffffffffffffffffffff60405191168152f35b90503861012e565b6024857f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b8380fd5b8280fd5b80fdfea26469706673582212207adef8895ad3393b02fab10a111d85ea80ff35366aa43995f4ea20e67f29200664736f6c63430008170033", - "nonce": "0x1" - }, - "f39fd6e51aad88f6f4ce6ab8827279cfffb92266": { - "balance": "0x21e19e0c9bab2400000" - }, - "fabb0ac9d68b0b445fb7357272ff202c5651694a": { - "balance": "0x21e19e0c9bab2400000" - }, - "fb1bffc9d739b8d520daf37df666da4c687191ea": { - "balance": "0x0", - "code": "0x6080604052600436106101dc5760003560e01c8063affed0e011610102578063e19a9dd911610095578063f08a032311610064578063f08a032314611647578063f698da2514611698578063f8dc5dd9146116c3578063ffa1ad741461173e57610231565b8063e19a9dd91461139b578063e318b52b146113ec578063e75235b81461147d578063e86637db146114a857610231565b8063cc2f8452116100d1578063cc2f8452146110e8578063d4d9bdcd146111b5578063d8d11f78146111f0578063e009cfde1461132a57610231565b8063affed0e014610d94578063b4faba0914610dbf578063b63e800d14610ea7578063c4ca3a9c1461101757610231565b80635624b25b1161017a5780636a761202116101495780636a761202146109945780637d83297414610b50578063934f3a1114610bbf578063a0e67e2b14610d2857610231565b80635624b25b146107fb5780635ae6bd37146108b9578063610b592514610908578063694e80c31461095957610231565b80632f54bf6e116101b65780632f54bf6e146104d35780633408e4701461053a578063468721a7146105655780635229073f1461067a57610231565b80630d582f131461029e57806312fb68e0146102f95780632d9ad53d1461046c57610231565b36610231573373ffffffffffffffffffffffffffffffffffffffff167f3d0ce9bfc3ed7d6862dbb28b2dea94561fe714a1b4d019aa8af39730d1ad7c3d346040518082815260200191505060405180910390a2005b34801561023d57600080fd5b5060007f6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d560001b905080548061027257600080f35b36600080373360601b365260008060143601600080855af13d6000803e80610299573d6000fd5b3d6000f35b3480156102aa57600080fd5b506102f7600480360360408110156102c157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506117ce565b005b34801561030557600080fd5b5061046a6004803603608081101561031c57600080fd5b81019080803590602001909291908035906020019064010000000081111561034357600080fd5b82018360208201111561035557600080fd5b8035906020019184600183028401116401000000008311171561037757600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803590602001906401000000008111156103da57600080fd5b8201836020820111156103ec57600080fd5b8035906020019184600183028401116401000000008311171561040e57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929080359060200190929190505050611bbe565b005b34801561047857600080fd5b506104bb6004803603602081101561048f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612440565b60405180821515815260200191505060405180910390f35b3480156104df57600080fd5b50610522600480360360208110156104f657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612512565b60405180821515815260200191505060405180910390f35b34801561054657600080fd5b5061054f6125e4565b6040518082815260200191505060405180910390f35b34801561057157600080fd5b506106626004803603608081101561058857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001906401000000008111156105cf57600080fd5b8201836020820111156105e157600080fd5b8035906020019184600183028401116401000000008311171561060357600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803560ff1690602001909291905050506125f1565b60405180821515815260200191505060405180910390f35b34801561068657600080fd5b506107776004803603608081101561069d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001906401000000008111156106e457600080fd5b8201836020820111156106f657600080fd5b8035906020019184600183028401116401000000008311171561071857600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803560ff1690602001909291905050506126fc565b60405180831515815260200180602001828103825283818151815260200191508051906020019080838360005b838110156107bf5780820151818401526020810190506107a4565b50505050905090810190601f1680156107ec5780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b34801561080757600080fd5b5061083e6004803603604081101561081e57600080fd5b810190808035906020019092919080359060200190929190505050612732565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561087e578082015181840152602081019050610863565b50505050905090810190601f1680156108ab5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156108c557600080fd5b506108f2600480360360208110156108dc57600080fd5b81019080803590602001909291905050506127b9565b6040518082815260200191505060405180910390f35b34801561091457600080fd5b506109576004803603602081101561092b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506127d1565b005b34801561096557600080fd5b506109926004803603602081101561097c57600080fd5b8101908080359060200190929190505050612b63565b005b610b3860048036036101408110156109ab57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001906401000000008111156109f257600080fd5b820183602082011115610a0457600080fd5b80359060200191846001830284011164010000000083111715610a2657600080fd5b9091929391929390803560ff169060200190929190803590602001909291908035906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190640100000000811115610ab257600080fd5b820183602082011115610ac457600080fd5b80359060200191846001830284011164010000000083111715610ae657600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050612c9d565b60405180821515815260200191505060405180910390f35b348015610b5c57600080fd5b50610ba960048036036040811015610b7357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050612edc565b6040518082815260200191505060405180910390f35b348015610bcb57600080fd5b50610d2660048036036060811015610be257600080fd5b810190808035906020019092919080359060200190640100000000811115610c0957600080fd5b820183602082011115610c1b57600080fd5b80359060200191846001830284011164010000000083111715610c3d57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929080359060200190640100000000811115610ca057600080fd5b820183602082011115610cb257600080fd5b80359060200191846001830284011164010000000083111715610cd457600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050612f01565b005b348015610d3457600080fd5b50610d3d612f90565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610d80578082015181840152602081019050610d65565b505050509050019250505060405180910390f35b348015610da057600080fd5b50610da9613139565b6040518082815260200191505060405180910390f35b348015610dcb57600080fd5b50610ea560048036036040811015610de257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190640100000000811115610e1f57600080fd5b820183602082011115610e3157600080fd5b80359060200191846001830284011164010000000083111715610e5357600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929050505061313f565b005b348015610eb357600080fd5b506110156004803603610100811015610ecb57600080fd5b8101908080359060200190640100000000811115610ee857600080fd5b820183602082011115610efa57600080fd5b80359060200191846020830284011164010000000083111715610f1c57600080fd5b909192939192939080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190640100000000811115610f6757600080fd5b820183602082011115610f7957600080fd5b80359060200191846001830284011164010000000083111715610f9b57600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613161565b005b34801561102357600080fd5b506110d26004803603608081101561103a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561108157600080fd5b82018360208201111561109357600080fd5b803590602001918460018302840111640100000000831117156110b557600080fd5b9091929391929390803560ff16906020019092919050505061331f565b6040518082815260200191505060405180910390f35b3480156110f457600080fd5b506111416004803603604081101561110b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050613447565b60405180806020018373ffffffffffffffffffffffffffffffffffffffff168152602001828103825284818151815260200191508051906020019060200280838360005b838110156111a0578082015181840152602081019050611185565b50505050905001935050505060405180910390f35b3480156111c157600080fd5b506111ee600480360360208110156111d857600080fd5b8101908080359060200190929190505050613639565b005b3480156111fc57600080fd5b50611314600480360361014081101561121457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561125b57600080fd5b82018360208201111561126d57600080fd5b8035906020019184600183028401116401000000008311171561128f57600080fd5b9091929391929390803560ff169060200190929190803590602001909291908035906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506137d8565b6040518082815260200191505060405180910390f35b34801561133657600080fd5b506113996004803603604081101561134d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613805565b005b3480156113a757600080fd5b506113ea600480360360208110156113be57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613b96565b005b3480156113f857600080fd5b5061147b6004803603606081101561140f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613c1a565b005b34801561148957600080fd5b5061149261428c565b6040518082815260200191505060405180910390f35b3480156114b457600080fd5b506115cc60048036036101408110156114cc57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561151357600080fd5b82018360208201111561152557600080fd5b8035906020019184600183028401116401000000008311171561154757600080fd5b9091929391929390803560ff169060200190929190803590602001909291908035906020019092919080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050614296565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561160c5780820151818401526020810190506115f1565b50505050905090810190601f1680156116395780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561165357600080fd5b506116966004803603602081101561166a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061443e565b005b3480156116a457600080fd5b506116ad61449f565b6040518082815260200191505060405180910390f35b3480156116cf57600080fd5b5061173c600480360360608110156116e657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061451d565b005b34801561174a57600080fd5b50611753614950565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015611793578082015181840152602081019050611778565b50505050905090810190601f1680156117c05780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6117d6614989565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141580156118405750600173ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b801561187857503073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b6118ea576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146119eb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303400000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60026000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508160026000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506003600081548092919060010191905055507f9465fa0c962cc76958e6373a993326400c1c94f8be2fe3a952adfa7f60b2ea2682604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a18060045414611bba57611bb981612b63565b5b5050565b611bd2604182614a2c90919063ffffffff16565b82511015611c48576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b6000808060008060005b8681101561243457611c648882614a66565b80945081955082965050505060008460ff16141561206d578260001c9450611c96604188614a2c90919063ffffffff16565b8260001c1015611d0e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b8751611d2760208460001c614a9590919063ffffffff16565b1115611d9b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323200000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60006020838a01015190508851611dd182611dc360208760001c614a9590919063ffffffff16565b614a9590919063ffffffff16565b1115611e45576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60606020848b010190506320c13b0b60e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168773ffffffffffffffffffffffffffffffffffffffff166320c13b0b8d846040518363ffffffff1660e01b8152600401808060200180602001838103835285818151815260200191508051906020019080838360005b83811015611ee7578082015181840152602081019050611ecc565b50505050905090810190601f168015611f145780820380516001836020036101000a031916815260200191505b50838103825284818151815260200191508051906020019080838360005b83811015611f4d578082015181840152602081019050611f32565b50505050905090810190601f168015611f7a5780820380516001836020036101000a031916815260200191505b5094505050505060206040518083038186803b158015611f9957600080fd5b505afa158015611fad573d6000803e3d6000fd5b505050506040513d6020811015611fc357600080fd5b81019080805190602001909291905050507bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614612066576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323400000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b50506122b2565b60018460ff161415612181578260001c94508473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16148061210a57506000600860008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008c81526020019081526020016000205414155b61217c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323500000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b6122b1565b601e8460ff1611156122495760018a60405160200180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c018281526020019150506040516020818303038152906040528051906020012060048603858560405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015612238573d6000803e3d6000fd5b5050506020604051035194506122b0565b60018a85858560405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa1580156122a3573d6000803e3d6000fd5b5050506020604051035194505b5b5b8573ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161180156123795750600073ffffffffffffffffffffffffffffffffffffffff16600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b80156123b25750600173ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614155b612424576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330323600000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b8495508080600101915050611c52565b50505050505050505050565b60008173ffffffffffffffffffffffffffffffffffffffff16600173ffffffffffffffffffffffffffffffffffffffff161415801561250b5750600073ffffffffffffffffffffffffffffffffffffffff16600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b9050919050565b6000600173ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141580156125dd5750600073ffffffffffffffffffffffffffffffffffffffff16600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b9050919050565b6000804690508091505090565b60007fb648d3644f584ed1c2232d53c46d87e693586486ad0d1175f8656013110b714e3386868686604051808673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018060200183600181111561266b57fe5b8152602001828103825284818151815260200191508051906020019080838360005b838110156126a857808201518184015260208101905061268d565b50505050905090810190601f1680156126d55780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390a16126f285858585614ab4565b9050949350505050565b6000606061270c868686866125f1565b915060405160203d0181016040523d81523d6000602083013e8091505094509492505050565b606060006020830267ffffffffffffffff8111801561275057600080fd5b506040519080825280601f01601f1916602001820160405280156127835781602001600182028036833780820191505090505b50905060005b838110156127ae57808501548060208302602085010152508080600101915050612789565b508091505092915050565b60076020528060005260406000206000915090505481565b6127d9614989565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141580156128435750600173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b6128b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146129b6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303200000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60016000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060016000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507fecdf3a3effea5783a3c4c2140e677577666428d44ed9d474a0b3a4c9943f844081604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a150565b612b6b614989565b600354811115612be3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b6001811015612c5a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303200000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b806004819055507f610f7ff2b304ae8903c3de74c60c6ab1f7d6226b3f52c5161905bb5ad4039c936004546040518082815260200191505060405180910390a150565b6000606060055433600454604051602001808481526020018373ffffffffffffffffffffffffffffffffffffffff168152602001828152602001935050505060405160208183030381529060405290507f66753cd2356569ee081232e3be8909b950e0a76c1f8460c3a5e3c2be32b11bed8d8d8d8d8d8d8d8d8d8d8d8c604051808d73ffffffffffffffffffffffffffffffffffffffff1681526020018c8152602001806020018a6001811115612d5057fe5b81526020018981526020018881526020018781526020018673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff168152602001806020018060200184810384528e8e82818152602001925080828437600081840152601f19601f820116905080830192505050848103835286818151815260200191508051906020019080838360005b83811015612e0a578082015181840152602081019050612def565b50505050905090810190601f168015612e375780820380516001836020036101000a031916815260200191505b50848103825285818151815260200191508051906020019080838360005b83811015612e70578082015181840152602081019050612e55565b50505050905090810190601f168015612e9d5780820380516001836020036101000a031916815260200191505b509f5050505050505050505050505050505060405180910390a1612eca8d8d8d8d8d8d8d8d8d8d8d614c9a565b9150509b9a5050505050505050505050565b6008602052816000526040600020602052806000526040600020600091509150505481565b6000600454905060008111612f7e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330303100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b612f8a84848484611bbe565b50505050565b6060600060035467ffffffffffffffff81118015612fad57600080fd5b50604051908082528060200260200182016040528015612fdc5781602001602082028036833780820191505090505b50905060008060026000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505b600173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614613130578083838151811061308757fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508180600101925050613046565b82935050505090565b60055481565b600080825160208401855af4806000523d6020523d600060403e60403d016000fd5b6131ac8a8a80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050896151d7565b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16146131ea576131e9846156d7565b5b6132388787878080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050615706565b60008211156132525761325082600060018685615941565b505b3373ffffffffffffffffffffffffffffffffffffffff167f141df868a6331af528e38c83b7aa03edc19be66e37ae67f9285bf4f8e3c6a1a88b8b8b8b8960405180806020018581526020018473ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1681526020018281038252878782818152602001925060200280828437600081840152601f19601f820116905080830192505050965050505050505060405180910390a250505050505050505050565b6000805a9050613376878787878080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050865a615b47565b61337f57600080fd5b60005a8203905080604051602001808281526020019150506040516020818303038152906040526040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561340c5780820151818401526020810190506133f1565b50505050905090810190601f1680156134395780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b606060008267ffffffffffffffff8111801561346257600080fd5b506040519080825280602002602001820160405280156134915781602001602082028036833780820191505090505b509150600080600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141580156135645750600173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b801561356f57508482105b1561362a578084838151811061358157fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600160008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081806001019250506134fa565b80925081845250509250929050565b600073ffffffffffffffffffffffffffffffffffffffff16600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561373b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330333000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b6001600860003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000838152602001908152602001600020819055503373ffffffffffffffffffffffffffffffffffffffff16817ff2a0eb156472d1440255b0d7c1e19cc07115d1051fe605b0dce69acfec884d9c60405160405180910390a350565b60006137ed8c8c8c8c8c8c8c8c8c8c8c614296565b8051906020012090509b9a5050505050505050505050565b61380d614989565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141580156138775750600173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b6138e9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff16600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146139e9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600160008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507faab4fa2b463f581b2b32cb3b7e3b704b9ce37cc209b5fb4d77e593ace405427681604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a15050565b613b9e614989565b60007f4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c860001b90508181557f1151116914515bc0891ff9047a6cb32cf902546f83066499bcf8ba33d2353fa282604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a15050565b613c22614989565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614158015613c8c5750600173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b8015613cc457503073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b613d36576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614613e37576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303400000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614158015613ea15750600173ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b613f13576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff16600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614614013576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303500000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507ff8d49fc529812e9a7c5c50e69c20f0dccc0db8fa95c98bc58cc9a4f1c1299eaf82604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a17f9465fa0c962cc76958e6373a993326400c1c94f8be2fe3a952adfa7f60b2ea2681604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a1505050565b6000600454905090565b606060007fbb8310d486368db6bd6f849402fdd73ad53d316b5a4b2644ad6efe0f941286d860001b8d8d8d8d60405180838380828437808301925050509250505060405180910390208c8c8c8c8c8c8c604051602001808c81526020018b73ffffffffffffffffffffffffffffffffffffffff1681526020018a815260200189815260200188600181111561432757fe5b81526020018781526020018681526020018581526020018473ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019b505050505050505050505050604051602081830303815290604052805190602001209050601960f81b600160f81b6143b361449f565b8360405160200180857effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152600101847effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526001018381526020018281526020019450505050506040516020818303038152906040529150509b9a5050505050505050505050565b614446614989565b61444f816156d7565b7f5ac6c46c93c8d0e53714ba3b53db3e7c046da994313d7ed0d192028bc7c228b081604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a150565b60007f47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a7946921860001b6144cd6125e4565b30604051602001808481526020018381526020018273ffffffffffffffffffffffffffffffffffffffff168152602001935050505060405160208183030381529060405280519060200120905090565b614525614989565b8060016003540310156145a0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415801561460a5750600173ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b61467c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff16600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461477c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303500000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600360008154809291906001900391905055507ff8d49fc529812e9a7c5c50e69c20f0dccc0db8fa95c98bc58cc9a4f1c1299eaf82604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a1806004541461494b5761494a81612b63565b5b505050565b6040518060400160405280600581526020017f312e332e3000000000000000000000000000000000000000000000000000000081525081565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614614a2a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330333100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b565b600080831415614a3f5760009050614a60565b6000828402905082848281614a5057fe5b0414614a5b57600080fd5b809150505b92915050565b60008060008360410260208101860151925060408101860151915060ff60418201870151169350509250925092565b600080828401905083811015614aaa57600080fd5b8091505092915050565b6000600173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614158015614b7f5750600073ffffffffffffffffffffffffffffffffffffffff16600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614155b614bf1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303400000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b614bfe858585855a615b47565b90508015614c4e573373ffffffffffffffffffffffffffffffffffffffff167f6895c13664aa4f67288b25d7a21d7aaa34916e355fb9b6fae0a139a9085becb860405160405180910390a2614c92565b3373ffffffffffffffffffffffffffffffffffffffff167facd2c8702804128fdb0db2bb49f6d127dd0181c13fd45dbfe16de0930e2bd37560405160405180910390a25b949350505050565b6000806000614cb48e8e8e8e8e8e8e8e8e8e600554614296565b905060056000815480929190600101919050555080805190602001209150614cdd828286612f01565b506000614ce8615b93565b9050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614614ece578073ffffffffffffffffffffffffffffffffffffffff166375f0bb528f8f8f8f8f8f8f8f8f8f8f336040518d63ffffffff1660e01b8152600401808d73ffffffffffffffffffffffffffffffffffffffff1681526020018c8152602001806020018a6001811115614d8b57fe5b81526020018981526020018881526020018781526020018673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff168152602001806020018473ffffffffffffffffffffffffffffffffffffffff16815260200183810383528d8d82818152602001925080828437600081840152601f19601f820116905080830192505050838103825285818151815260200191508051906020019080838360005b83811015614e5d578082015181840152602081019050614e42565b50505050905090810190601f168015614e8a5780820380516001836020036101000a031916815260200191505b509e505050505050505050505050505050600060405180830381600087803b158015614eb557600080fd5b505af1158015614ec9573d6000803e3d6000fd5b505050505b6101f4614ef56109c48b01603f60408d0281614ee657fe5b04615bc490919063ffffffff16565b015a1015614f6b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330313000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60005a9050614fd48f8f8f8f8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050508e60008d14614fc9578e614fcf565b6109c45a035b615b47565b9350614fe95a82615bde90919063ffffffff16565b90508380614ff8575060008a14155b80615004575060008814155b615076576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330313300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b6000808911156150905761508d828b8b8b8b615941565b90505b84156150da577f442e715f626346e8c54381002da614f62bee8d27386535b2521ec8540898556e8482604051808381526020018281526020019250505060405180910390a161511a565b7f23428b18acfb3ea64b08dc0c1d296ea9c09702c09083ca5272e64d115b687d238482604051808381526020018281526020019250505060405180910390a15b5050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146151c6578073ffffffffffffffffffffffffffffffffffffffff16639327136883856040518363ffffffff1660e01b815260040180838152602001821515815260200192505050600060405180830381600087803b1580156151ad57600080fd5b505af11580156151c1573d6000803e3d6000fd5b505050505b50509b9a5050505050505050505050565b60006004541461524f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b81518111156152c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600181101561533d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303200000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b60006001905060005b835181101561564357600084828151811061535d57fe5b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141580156153d15750600173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b801561540957503073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b801561544157508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614155b6154b3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303300000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146155b4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475332303400000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b80600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550809250508080600101915050615346565b506001600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550825160038190555081600481905550505050565b60007f6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d560001b90508181555050565b600073ffffffffffffffffffffffffffffffffffffffff1660016000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614615808576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475331303000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b6001806000600173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161461593d576158ca8260008360015a615b47565b61593c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330303000000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b5b5050565b600080600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161461597e5782615980565b325b9050600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161415615a98576159ea3a86106159c7573a6159c9565b855b6159dc888a614a9590919063ffffffff16565b614a2c90919063ffffffff16565b91508073ffffffffffffffffffffffffffffffffffffffff166108fc839081150290604051600060405180830381858888f19350505050615a93576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330313100000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b615b3d565b615abd85615aaf888a614a9590919063ffffffff16565b614a2c90919063ffffffff16565b9150615aca848284615bfe565b615b3c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f475330313200000000000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b5b5095945050505050565b6000600180811115615b5557fe5b836001811115615b6157fe5b1415615b7a576000808551602087018986f49050615b8a565b600080855160208701888a87f190505b95945050505050565b6000807f4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c860001b9050805491505090565b600081831015615bd45781615bd6565b825b905092915050565b600082821115615bed57600080fd5b600082840390508091505092915050565b60008063a9059cbb8484604051602401808373ffffffffffffffffffffffffffffffffffffffff168152602001828152602001925050506040516020818303038152906040529060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050602060008251602084016000896127105a03f13d60008114615ca55760208114615cad5760009350615cb8565b819350615cb8565b600051158215171593505b505050939250505056fea2646970667358221220047fac33099ca576d1c4f1ac6a8abdb0396e42ad6a397d2cb2f4dc1624cc0c5b64736f6c63430007060033", - "nonce": "0x1" - } - }, - "baseFeePerGas": "0x1", - "blobGasUsed": "0x0", - "coinbase": "0x4200000000000000000000000000000000000011", - "config": { - "arrowGlacierBlock": 0, - "bedrockBlock": 0, - "berlinBlock": 0, - "byzantiumBlock": 0, - "cancunTime": 0, - "canyonTime": 0, - "chainId": 901, - "constantinopleBlock": 0, - "depositContractAddress": "0x4242424242424242424242424242424242424242", - "ecotoneTime": 0, - "eip150Block": 0, - "eip155Block": 0, - "eip158Block": 0, - "fjordTime": 0, - "graniteTime": 0, - "grayGlacierBlock": 0, - "homesteadBlock": 0, - "istanbulBlock": 0, - "londonBlock": 0, - "mergeNetsplitBlock": 0, - "muirGlacierBlock": 0, - "blobSchedule": { - "cancun": { - "target": 3, - "max": 6, - "baseFeeUpdateFraction": 3338477 - }, - "prague": { - "target": 6, - "max": 9, - "baseFeeUpdateFraction": 5007716 - }, - "osaka": { - "target": 9, - "max": 12, - "baseFeeUpdateFraction": 5007716 - } - }, - "optimism": { - "eip1559Denominator": 50, - "eip1559DenominatorCanyon": 250, - "eip1559Elasticity": 6 - }, - "petersburgBlock": 0, - "regolithTime": 0, - "shanghaiTime": 0, - "terminalTotalDifficulty": 0, - "terminalTotalDifficultyPassed": true - }, - "difficulty": "0x0", - "excessBlobGas": "0x0", - "extraData": "0x", - "gasLimit": "0x1c9c380", - "gasUsed": "0x0", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "nonce": "0x0", - "number": "0x0", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "timestamp": "" -} \ No newline at end of file diff --git a/tests/integration/testdata/jwt_secret.hex b/tests/integration/testdata/jwt_secret.hex deleted file mode 100644 index 6e72091c..00000000 --- a/tests/integration/testdata/jwt_secret.hex +++ /dev/null @@ -1 +0,0 @@ -688f5d737bad920bdfb2fc2f488d6b6209eebda1dae949a8de91398d932c517a diff --git a/tests/integration/testdata/p2p_secret.hex b/tests/integration/testdata/p2p_secret.hex deleted file mode 100644 index 499ea6cd..00000000 --- a/tests/integration/testdata/p2p_secret.hex +++ /dev/null @@ -1 +0,0 @@ -a11ac89899cd86e36b6fb881ec1255b8a92a688790b7d950f8b7d8dd626671fb diff --git a/tests/no_tx_pool.rs b/tests/no_tx_pool.rs index 1396ec3f..02328c34 100644 --- a/tests/no_tx_pool.rs +++ b/tests/no_tx_pool.rs @@ -1,21 +1,23 @@ -use alloy_primitives::B256; -use serde_json::Value; -use std::sync::{Arc, Mutex}; -use std::time::Duration; - mod integration; use crate::integration::RollupBoostTestHarnessBuilder; // use crate::server::ExecutionMode; -use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; #[tokio::test] -async fn test_integration_simple() -> eyre::Result<()> { - let harness = RollupBoostTestHarnessBuilder::new("test_integration_simple") +async fn simple() -> eyre::Result<()> { + let harness = RollupBoostTestHarnessBuilder::new("test_integration_no_tx_pool") .build() .await?; let mut block_generator = harness.get_block_generator().await?; + // start creating 5 empty blocks which are processed by the L2 builder + for _ in 0..5 { + let (_block, block_creator) = block_generator.generate_block(true).await?; + assert!(block_creator.is_l2(), "Block creator should be l2"); + } + + // process 5 more non empty blocks which are processed by the builder. + // The builder should be on sync because it has received the new payload requests from rollup-boost. for _ in 0..5 { let (_block, block_creator) = block_generator.generate_block(false).await?; assert!( diff --git a/tests/remote_builder_down.rs b/tests/remote_builder_down.rs index 377266bd..16d0368d 100644 --- a/tests/remote_builder_down.rs +++ b/tests/remote_builder_down.rs @@ -1,17 +1,14 @@ -use alloy_primitives::B256; -use serde_json::Value; -use std::sync::{Arc, Mutex}; +mod integration; + use std::time::Duration; -mod integration; +use testcontainers::core::client::docker_client_instance; use crate::integration::RollupBoostTestHarnessBuilder; -// use crate::server::ExecutionMode; -use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; #[tokio::test] -async fn test_integration_remote_builder_down() -> eyre::Result<()> { - let harness = RollupBoostTestHarnessBuilder::new("test_integration_remote_builder_down") +async fn remote_builder_down() -> eyre::Result<()> { + let harness = RollupBoostTestHarnessBuilder::new("remote_builder_down") .build() .await?; let mut block_generator = harness.get_block_generator().await?; @@ -25,7 +22,9 @@ async fn test_integration_remote_builder_down() -> eyre::Result<()> { } // stop the builder - harness.builder.stop().await?; + let client = docker_client_instance().await?; + client.pause_container(harness.builder.id()).await?; + tokio::time::sleep(Duration::from_secs(2)).await; // create 3 new blocks that are processed by the l2 builder for _ in 0..3 { @@ -34,7 +33,7 @@ async fn test_integration_remote_builder_down() -> eyre::Result<()> { } // start the builder again - harness.builder.start().await?; + client.unpause_container(harness.builder.id()).await?; // the next block is computed by the l2 builder because the builder is not synced with the previous 3 blocks // But, once the builder receives the FCU request from rollup-boost, it will sync up the blocks with the @@ -43,11 +42,15 @@ async fn test_integration_remote_builder_down() -> eyre::Result<()> { assert!(block_creator.is_l2(), "Block creator should be l2"); // Note: We might add some sleep here if the builder is not synced in time. I have not seen this happen yet. + tokio::time::sleep(Duration::from_secs(2)).await; // create 3 new blocks that are processed by the l2 builder because the builder is not synced with the previous 3 blocks for _ in 0..3 { let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!(block_creator.is_builder(), "Block creator should be l2"); + assert!( + block_creator.is_builder(), + "Block creator should be builder" + ); } Ok(()) diff --git a/tests/simple.rs b/tests/simple.rs index 11149f48..17841b92 100644 --- a/tests/simple.rs +++ b/tests/simple.rs @@ -1,19 +1,10 @@ -use alloy_primitives::B256; use integration::RollupBoostTestHarnessBuilder; -use rollup_boost::ExecutionMode; -use serde_json::Value; -use std::sync::{Arc, Mutex}; -use std::time::Duration; - -use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; mod integration; #[tokio::test] async fn test_integration_simple() -> eyre::Result<()> { - let harness = RollupBoostTestHarnessBuilder::new("test_integration_simple") - .build() - .await?; + let harness = RollupBoostTestHarnessBuilder::new("simple").build().await?; let mut block_generator = harness.get_block_generator().await?; for _ in 0..5 { From bfc16a03d657ada5508618d6d8b5c3b01ecb14d4 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Thu, 3 Apr 2025 22:45:56 -0700 Subject: [PATCH 08/20] wip --- Makefile | 2 +- src/client/rpc.rs | 34 +++++++++--- src/proxy.rs | 3 +- tests/builder_full_delay.rs | 51 +++++++++++++---- tests/builder_returns_incorrect_block.rs | 58 +++++++++++++------- tests/execution_mode.rs | 32 ++++++++--- tests/integration/containers/rollup_boost.rs | 7 ++- tests/integration/mod.rs | 34 +++++------- tests/integration/proxy.rs | 21 +++++-- tests/no_tx_pool.rs | 2 +- tests/remote_builder_down.rs | 11 +--- tests/simple.rs | 2 +- 12 files changed, 168 insertions(+), 89 deletions(-) diff --git a/Makefile b/Makefile index 61b48ea9..31254594 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ test: ## Run the tests for rollup-boost .PHONY: test-integration test-integration: ## Run the integration tests for rollup-boost - cargo test --verbose --features integration -- integration::integration_test::tests + cargo test .PHONY: lt lt: lint test ## Run "lint" and "test" diff --git a/src/client/rpc.rs b/src/client/rpc.rs index bdf863e7..9775effb 100644 --- a/src/client/rpc.rs +++ b/src/client/rpc.rs @@ -371,15 +371,29 @@ mod tests { use predicates::prelude::*; use reth_rpc_layer::{AuthLayer, JwtAuthValidator}; use std::net::SocketAddr; + use std::net::TcpListener; use std::result::Result; use std::str::FromStr; use super::*; - const AUTH_PORT: u32 = 8550; + // const AUTH_PORT: u32 = 8550; const AUTH_ADDR: &str = "0.0.0.0"; const SECRET: &str = "f79ae8046bc11c9927afe911db7143c51a806c4a537cc08e0d37140b0192f430"; + fn get_available_port() -> u16 { + loop { + let port = rand::random::() % 20000 + 1000; + if port_is_available(port) { + return port; + } + } + } + + fn port_is_available(port: u16) -> bool { + TcpListener::bind(("127.0.0.1", port)).is_ok() + } + #[test] fn test_invalid_args() { let mut cmd = Command::cargo_bin("rollup-boost").unwrap(); @@ -392,21 +406,22 @@ mod tests { #[tokio::test] async fn valid_jwt() { + let port = get_available_port(); let secret = JwtSecret::from_hex(SECRET).unwrap(); - - let auth_rpc = Uri::from_str(&format!("http://{}:{}", AUTH_ADDR, AUTH_PORT)).unwrap(); + let auth_rpc = Uri::from_str(&format!("http://{}:{}", AUTH_ADDR, port)).unwrap(); let client = RpcClient::new(auth_rpc, secret, 1000, PayloadSource::L2).unwrap(); - let response = send_request(client.auth_client).await; + let response = send_request(client.auth_client, port).await; assert!(response.is_ok()); assert_eq!(response.unwrap(), "You are the dark lord"); } #[tokio::test] async fn invalid_jwt() { + let port = get_available_port(); let secret = JwtSecret::random(); - let auth_rpc = Uri::from_str(&format!("http://{}:{}", AUTH_ADDR, AUTH_PORT)).unwrap(); + let auth_rpc = Uri::from_str(&format!("http://{}:{}", AUTH_ADDR, port)).unwrap(); let client = RpcClient::new(auth_rpc, secret, 1000, PayloadSource::L2).unwrap(); - let response = send_request(client.auth_client).await; + let response = send_request(client.auth_client, port).await; assert!(response.is_err()); assert!(matches!( response.unwrap_err(), @@ -417,8 +432,9 @@ mod tests { async fn send_request( client: HttpClient>, + port: u16, ) -> Result { - let server = spawn_server().await; + let server = spawn_server(port).await; let response = client .request::("greet_melkor", rpc_params![]) @@ -431,9 +447,9 @@ mod tests { } /// Spawn a new RPC server equipped with a `JwtLayer` auth middleware. - async fn spawn_server() -> ServerHandle { + async fn spawn_server(port: u16) -> ServerHandle { let secret = JwtSecret::from_hex(SECRET).unwrap(); - let addr = format!("{AUTH_ADDR}:{AUTH_PORT}"); + let addr = format!("{AUTH_ADDR}:{port}"); let validator = JwtAuthValidator::new(secret); let layer = AuthLayer::new(validator); let middleware = tower::ServiceBuilder::default().layer(layer); diff --git a/src/proxy.rs b/src/proxy.rs index 7afd478d..fabba3d0 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -673,7 +673,6 @@ mod tests { #[tokio::test] async fn test_forward_miner_set_gas_limit() -> eyre::Result<()> { - tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; let test_harness = TestHarness::new().await?; let gas_limit = U128::ZERO; @@ -686,6 +685,8 @@ mod tests { let expected_price = json!(gas_limit); + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; + // Assert the builder received the correct payload let builder = &test_harness.builder; let builder_requests = builder.requests.lock().unwrap(); diff --git a/tests/builder_full_delay.rs b/tests/builder_full_delay.rs index 7ffd3747..b3262d95 100644 --- a/tests/builder_full_delay.rs +++ b/tests/builder_full_delay.rs @@ -1,21 +1,40 @@ +use futures::FutureExt; use integration::RollupBoostTestHarnessBuilder; +use integration::proxy::ProxyHandler; use serde_json::Value; +use std::pin::Pin; use std::sync::{Arc, Mutex}; use std::time::Duration; mod integration; +// Create a dynamic handler that delays all the calls by 2 seconds +struct DelayHandler { + delay: Arc>, +} + +impl ProxyHandler for DelayHandler { + fn handle( + &self, + _method: String, + _params: Value, + _result: Value, + ) -> Pin> + Send>> { + let delay = *self.delay.lock().unwrap(); + async move { + tokio::time::sleep(delay).await; + None + } + .boxed() + } +} + #[tokio::test] async fn builder_full_delay() -> eyre::Result<()> { - // Create a dynamic handler that delays all the calls by 2 seconds let delay = Arc::new(Mutex::new(Duration::from_secs(0))); - let delay_for_handler = delay.clone(); - let handler = Box::new(move |_method: &str, _params: Value, _result: Value| { - let delay = delay_for_handler.lock().unwrap(); - // sleep the amount of time specified in the delay - std::thread::sleep(*delay); - None + let handler = Arc::new(DelayHandler { + delay: delay.clone(), }); // This integration test checks that if the builder has a general delay in processing ANY of the requests, @@ -25,7 +44,19 @@ async fn builder_full_delay() -> eyre::Result<()> { .build() .await?; - let mut block_generator = harness.get_block_generator().await?; + let mut block_generator = harness.block_generator().await?; + + // create 3 blocks that are processed by the builder + for _ in 0..3 { + let (_block, block_creator) = block_generator.generate_block(false).await?; + assert!( + block_creator.is_builder(), + "Block creator should be the builder" + ); + } + + // add the delay + // *delay.lock().unwrap() = Duration::from_secs(1); // create 3 blocks that are processed by the builder for _ in 0..3 { @@ -37,12 +68,12 @@ async fn builder_full_delay() -> eyre::Result<()> { } // add the delay - *delay.lock().unwrap() = Duration::from_secs(2); + *delay.lock().unwrap() = Duration::from_secs(5); // create 3 blocks that are processed by the builder for _ in 0..3 { let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!(block_creator.is_l2(), "Block creator should be the builder"); + assert!(block_creator.is_l2(), "Block creator should be the l2"); } Ok(()) diff --git a/tests/builder_returns_incorrect_block.rs b/tests/builder_returns_incorrect_block.rs index 9cc49861..d68cd8e4 100644 --- a/tests/builder_returns_incorrect_block.rs +++ b/tests/builder_returns_incorrect_block.rs @@ -1,39 +1,55 @@ +use std::{pin::Pin, sync::Arc}; + use alloy_primitives::B256; -use integration::RollupBoostTestHarnessBuilder; +use futures::FutureExt as _; +use integration::{RollupBoostTestHarnessBuilder, proxy::ProxyHandler}; use serde_json::Value; use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; mod integration; -#[tokio::test] -async fn builder_returns_incorrect_block() -> eyre::Result<()> { - // Test that the builder returns a block with an incorrect state root and that rollup-boost - // does not process it. - let handler = Box::new(move |method: &str, _params: Value, _result: Value| { - if method != "engine_getPayloadV3" { - return None; - } +struct Handler; + +impl ProxyHandler for Handler { + fn handle( + &self, + method: String, + _params: Value, + _result: Value, + ) -> Pin> + Send>> { + async move { + if method != "engine_getPayloadV3" { + return None; + } - let mut payload = serde_json::from_value::(_result).unwrap(); + let mut payload = + serde_json::from_value::(_result).unwrap(); - // modify the state root field - payload - .execution_payload - .payload_inner - .payload_inner - .state_root = B256::ZERO; + // modify the state root field + payload + .execution_payload + .payload_inner + .payload_inner + .state_root = B256::ZERO; - let result = serde_json::to_value(&payload).unwrap(); - Some(result) - }); + let result = serde_json::to_value(&payload).unwrap(); + Some(result) + } + .boxed() + } +} +#[tokio::test] +async fn builder_returns_incorrect_block() -> eyre::Result<()> { + // Test that the builder returns a block with an incorrect state root and that rollup-boost + // does not process it. let harness = RollupBoostTestHarnessBuilder::new("builder_returns_incorrect_block") - .proxy_handler(handler) + .proxy_handler(Arc::new(Handler)) .build() .await?; - let mut block_generator = harness.get_block_generator().await?; + let mut block_generator = harness.block_generator().await?; // create 3 blocks that are processed by the builder for _ in 0..3 { diff --git a/tests/execution_mode.rs b/tests/execution_mode.rs index 60fd7204..45f73138 100644 --- a/tests/execution_mode.rs +++ b/tests/execution_mode.rs @@ -1,5 +1,8 @@ +use futures::FutureExt as _; +use integration::proxy::ProxyHandler; use rollup_boost::ExecutionMode; use serde_json::Value; +use std::pin::Pin; use std::sync::{Arc, Mutex}; use std::time::Duration; @@ -7,24 +10,35 @@ mod integration; use crate::integration::RollupBoostTestHarnessBuilder; +struct CounterHandler { + counter: Arc>, +} + +impl ProxyHandler for CounterHandler { + fn handle( + &self, + _method: String, + _params: Value, + _result: Value, + ) -> Pin> + Send>> { + *self.counter.lock().unwrap() += 1; + async move { None }.boxed() + } +} + #[tokio::test] async fn execution_mode() -> eyre::Result<()> { // Create a counter that increases whenever we receive a new RPC call in the builder let counter = Arc::new(Mutex::new(0)); - - let counter_for_handler = counter.clone(); - let handler = Box::new(move |_method: &str, _params: Value, _result: Value| { - let mut counter = counter_for_handler.lock().unwrap(); - - *counter += 1; - None + let handler = Arc::new(CounterHandler { + counter: counter.clone(), }); let harness = RollupBoostTestHarnessBuilder::new("execution_mode") .proxy_handler(handler) .build() .await?; - let mut block_generator = harness.get_block_generator().await?; + let mut block_generator = harness.block_generator().await?; // start creating 5 empty blocks which are processed by the builder for _ in 0..5 { @@ -35,7 +49,7 @@ async fn execution_mode() -> eyre::Result<()> { ); } - let client = harness.get_client().await; + let client = harness.debug_client().await; // enable dry run mode { diff --git a/tests/integration/containers/rollup_boost.rs b/tests/integration/containers/rollup_boost.rs index 45e81e73..b6915ca5 100644 --- a/tests/integration/containers/rollup_boost.rs +++ b/tests/integration/containers/rollup_boost.rs @@ -1,3 +1,5 @@ +use std::time::Duration; + use clap::Parser; use rollup_boost::Args; use tokio::task::JoinHandle; @@ -53,7 +55,7 @@ impl Default for RollupBoostConfig { } impl RollupBoostConfig { - pub fn start(self) -> RollupBoost { + pub async fn start(self) -> RollupBoost { let args = self.args.clone(); let _handle = tokio::spawn(async move { let res = args.clone().run().await; @@ -63,6 +65,9 @@ impl RollupBoostConfig { res }); + // Allow some time for the app to startup + tokio::time::sleep(Duration::from_secs(2)).await; + RollupBoost { args: self.args, _handle, diff --git a/tests/integration/mod.rs b/tests/integration/mod.rs index 5354d949..a4b1fa2c 100644 --- a/tests/integration/mod.rs +++ b/tests/integration/mod.rs @@ -12,12 +12,11 @@ use containers::rollup_boost::{RollupBoost, RollupBoostConfig}; use eyre::bail; use futures::FutureExt; use futures::future::BoxFuture; -use http::Uri; use jsonrpsee::http_client::{HttpClient, transport::HttpBackend}; use jsonrpsee::proc_macros::rpc; use op_alloy_consensus::TxDeposit; use op_alloy_rpc_types_engine::OpPayloadAttributes; -use proxy::{DynHandlerFn, start_proxy_server}; +use proxy::{ProxyHandler, start_proxy_server}; use rollup_boost::client::auth::{AuthClientLayer, AuthClientService}; use rollup_boost::debug_api::DebugClient; use rollup_boost::{EngineApiClient, OpExecutionPayloadEnvelope, Version}; @@ -25,7 +24,7 @@ use rollup_boost::{NewPayload, PayloadSource}; use std::net::TcpListener; use std::path::PathBuf; use std::str::FromStr; -use std::sync::LazyLock; +use std::sync::{Arc, LazyLock}; use std::time::{Duration, SystemTime}; use testcontainers::core::ContainerPort; use testcontainers::core::client::docker_client_instance; @@ -45,8 +44,8 @@ pub const L2_P2P_ENODE: &str = "3479db4d9217fb5d7a8ed4d61ac36e120b05d36c2eefb795 pub static TEST_DATA: LazyLock = LazyLock::new(|| format!("{}/tests/integration/test_data", env!("CARGO_MANIFEST_DIR"))); -mod containers; -mod proxy; +pub mod containers; +pub mod proxy; pub struct LoggingConsumer { target: String, @@ -71,7 +70,7 @@ impl LogConsumer for LoggingConsumer { } } -pub async fn wait_for_log( +pub async fn _wait_for_log( container: ContainerAsync, pattern: &str, timeout: Duration, @@ -200,6 +199,7 @@ pub trait BlockApi { } /// Test flavor that sets up one Rollup-boost instance connected to two Reth nodes +#[allow(dead_code)] pub struct RollupBoostTestHarness { pub l2: ContainerAsync, pub builder: ContainerAsync, @@ -208,7 +208,7 @@ pub struct RollupBoostTestHarness { pub struct RollupBoostTestHarnessBuilder { test_name: String, - proxy_handler: Option, + proxy_handler: Option>, } impl RollupBoostTestHarnessBuilder { @@ -224,8 +224,6 @@ impl RollupBoostTestHarnessBuilder { let format = format_description::parse("[year]_[month]_[day]_[hour]_[minute]_[second]")?; let timestamp = dt.format(&format)?; - // let test_name = format!("{}_{}", timestamp, test_name); - let dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("test_logs") .join(timestamp) @@ -261,7 +259,7 @@ impl RollupBoostTestHarnessBuilder { }) } - pub fn proxy_handler(mut self, proxy_handler: DynHandlerFn) -> Self { + pub fn proxy_handler(mut self, proxy_handler: Arc) -> Self { self.proxy_handler = Some(proxy_handler); self } @@ -330,12 +328,10 @@ impl RollupBoostTestHarnessBuilder { rollup_boost.args.l2_client.l2_url = l2.auth_rpc().await?; rollup_boost.args.builder.builder_url = builder_url.try_into().unwrap(); rollup_boost.args.log_file = Some(rollup_boost_log_file_path); - let rollup_boost = rollup_boost.start(); + let rollup_boost = rollup_boost.start().await; println!("rollup-boost authrpc: {}", rollup_boost.rpc_endpoint()); println!("rollup-boost metrics: {}", rollup_boost.metrics_endpoint()); - tokio::time::sleep(Duration::from_secs(2)).await; - Ok(RollupBoostTestHarness { l2, builder, @@ -345,7 +341,7 @@ impl RollupBoostTestHarnessBuilder { } impl RollupBoostTestHarness { - pub async fn get_block_generator(&self) -> eyre::Result { + pub async fn block_generator(&self) -> eyre::Result { let validator = BlockBuilderCreatorValidator::new(self.rollup_boost.args().log_file.clone().unwrap()); @@ -356,7 +352,7 @@ impl RollupBoostTestHarness { Ok(block_creator) } - pub async fn get_client(&self) -> DebugClient { + pub async fn debug_client(&self) -> DebugClient { DebugClient::new(&self.rollup_boost.debug_endpoint()).unwrap() } } @@ -472,13 +468,13 @@ pub struct BlockBuilderCreatorValidator { file: PathBuf, } -impl<'a> BlockBuilderCreatorValidator { +impl BlockBuilderCreatorValidator { pub fn new(file: PathBuf) -> Self { Self { file } } } -impl<'a> BlockBuilderCreatorValidator { +impl BlockBuilderCreatorValidator { pub async fn get_block_creator(&self, block_hash: B256) -> eyre::Result> { let contents = std::fs::read_to_string(&self.file)?; @@ -534,10 +530,6 @@ fn create_deposit_tx() -> Bytes { buffer_without_header.to_vec().into() } -fn local_host(port: u16) -> Uri { - format!("http://localhost:{port}").parse::().unwrap() -} - fn get_available_port() -> Option { loop { let port = rand::random::() % 20000 + 1000; diff --git a/tests/integration/proxy.rs b/tests/integration/proxy.rs index cf3cce5f..a7bf23b7 100644 --- a/tests/integration/proxy.rs +++ b/tests/integration/proxy.rs @@ -9,6 +9,7 @@ use hyper_util::rt::TokioIo; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::net::SocketAddr; +use std::pin::Pin; use std::sync::Arc; use tokio::net::{TcpListener, TcpStream}; @@ -37,13 +38,20 @@ pub struct JsonRpcError { data: Option, } -pub type DynHandlerFn = Box Option + Send + Sync>; +pub trait ProxyHandler: Send + Sync + 'static { + fn handle( + &self, + method: String, + params: Value, + result: Value, + ) -> Pin> + Send>>; +} // Structure to hold the target address that we'll pass to the proxy function #[derive(Clone)] struct ProxyConfig { target_addr: SocketAddr, - handler: Arc, + handler: Arc, } async fn proxy( @@ -78,7 +86,10 @@ async fn proxy( let json_rpc_response = serde_json::from_slice::(&bytes).unwrap(); let bytes = if let Some(result) = json_rpc_response.clone().result { - let value = (config.handler)(&json_rpc_request.method, json_rpc_request.params, result); + let value = config + .handler + .handle(json_rpc_request.method, json_rpc_request.params, result) + .await; if let Some(value) = value { // If the handler returns a value, we replace the result with the new value // The callback only returns the result of the jsonrpc request so we have to wrap it up @@ -105,7 +116,7 @@ async fn proxy( } pub async fn start_proxy_server( - handler: DynHandlerFn, + handler: Arc, listen_port: u16, target_port: u16, ) -> Result<(), Box> { @@ -114,7 +125,7 @@ pub async fn start_proxy_server( let config = ProxyConfig { target_addr, - handler: Arc::new(handler), + handler, }; let listener = TcpListener::bind(listen_addr).await?; diff --git a/tests/no_tx_pool.rs b/tests/no_tx_pool.rs index 02328c34..4fd12e93 100644 --- a/tests/no_tx_pool.rs +++ b/tests/no_tx_pool.rs @@ -8,7 +8,7 @@ async fn simple() -> eyre::Result<()> { let harness = RollupBoostTestHarnessBuilder::new("test_integration_no_tx_pool") .build() .await?; - let mut block_generator = harness.get_block_generator().await?; + let mut block_generator = harness.block_generator().await?; // start creating 5 empty blocks which are processed by the L2 builder for _ in 0..5 { diff --git a/tests/remote_builder_down.rs b/tests/remote_builder_down.rs index 16d0368d..7acf5c55 100644 --- a/tests/remote_builder_down.rs +++ b/tests/remote_builder_down.rs @@ -11,7 +11,7 @@ async fn remote_builder_down() -> eyre::Result<()> { let harness = RollupBoostTestHarnessBuilder::new("remote_builder_down") .build() .await?; - let mut block_generator = harness.get_block_generator().await?; + let mut block_generator = harness.block_generator().await?; for _ in 0..3 { let (_block, block_creator) = block_generator.generate_block(false).await?; @@ -32,16 +32,9 @@ async fn remote_builder_down() -> eyre::Result<()> { assert!(block_creator.is_l2(), "Block creator should be l2"); } - // start the builder again client.unpause_container(harness.builder.id()).await?; - // the next block is computed by the l2 builder because the builder is not synced with the previous 3 blocks - // But, once the builder receives the FCU request from rollup-boost, it will sync up the blocks with the - // L2 block builder and be ready again. - let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!(block_creator.is_l2(), "Block creator should be l2"); - - // Note: We might add some sleep here if the builder is not synced in time. I have not seen this happen yet. + // Sleep briefly to allow the builder to sync tokio::time::sleep(Duration::from_secs(2)).await; // create 3 new blocks that are processed by the l2 builder because the builder is not synced with the previous 3 blocks diff --git a/tests/simple.rs b/tests/simple.rs index 17841b92..5072d558 100644 --- a/tests/simple.rs +++ b/tests/simple.rs @@ -5,7 +5,7 @@ mod integration; #[tokio::test] async fn test_integration_simple() -> eyre::Result<()> { let harness = RollupBoostTestHarnessBuilder::new("simple").build().await?; - let mut block_generator = harness.get_block_generator().await?; + let mut block_generator = harness.block_generator().await?; for _ in 0..5 { let (_block, block_creator) = block_generator.generate_block(false).await?; From 2494cbfa7008e7c5cce54c54b884b2e930a221dc Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Thu, 3 Apr 2025 23:38:46 -0700 Subject: [PATCH 09/20] wip --- src/client/rpc.rs | 2 +- tests/integration/containers/op_reth.rs | 53 +++------- tests/integration/containers/rollup_boost.rs | 8 +- tests/integration/mod.rs | 104 +++++-------------- tests/no_tx_pool.rs | 4 +- 5 files changed, 45 insertions(+), 126 deletions(-) diff --git a/src/client/rpc.rs b/src/client/rpc.rs index 9775effb..5072452d 100644 --- a/src/client/rpc.rs +++ b/src/client/rpc.rs @@ -383,7 +383,7 @@ mod tests { fn get_available_port() -> u16 { loop { - let port = rand::random::() % 20000 + 1000; + let port: u16 = rand::random_range(1000..20000); if port_is_available(port) { return port; } diff --git a/tests/integration/containers/op_reth.rs b/tests/integration/containers/op_reth.rs index 0e68d4e8..4e7ce785 100644 --- a/tests/integration/containers/op_reth.rs +++ b/tests/integration/containers/op_reth.rs @@ -15,13 +15,13 @@ use testcontainers::{ core::{ContainerPort, WaitFor}, }; -use crate::integration::{L2_P2P_ENODE, TEST_DATA}; +use crate::integration::TEST_DATA; const NAME: &str = "ghcr.io/paradigmxyz/op-reth"; const TAG: &str = "v1.3.4"; -const AUTH_RPC_PORT: u16 = 8551; -const P2P_PORT: u16 = 30303; +pub const AUTH_RPC_PORT: u16 = 8551; +pub const P2P_PORT: u16 = 30303; // Genesis should only be created once and reused for // each instance @@ -49,7 +49,6 @@ pub struct OpRethConfig { jwt_secret: PathBuf, p2p_secret: Option, pub trusted_peers: Vec, - pub datadir: String, pub color: String, pub ipcdisable: bool, pub env_vars: HashMap, @@ -61,7 +60,6 @@ impl Default for OpRethConfig { jwt_secret: PathBuf::from(format!("{}/jwt_secret.hex", *TEST_DATA)), p2p_secret: None, trusted_peers: vec![], - datadir: "data".to_string(), color: "never".to_string(), ipcdisable: true, env_vars: Default::default(), @@ -85,41 +83,32 @@ impl OpRethConfig { self } - pub fn build(self) -> OpRethImage { - // Write the genesis file to the test directory and update the timestamp to the current time - + pub fn build(self) -> eyre::Result { let mut copy_to_sources = vec![ CopyToContainer::new( - // std::fs::read(&self.jwt_secret).unwrap(), - "688f5d737bad920bdfb2fc2f488d6b6209eebda1dae949a8de91398d932c517a" - .to_string() - .into_bytes(), + std::fs::read_to_string(&self.jwt_secret)?.into_bytes(), "/jwt_secret.hex".to_string(), ), CopyToContainer::new(GENESIS.clone().into_bytes(), "/genesis.json".to_string()), ]; if let Some(p2p_secret) = &self.p2p_secret { + let p2p_string = std::fs::read_to_string(p2p_secret) + .unwrap() + .replace("\n", ""); copy_to_sources.push(CopyToContainer::new( - // std::fs::read(p2p_secret).unwrap(), - "a11ac89899cd86e36b6fb881ec1255b8a92a688790b7d950f8b7d8dd626671fb" - .to_string() - .into_bytes(), + p2p_string.into_bytes(), "/p2p_secret.hex".to_string(), )); } - let expose_ports = vec![ - ContainerPort::Tcp(8551), - // ContainerPort::Tcp(30303), - // ContainerPort::Udp(30303), - ]; + let expose_ports = vec![]; - OpRethImage { + Ok(OpRethImage { config: self, copy_to_sources, expose_ports, - } + }) } } @@ -172,8 +161,6 @@ impl Image for OpRethImage { "--chain=/genesis.json".to_string(), "--log.stdout.filter=trace".to_string(), "-vvvvv".to_string(), - "--datadir".to_string(), - self.config.datadir.clone(), "--disable-discovery".to_string(), "--color".to_string(), self.config.color.clone(), @@ -188,12 +175,6 @@ impl Image for OpRethImage { self.config.trusted_peers.join(","), ]); } - if !self.config.trusted_peers.is_empty() { - cmd.extend([ - "--bootnodes".to_string(), - self.config.trusted_peers.join(","), - ]); - } if self.config.ipcdisable { cmd.push("--ipcdisable".to_string()); } @@ -208,7 +189,6 @@ impl Image for OpRethImage { pub trait OpRethMehods { async fn auth_rpc(&self) -> eyre::Result; async fn auth_rpc_port(&self) -> eyre::Result; - async fn enode(&self, port: u16) -> eyre::Result; } impl OpRethMehods for ContainerAsync { @@ -224,13 +204,4 @@ impl OpRethMehods for ContainerAsync { ) .parse()?) } - - async fn enode(&self, port: u16) -> eyre::Result { - Ok(format!( - "enode://{}@l2:{}", - L2_P2P_ENODE, - // self.get_host().await?, - port // self.get_host_port_ipv4(P2P_PORT).await? - )) - } } diff --git a/tests/integration/containers/rollup_boost.rs b/tests/integration/containers/rollup_boost.rs index b6915ca5..193c0cca 100644 --- a/tests/integration/containers/rollup_boost.rs +++ b/tests/integration/containers/rollup_boost.rs @@ -46,9 +46,9 @@ impl Default for RollupBoostConfig { "--metrics", ]); - args.rpc_port = get_available_port().unwrap(); - args.metrics_port = get_available_port().unwrap(); - args.debug_server_port = get_available_port().unwrap(); + args.rpc_port = get_available_port(); + args.metrics_port = get_available_port(); + args.debug_server_port = get_available_port(); Self { args } } @@ -66,7 +66,7 @@ impl RollupBoostConfig { }); // Allow some time for the app to startup - tokio::time::sleep(Duration::from_secs(2)).await; + tokio::time::sleep(Duration::from_secs(4)).await; RollupBoost { args: self.args, diff --git a/tests/integration/mod.rs b/tests/integration/mod.rs index a4b1fa2c..2a9b831a 100644 --- a/tests/integration/mod.rs +++ b/tests/integration/mod.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] use alloy_eips::Encodable2718; use alloy_primitives::{B256, Bytes, TxKind, U256, address, hex}; use alloy_rpc_types_engine::{ExecutionPayload, JwtSecret}; @@ -7,9 +8,8 @@ use alloy_rpc_types_engine::{ }; use alloy_rpc_types_eth::BlockNumberOrTag; use bytes::BytesMut; -use containers::op_reth::{OpRethConfig, OpRethImage, OpRethMehods}; +use containers::op_reth::{AUTH_RPC_PORT, OpRethConfig, OpRethImage, OpRethMehods, P2P_PORT}; use containers::rollup_boost::{RollupBoost, RollupBoostConfig}; -use eyre::bail; use futures::FutureExt; use futures::future::BoxFuture; use jsonrpsee::http_client::{HttpClient, transport::HttpBackend}; @@ -25,24 +25,23 @@ use std::net::TcpListener; use std::path::PathBuf; use std::str::FromStr; use std::sync::{Arc, LazyLock}; -use std::time::{Duration, SystemTime}; +use std::time::SystemTime; use testcontainers::core::ContainerPort; use testcontainers::core::client::docker_client_instance; use testcontainers::core::logs::LogFrame; use testcontainers::core::logs::consumer::LogConsumer; use testcontainers::runners::AsyncRunner; -use testcontainers::{ContainerAsync, Image, ImageExt}; +use testcontainers::{ContainerAsync, ImageExt}; use time::{OffsetDateTime, format_description}; -use tokio::io::{AsyncBufReadExt, AsyncWriteExt as _}; +use tokio::io::AsyncWriteExt as _; use tracing::info; -// use time::{OffsetDateTime, format_description}; - /// Default JWT token for testing purposes pub const JWT_SECRET: &str = "688f5d737bad920bdfb2fc2f488d6b6209eebda1dae949a8de91398d932c517a"; pub const L2_P2P_ENODE: &str = "3479db4d9217fb5d7a8ed4d61ac36e120b05d36c2eefb795dc42ff2e971f251a2315f5649ea1833271e020b9adc98d5db9973c7ed92d6b2f1f2223088c3d852f"; pub static TEST_DATA: LazyLock = LazyLock::new(|| format!("{}/tests/integration/test_data", env!("CARGO_MANIFEST_DIR"))); +pub const NETWORK: &str = "devnet"; pub mod containers; pub mod proxy; @@ -70,42 +69,6 @@ impl LogConsumer for LoggingConsumer { } } -pub async fn _wait_for_log( - container: ContainerAsync, - pattern: &str, - timeout: Duration, -) -> eyre::Result<()> { - let timeout = tokio::time::sleep(timeout); - let mut stderr = container.stderr(true).lines(); - let mut stdout = container.stdout(true).lines(); - - tokio::select! { - result = async { - loop { - tokio::select! { - line = stderr.next_line() => { - if let Ok(Some(line)) = line { - if line.contains(pattern) { - return Ok::<_, eyre::Report>(()); - } - } - } - line = stdout.next_line() => { - if let Ok(Some(line)) = line { - if line.contains(pattern) { - return Ok::<_, eyre::Report>(()); - } - } - } - } - } - } => result, - _ = timeout => { - bail!("Timeout waiting for log message: {}", pattern); - } - } -} - pub struct EngineApi { pub engine_api_client: HttpClient>, } @@ -199,7 +162,6 @@ pub trait BlockApi { } /// Test flavor that sets up one Rollup-boost instance connected to two Reth nodes -#[allow(dead_code)] pub struct RollupBoostTestHarness { pub l2: ContainerAsync, pub builder: ContainerAsync, @@ -225,9 +187,9 @@ impl RollupBoostTestHarnessBuilder { let timestamp = dt.format(&format)?; let dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("test_logs") - .join(timestamp) - .join(self.test_name.clone()); + .join("integration_logs") + .join(self.test_name.clone()) + .join(timestamp); std::fs::create_dir_all(&dir)?; let file_name = format!("{service_name}.log"); @@ -243,14 +205,6 @@ impl RollupBoostTestHarnessBuilder { .await?) } - pub fn log_file(&self, service_name: &str) -> eyre::Result { - let file_path = self.file_path(service_name)?; - Ok(std::fs::OpenOptions::new() - .append(true) - .create(true) - .open(file_path)?) - } - pub async fn log_consumer(&self, service_name: &str) -> eyre::Result { let file = self.async_log_file(service_name).await?; Ok(LoggingConsumer { @@ -269,17 +223,17 @@ impl RollupBoostTestHarnessBuilder { let builder_log_consumer = self.log_consumer("builder").await?; let rollup_boost_log_file_path = self.file_path("rollup_boost")?; - let l2_p2p_port = get_available_port().expect("no available port"); + let l2_p2p_port = get_available_port(); let l2 = OpRethConfig::default() .set_p2p_secret(Some(PathBuf::from(format!( "{}/p2p_secret.hex", *TEST_DATA )))) - .build() - .with_mapped_port(l2_p2p_port, ContainerPort::Tcp(30303)) - .with_mapped_port(l2_p2p_port, ContainerPort::Udp(30303)) - .with_mapped_port(get_available_port().unwrap(), ContainerPort::Tcp(8551)) - .with_network("devnet") + .build()? + .with_mapped_port(l2_p2p_port, ContainerPort::Tcp(P2P_PORT)) + .with_mapped_port(l2_p2p_port, ContainerPort::Udp(P2P_PORT)) + .with_mapped_port(get_available_port(), ContainerPort::Tcp(AUTH_RPC_PORT)) + .with_network(NETWORK) .with_log_consumer(l2_log_consumer) .start() .await?; @@ -289,22 +243,16 @@ impl RollupBoostTestHarnessBuilder { let name = res.name.unwrap(); let name = name[1..].to_string(); // remove the leading '/' - let l2_enode = format!( - "enode://{}@{}:{}", - L2_P2P_ENODE, - name, - 30303 // l2_p2p_port // self.get_host_port_ipv4(P2P_PORT).await? - ); - tokio::time::sleep(Duration::from_secs(2)).await; + let l2_enode = format!("enode://{}@{}:{}", L2_P2P_ENODE, name, P2P_PORT); - let builder_p2p_port = get_available_port().expect("no available port"); + let builder_p2p_port = get_available_port(); let builder = OpRethConfig::default() .set_trusted_peers(vec![l2_enode]) - .build() - .with_mapped_port(builder_p2p_port, ContainerPort::Tcp(30303)) - .with_mapped_port(builder_p2p_port, ContainerPort::Udp(30303)) - .with_mapped_port(get_available_port().unwrap(), ContainerPort::Tcp(8551)) - .with_network("devnet") + .build()? + .with_mapped_port(builder_p2p_port, ContainerPort::Tcp(P2P_PORT)) + .with_mapped_port(builder_p2p_port, ContainerPort::Udp(P2P_PORT)) + .with_mapped_port(get_available_port(), ContainerPort::Tcp(AUTH_RPC_PORT)) + .with_network(NETWORK) .with_log_consumer(builder_log_consumer) .start() .await?; @@ -316,7 +264,7 @@ impl RollupBoostTestHarnessBuilder { let mut builder_authrpc_port = builder.auth_rpc_port().await?; if let Some(proxy_handler) = self.proxy_handler { println!("starting proxy server"); - let proxy_port = get_available_port().expect("no available port"); + let proxy_port = get_available_port(); let _ = start_proxy_server(proxy_handler, proxy_port, builder_authrpc_port).await; builder_authrpc_port = proxy_port }; @@ -530,11 +478,11 @@ fn create_deposit_tx() -> Bytes { buffer_without_header.to_vec().into() } -fn get_available_port() -> Option { +fn get_available_port() -> u16 { loop { - let port = rand::random::() % 20000 + 1000; + let port: u16 = rand::random_range(1000..20000); if port_is_available(port) { - return Some(port); + return port; } } } diff --git a/tests/no_tx_pool.rs b/tests/no_tx_pool.rs index 4fd12e93..85064f39 100644 --- a/tests/no_tx_pool.rs +++ b/tests/no_tx_pool.rs @@ -4,8 +4,8 @@ use crate::integration::RollupBoostTestHarnessBuilder; // use crate::server::ExecutionMode; #[tokio::test] -async fn simple() -> eyre::Result<()> { - let harness = RollupBoostTestHarnessBuilder::new("test_integration_no_tx_pool") +async fn no_tx_pool() -> eyre::Result<()> { + let harness = RollupBoostTestHarnessBuilder::new("no_tx_pool") .build() .await?; let mut block_generator = harness.block_generator().await?; From 721a39977778fc342c59410273034b77aff0c44d Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Thu, 3 Apr 2025 23:48:07 -0700 Subject: [PATCH 10/20] cleanup --- tests/builder_full_delay.rs | 3 --- tests/integration/mod.rs | 2 +- tests/integration/proxy.rs | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/builder_full_delay.rs b/tests/builder_full_delay.rs index b3262d95..c63a07a5 100644 --- a/tests/builder_full_delay.rs +++ b/tests/builder_full_delay.rs @@ -55,9 +55,6 @@ async fn builder_full_delay() -> eyre::Result<()> { ); } - // add the delay - // *delay.lock().unwrap() = Duration::from_secs(1); - // create 3 blocks that are processed by the builder for _ in 0..3 { let (_block, block_creator) = block_generator.generate_block(false).await?; diff --git a/tests/integration/mod.rs b/tests/integration/mod.rs index 2a9b831a..e00500b7 100644 --- a/tests/integration/mod.rs +++ b/tests/integration/mod.rs @@ -265,7 +265,7 @@ impl RollupBoostTestHarnessBuilder { if let Some(proxy_handler) = self.proxy_handler { println!("starting proxy server"); let proxy_port = get_available_port(); - let _ = start_proxy_server(proxy_handler, proxy_port, builder_authrpc_port).await; + start_proxy_server(proxy_handler, proxy_port, builder_authrpc_port).await?; builder_authrpc_port = proxy_port }; let builder_url = format!("http://localhost:{}/", builder_authrpc_port); diff --git a/tests/integration/proxy.rs b/tests/integration/proxy.rs index a7bf23b7..356729b5 100644 --- a/tests/integration/proxy.rs +++ b/tests/integration/proxy.rs @@ -119,7 +119,7 @@ pub async fn start_proxy_server( handler: Arc, listen_port: u16, target_port: u16, -) -> Result<(), Box> { +) -> eyre::Result<()> { let listen_addr = SocketAddr::from(([127, 0, 0, 1], listen_port)); let target_addr = SocketAddr::from(([127, 0, 0, 1], target_port)); From 619ae1c184efa483a83087c8615cbd6e1df8aa18 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Fri, 4 Apr 2025 00:09:49 -0700 Subject: [PATCH 11/20] rename --- Cargo.lock | 123 ++++-------------- Cargo.toml | 8 +- Makefile | 4 - RELEASE.md | 2 +- docs/design-philosophy-testing-strategy.md | 2 +- src/lib.rs | 12 +- tests/builder_full_delay.rs | 6 +- tests/builder_returns_incorrect_block.rs | 4 +- tests/{integration => common}/mod.rs | 15 +-- tests/{integration => common}/proxy.rs | 0 .../containers => common/services}/mod.rs | 0 .../containers => common/services}/op_reth.rs | 2 +- .../services}/rollup_boost.rs | 2 +- .../test_data/genesis.json | 0 .../test_data/jwt_secret.hex | 0 .../test_data/p2p_secret.hex | 0 tests/execution_mode.rs | 6 +- tests/no_tx_pool.rs | 4 +- tests/remote_builder_down.rs | 4 +- tests/simple.rs | 4 +- 20 files changed, 51 insertions(+), 147 deletions(-) rename tests/{integration => common}/mod.rs (97%) rename tests/{integration => common}/proxy.rs (100%) rename tests/{integration/containers => common/services}/mod.rs (100%) rename tests/{integration/containers => common/services}/op_reth.rs (99%) rename tests/{integration/containers => common/services}/rollup_boost.rs (96%) rename tests/{integration => common}/test_data/genesis.json (100%) rename tests/{integration => common}/test_data/jwt_secret.hex (100%) rename tests/{integration => common}/test_data/p2p_secret.hex (100%) diff --git a/Cargo.lock b/Cargo.lock index aa637cd9..4d2e0076 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,7 +23,7 @@ version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "once_cell", "version_check", "zerocopy 0.7.35", @@ -181,7 +181,7 @@ checksum = "8c77490fe91a0ce933a1f219029521f20fc28c2c0ca95d53fa4da9c00b8d9d4e" dependencies = [ "alloy-rlp", "bytes", - "cfg-if 1.0.0", + "cfg-if", "const-hex", "derive_more 2.0.1", "foldhash", @@ -739,7 +739,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cfg-if 1.0.0", + "cfg-if", "libc", "miniz_oxide", "object", @@ -1005,12 +1005,6 @@ dependencies = [ "nom", ] -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" @@ -1025,10 +1019,8 @@ checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" dependencies = [ "android-tzdata", "iana-time-zone", - "js-sys", "num-traits", "serde", - "wasm-bindgen", "windows-link", ] @@ -1114,7 +1106,7 @@ version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b0485bab839b018a8f1723fc5391819fea5f8f0f32288ef8a735fd096b6160c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "hex", "proptest", @@ -1193,7 +1185,7 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -1514,19 +1506,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" -[[package]] -name = "env_logger" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" -dependencies = [ - "humantime", - "is-terminal", - "log", - "regex", - "termcolor", -] - [[package]] name = "equivalent" version = "1.0.2" @@ -1549,7 +1528,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "home", "windows-sys 0.48.0", ] @@ -1648,7 +1627,7 @@ version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "libredox", "windows-sys 0.59.0", @@ -1813,7 +1792,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "log", "rustversion", @@ -1837,7 +1816,7 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", @@ -1850,7 +1829,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "r-efi", "wasi 0.14.2+wasi-0.2.4", @@ -1926,12 +1905,6 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" -[[package]] -name = "hermit-abi" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e" - [[package]] name = "hex" version = "0.4.3" @@ -2011,12 +1984,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "humantime" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" - [[package]] name = "hyper" version = "1.6.0" @@ -2353,17 +2320,6 @@ dependencies = [ "serde", ] -[[package]] -name = "is-terminal" -version = "0.4.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" -dependencies = [ - "hermit-abi 0.5.0", - "libc", - "windows-sys 0.59.0", -] - [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -2419,7 +2375,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" dependencies = [ "cesu8", - "cfg-if 1.0.0", + "cfg-if", "combine", "jni-sys", "log", @@ -2590,7 +2546,7 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "ecdsa", "elliptic-curve", "once_cell", @@ -2641,7 +2597,7 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "windows-targets 0.52.6", ] @@ -2702,7 +2658,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "generator", "scoped-tls", "tracing", @@ -2862,19 +2818,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "nix" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" -dependencies = [ - "bitflags 1.3.2", - "cc", - "cfg-if 0.1.10", - "libc", - "void", -] - [[package]] name = "nom" version = "7.1.3" @@ -2942,7 +2885,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi", "libc", ] @@ -3160,7 +3103,7 @@ version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "redox_syscall 0.5.10", "smallvec", @@ -3698,7 +3641,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", - "cfg-if 1.0.0", + "cfg-if", "getrandom 0.2.15", "libc", "untrusted", @@ -3726,11 +3669,9 @@ dependencies = [ "anyhow", "assert_cmd", "bytes", - "chrono", "clap", "ctor", "dotenv", - "env_logger", "eyre", "futures", "http", @@ -3739,13 +3680,10 @@ dependencies = [ "hyper-rustls", "hyper-util", "jsonrpsee", - "lazy_static", - "log", "metrics", "metrics-exporter-prometheus", "metrics-util", "moka", - "nix", "op-alloy-consensus", "op-alloy-rpc-types-engine", "opentelemetry", @@ -4180,7 +4118,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "digest 0.10.7", ] @@ -4191,7 +4129,7 @@ version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "digest 0.10.7", ] @@ -4213,7 +4151,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" dependencies = [ "cc", - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -4476,15 +4414,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - [[package]] name = "termtree" version = "0.5.1" @@ -4566,7 +4495,7 @@ version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "once_cell", ] @@ -5058,12 +4987,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - [[package]] name = "wait-timeout" version = "0.2.1" @@ -5113,7 +5036,7 @@ version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", @@ -5139,7 +5062,7 @@ version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "js-sys", "once_cell", "wasm-bindgen", diff --git a/Cargo.toml b/Cargo.toml index a56d8855..25127e67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,25 +42,19 @@ metrics-exporter-prometheus = "0.16.0" metrics-util = "0.19.0" eyre = "0.6.12" paste = "1.0.15" - -# dev dependencies for integration tests parking_lot = "0.12.3" -lazy_static = { version = "1.5.0" } [dev-dependencies] rand = "0.9.0" time = { version = "0.3.36", features = ["macros", "formatting", "parsing"] } op-alloy-consensus = "0.12.0" alloy-rpc-types-eth = "0.13.0" -chrono = { version = "0.4.40", features = ["serde"] } anyhow = "1.0" -log = "0.4" -env_logger = "0.10" testcontainers = { version = "0.23.3" } assert_cmd = "2.0.10" predicates = "3.1.2" tokio-util = { version = "0.7.13" } -nix = "0.15.0" +# nix = "0.15.0" bytes = "1.2" reth-rpc-layer = { git = "https://github.com/paradigmxyz/reth.git", rev = "v1.3.7" } ctor = "0.4.1" diff --git a/Makefile b/Makefile index 31254594..abec98aa 100644 --- a/Makefile +++ b/Makefile @@ -41,10 +41,6 @@ lint: ## Run the linters test: ## Run the tests for rollup-boost cargo test --verbose --features "$(FEATURES)" -.PHONY: test-integration -test-integration: ## Run the integration tests for rollup-boost - cargo test - .PHONY: lt lt: lint test ## Run "lint" and "test" diff --git a/RELEASE.md b/RELEASE.md index 4e1e4a32..8d734701 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -18,7 +18,7 @@ Then run the following commands to check the code is working fine: ```bash make lint make test -make test-integration +make test git status # should show no changes # Start rollup-boost with the example .env config diff --git a/docs/design-philosophy-testing-strategy.md b/docs/design-philosophy-testing-strategy.md index 7cce6e6a..394e299f 100644 --- a/docs/design-philosophy-testing-strategy.md +++ b/docs/design-philosophy-testing-strategy.md @@ -20,7 +20,7 @@ We employ a layered testing strategy that provides defense in depth: **Unit Tests** verify individual components, but as this is a distributed system, they only get us so far. -**Integration Tests** serve as our most critical testing layer. Located in `src/integration/`, these tests use a simulated environment to verify system behavior under various conditions: +**Integration Tests** serve as our most critical testing layer. Located in `tests`, these tests use a simulated environment to verify system behavior under various conditions: - How does the system respond when the builder produces invalid blocks? - What happens when the builder experiences high latency? diff --git a/src/lib.rs b/src/lib.rs index daf780ff..40b11818 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,26 +1,18 @@ -// #![cfg_attr( -// not(any(test, feature = "integration")), -// warn(unused_crate_dependencies) -// )] - use dotenv as _; use rustls as _; -pub mod client; +mod client; pub use client::{auth::*, http::*, rpc::*}; mod cli; pub use cli::*; -pub mod debug_api; +mod debug_api; pub use debug_api::*; mod health; pub use health::{HealthLayer, HealthService}; -// #[cfg(all(feature = "integration", test))] -// mod integration; - mod metrics; pub use metrics::*; diff --git a/tests/builder_full_delay.rs b/tests/builder_full_delay.rs index c63a07a5..79aa2c04 100644 --- a/tests/builder_full_delay.rs +++ b/tests/builder_full_delay.rs @@ -1,12 +1,12 @@ +use common::RollupBoostTestHarnessBuilder; +use common::proxy::ProxyHandler; use futures::FutureExt; -use integration::RollupBoostTestHarnessBuilder; -use integration::proxy::ProxyHandler; use serde_json::Value; use std::pin::Pin; use std::sync::{Arc, Mutex}; use std::time::Duration; -mod integration; +mod common; // Create a dynamic handler that delays all the calls by 2 seconds struct DelayHandler { diff --git a/tests/builder_returns_incorrect_block.rs b/tests/builder_returns_incorrect_block.rs index d68cd8e4..0ea90b1f 100644 --- a/tests/builder_returns_incorrect_block.rs +++ b/tests/builder_returns_incorrect_block.rs @@ -1,13 +1,13 @@ use std::{pin::Pin, sync::Arc}; use alloy_primitives::B256; +use common::{RollupBoostTestHarnessBuilder, proxy::ProxyHandler}; use futures::FutureExt as _; -use integration::{RollupBoostTestHarnessBuilder, proxy::ProxyHandler}; use serde_json::Value; use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; -mod integration; +mod common; struct Handler; diff --git a/tests/integration/mod.rs b/tests/common/mod.rs similarity index 97% rename from tests/integration/mod.rs rename to tests/common/mod.rs index e00500b7..b051ee5d 100644 --- a/tests/integration/mod.rs +++ b/tests/common/mod.rs @@ -8,8 +8,6 @@ use alloy_rpc_types_engine::{ }; use alloy_rpc_types_eth::BlockNumberOrTag; use bytes::BytesMut; -use containers::op_reth::{AUTH_RPC_PORT, OpRethConfig, OpRethImage, OpRethMehods, P2P_PORT}; -use containers::rollup_boost::{RollupBoost, RollupBoostConfig}; use futures::FutureExt; use futures::future::BoxFuture; use jsonrpsee::http_client::{HttpClient, transport::HttpBackend}; @@ -17,10 +15,12 @@ use jsonrpsee::proc_macros::rpc; use op_alloy_consensus::TxDeposit; use op_alloy_rpc_types_engine::OpPayloadAttributes; use proxy::{ProxyHandler, start_proxy_server}; -use rollup_boost::client::auth::{AuthClientLayer, AuthClientService}; -use rollup_boost::debug_api::DebugClient; +use rollup_boost::DebugClient; +use rollup_boost::{AuthClientLayer, AuthClientService}; use rollup_boost::{EngineApiClient, OpExecutionPayloadEnvelope, Version}; use rollup_boost::{NewPayload, PayloadSource}; +use services::op_reth::{AUTH_RPC_PORT, OpRethConfig, OpRethImage, OpRethMehods, P2P_PORT}; +use services::rollup_boost::{RollupBoost, RollupBoostConfig}; use std::net::TcpListener; use std::path::PathBuf; use std::str::FromStr; @@ -40,11 +40,11 @@ use tracing::info; pub const JWT_SECRET: &str = "688f5d737bad920bdfb2fc2f488d6b6209eebda1dae949a8de91398d932c517a"; pub const L2_P2P_ENODE: &str = "3479db4d9217fb5d7a8ed4d61ac36e120b05d36c2eefb795dc42ff2e971f251a2315f5649ea1833271e020b9adc98d5db9973c7ed92d6b2f1f2223088c3d852f"; pub static TEST_DATA: LazyLock = - LazyLock::new(|| format!("{}/tests/integration/test_data", env!("CARGO_MANIFEST_DIR"))); + LazyLock::new(|| format!("{}/tests/common/test_data", env!("CARGO_MANIFEST_DIR"))); pub const NETWORK: &str = "devnet"; -pub mod containers; pub mod proxy; +pub mod services; pub struct LoggingConsumer { target: String, @@ -240,8 +240,7 @@ impl RollupBoostTestHarnessBuilder { let client = docker_client_instance().await?; let res = client.inspect_container(l2.id(), None).await?; - let name = res.name.unwrap(); - let name = name[1..].to_string(); // remove the leading '/' + let name = res.name.unwrap()[1..].to_string(); // remove the leading '/' let l2_enode = format!("enode://{}@{}:{}", L2_P2P_ENODE, name, P2P_PORT); diff --git a/tests/integration/proxy.rs b/tests/common/proxy.rs similarity index 100% rename from tests/integration/proxy.rs rename to tests/common/proxy.rs diff --git a/tests/integration/containers/mod.rs b/tests/common/services/mod.rs similarity index 100% rename from tests/integration/containers/mod.rs rename to tests/common/services/mod.rs diff --git a/tests/integration/containers/op_reth.rs b/tests/common/services/op_reth.rs similarity index 99% rename from tests/integration/containers/op_reth.rs rename to tests/common/services/op_reth.rs index 4e7ce785..f0921a78 100644 --- a/tests/integration/containers/op_reth.rs +++ b/tests/common/services/op_reth.rs @@ -15,7 +15,7 @@ use testcontainers::{ core::{ContainerPort, WaitFor}, }; -use crate::integration::TEST_DATA; +use crate::common::TEST_DATA; const NAME: &str = "ghcr.io/paradigmxyz/op-reth"; const TAG: &str = "v1.3.4"; diff --git a/tests/integration/containers/rollup_boost.rs b/tests/common/services/rollup_boost.rs similarity index 96% rename from tests/integration/containers/rollup_boost.rs rename to tests/common/services/rollup_boost.rs index 193c0cca..4efd3f5c 100644 --- a/tests/integration/containers/rollup_boost.rs +++ b/tests/common/services/rollup_boost.rs @@ -4,7 +4,7 @@ use clap::Parser; use rollup_boost::Args; use tokio::task::JoinHandle; -use crate::integration::{TEST_DATA, get_available_port}; +use crate::common::{TEST_DATA, get_available_port}; #[derive(Debug)] pub struct RollupBoost { diff --git a/tests/integration/test_data/genesis.json b/tests/common/test_data/genesis.json similarity index 100% rename from tests/integration/test_data/genesis.json rename to tests/common/test_data/genesis.json diff --git a/tests/integration/test_data/jwt_secret.hex b/tests/common/test_data/jwt_secret.hex similarity index 100% rename from tests/integration/test_data/jwt_secret.hex rename to tests/common/test_data/jwt_secret.hex diff --git a/tests/integration/test_data/p2p_secret.hex b/tests/common/test_data/p2p_secret.hex similarity index 100% rename from tests/integration/test_data/p2p_secret.hex rename to tests/common/test_data/p2p_secret.hex diff --git a/tests/execution_mode.rs b/tests/execution_mode.rs index 45f73138..ead3884d 100644 --- a/tests/execution_mode.rs +++ b/tests/execution_mode.rs @@ -1,14 +1,14 @@ +use common::proxy::ProxyHandler; use futures::FutureExt as _; -use integration::proxy::ProxyHandler; use rollup_boost::ExecutionMode; use serde_json::Value; use std::pin::Pin; use std::sync::{Arc, Mutex}; use std::time::Duration; -mod integration; +mod common; -use crate::integration::RollupBoostTestHarnessBuilder; +use crate::common::RollupBoostTestHarnessBuilder; struct CounterHandler { counter: Arc>, diff --git a/tests/no_tx_pool.rs b/tests/no_tx_pool.rs index 85064f39..623e59d7 100644 --- a/tests/no_tx_pool.rs +++ b/tests/no_tx_pool.rs @@ -1,6 +1,6 @@ -mod integration; +mod common; -use crate::integration::RollupBoostTestHarnessBuilder; +use crate::common::RollupBoostTestHarnessBuilder; // use crate::server::ExecutionMode; #[tokio::test] diff --git a/tests/remote_builder_down.rs b/tests/remote_builder_down.rs index 7acf5c55..cf459697 100644 --- a/tests/remote_builder_down.rs +++ b/tests/remote_builder_down.rs @@ -1,10 +1,10 @@ -mod integration; +mod common; use std::time::Duration; use testcontainers::core::client::docker_client_instance; -use crate::integration::RollupBoostTestHarnessBuilder; +use crate::common::RollupBoostTestHarnessBuilder; #[tokio::test] async fn remote_builder_down() -> eyre::Result<()> { diff --git a/tests/simple.rs b/tests/simple.rs index 5072d558..30390272 100644 --- a/tests/simple.rs +++ b/tests/simple.rs @@ -1,6 +1,6 @@ -use integration::RollupBoostTestHarnessBuilder; +use common::RollupBoostTestHarnessBuilder; -mod integration; +mod common; #[tokio::test] async fn test_integration_simple() -> eyre::Result<()> { From 57464f3e3add4c93bb73057666fefcfcb0470c99 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Fri, 4 Apr 2025 00:30:44 -0700 Subject: [PATCH 12/20] update test ci --- .github/workflows/test.yml | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b37b8f3b..a85aeee8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,25 +2,22 @@ name: Tests on: push: - branches: - - main + branches: [main] pull_request: +env: + CARGO_TERM_COLOR: always + jobs: - build: + run-tests: + name: Run tests runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Set up Rust - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + - uses: actions-rs/toolchain@v1 with: toolchain: stable - - - name: Build - run: cargo build - - - name: Run tests - run: cargo test -- --test-threads=1 + - name: Install cargo-nextest + uses: taiki-e/install-action@nextest + - name: Run nextest + run: cargo nextest run From a4ae40560a7ef37dded9c79aea0b34d3e5b721d0 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Fri, 4 Apr 2025 00:43:44 -0700 Subject: [PATCH 13/20] remove old integration CI --- .github/workflows/integration.yml | 50 ------------------------------- Cargo.toml | 1 - src/client/rpc.rs | 1 - tests/no_tx_pool.rs | 1 - 4 files changed, 53 deletions(-) delete mode 100644 .github/workflows/integration.yml diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml deleted file mode 100644 index d737e724..00000000 --- a/.github/workflows/integration.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: Integration Tests - -on: - push: - branches: - - main - pull_request: - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Set up Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - - - name: Install op-reth - uses: flashbots/flashbots-toolchain@v0.2 - with: - op-reth: latest - - - name: Log Op-reth version - run: | - op-reth --version - - - name: Build - run: cargo build - - - name: Run tests - run: cargo test --features integration -- integration::integration_test::tests - - - name: Create tar archive of integration logs - if: ${{ failure() }} - run: | - # Find and archive only the logs directories. There were some issues archiving the entire directory. - find integration_logs -type d -name "logs" | tar -czvf integration_logs.tar.gz -T - - - - name: Archive integration logs - uses: actions/upload-artifact@v4 - if: ${{ failure() }} - with: - name: integration-logs - path: integration_logs.tar.gz - retention-days: 5 - if-no-files-found: error diff --git a/Cargo.toml b/Cargo.toml index 25127e67..169261c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,7 +54,6 @@ testcontainers = { version = "0.23.3" } assert_cmd = "2.0.10" predicates = "3.1.2" tokio-util = { version = "0.7.13" } -# nix = "0.15.0" bytes = "1.2" reth-rpc-layer = { git = "https://github.com/paradigmxyz/reth.git", rev = "v1.3.7" } ctor = "0.4.1" diff --git a/src/client/rpc.rs b/src/client/rpc.rs index 5072452d..e30276d0 100644 --- a/src/client/rpc.rs +++ b/src/client/rpc.rs @@ -377,7 +377,6 @@ mod tests { use super::*; - // const AUTH_PORT: u32 = 8550; const AUTH_ADDR: &str = "0.0.0.0"; const SECRET: &str = "f79ae8046bc11c9927afe911db7143c51a806c4a537cc08e0d37140b0192f430"; diff --git a/tests/no_tx_pool.rs b/tests/no_tx_pool.rs index 623e59d7..ac0e708c 100644 --- a/tests/no_tx_pool.rs +++ b/tests/no_tx_pool.rs @@ -1,7 +1,6 @@ mod common; use crate::common::RollupBoostTestHarnessBuilder; -// use crate::server::ExecutionMode; #[tokio::test] async fn no_tx_pool() -> eyre::Result<()> { From cfa6fc7191e1dccf72bfc0ba8a748b9828864683 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Fri, 4 Apr 2025 08:57:06 -0700 Subject: [PATCH 14/20] unused_crate_dependencies --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 169261c2..ce177d89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,6 @@ edition = "2024" [dependencies] op-alloy-rpc-types-engine = "0.12.0" alloy-rpc-types-engine = "0.13.0" -alloy-eips = { version = "0.13.0", features = ["serde"] } alloy-primitives = { version = "0.8.10", features = ["rand"] } tokio = { version = "1", features = ["full"] } tracing = "0.1.4" @@ -48,6 +47,7 @@ parking_lot = "0.12.3" rand = "0.9.0" time = { version = "0.3.36", features = ["macros", "formatting", "parsing"] } op-alloy-consensus = "0.12.0" +alloy-eips = { version = "0.13.0", features = ["serde"] } alloy-rpc-types-eth = "0.13.0" anyhow = "1.0" testcontainers = { version = "0.23.3" } diff --git a/src/lib.rs b/src/lib.rs index 40b11818..0d131b74 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ +#![cfg_attr(not(test), warn(unused_crate_dependencies))] use dotenv as _; -use rustls as _; mod client; pub use client::{auth::*, http::*, rpc::*}; From 3d5416a855b64e1a028bbc884287bcbd24dca016 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Fri, 4 Apr 2025 08:59:58 -0700 Subject: [PATCH 15/20] typo --- RELEASE.md | 1 - 1 file changed, 1 deletion(-) diff --git a/RELEASE.md b/RELEASE.md index 8d734701..206b1d03 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -18,7 +18,6 @@ Then run the following commands to check the code is working fine: ```bash make lint make test -make test git status # should show no changes # Start rollup-boost with the example .env config From 9161e6fb1dac01120f13195d43246bb220725121 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Fri, 4 Apr 2025 09:21:04 -0700 Subject: [PATCH 16/20] Update tests/builder_returns_incorrect_block.rs Co-authored-by: 0xOsiris --- tests/builder_returns_incorrect_block.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/builder_returns_incorrect_block.rs b/tests/builder_returns_incorrect_block.rs index 0ea90b1f..d1ad4746 100644 --- a/tests/builder_returns_incorrect_block.rs +++ b/tests/builder_returns_incorrect_block.rs @@ -54,7 +54,7 @@ async fn builder_returns_incorrect_block() -> eyre::Result<()> { // create 3 blocks that are processed by the builder for _ in 0..3 { let (_block, block_creator) = block_generator.generate_block(false).await?; - assert!(block_creator.is_l2(), "Block creator should be the builder"); + assert!(block_creator.is_l2(), "Block creator should be the l2"); } // check that at some point we had the log "builder payload was not valid" which signals // that the builder returned a payload that was not valid and rollup-boost did not process it. From 4d1a9979c27ab3590cef41bd23deaa59195f809b Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Fri, 4 Apr 2025 11:36:37 -0700 Subject: [PATCH 17/20] lazylock to claim ports --- src/client/rpc.rs | 21 +++++++++++++-------- tests/common/mod.rs | 18 +++++++++++------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/client/rpc.rs b/src/client/rpc.rs index e30276d0..2d094712 100644 --- a/src/client/rpc.rs +++ b/src/client/rpc.rs @@ -351,10 +351,11 @@ macro_rules! define_rpc_args { define_rpc_args!((BuilderArgs, builder), (L2ClientArgs, l2)); #[cfg(test)] -mod tests { +pub mod tests { use assert_cmd::Command; use http::Uri; use jsonrpsee::core::client::ClientT; + use parking_lot::Mutex; use crate::client::auth::AuthClientService; use crate::server::PayloadSource; @@ -370,29 +371,33 @@ mod tests { }; use predicates::prelude::*; use reth_rpc_layer::{AuthLayer, JwtAuthValidator}; + use std::collections::HashSet; use std::net::SocketAddr; use std::net::TcpListener; use std::result::Result; use std::str::FromStr; + use std::sync::LazyLock; use super::*; const AUTH_ADDR: &str = "0.0.0.0"; const SECRET: &str = "f79ae8046bc11c9927afe911db7143c51a806c4a537cc08e0d37140b0192f430"; - fn get_available_port() -> u16 { + pub fn get_available_port() -> u16 { + static CLAIMED_PORTS: LazyLock>> = + LazyLock::new(|| Mutex::new(HashSet::new())); loop { let port: u16 = rand::random_range(1000..20000); - if port_is_available(port) { - return port; + match TcpListener::bind(("127.0.0.1", port)) { + Ok(_) => { + CLAIMED_PORTS.lock().insert(port); + return port; + } + Err(_) => continue, } } } - fn port_is_available(port: u16) -> bool { - TcpListener::bind(("127.0.0.1", port)).is_ok() - } - #[test] fn test_invalid_args() { let mut cmd = Command::cargo_bin("rollup-boost").unwrap(); diff --git a/tests/common/mod.rs b/tests/common/mod.rs index b051ee5d..adc09f9e 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -14,6 +14,7 @@ use jsonrpsee::http_client::{HttpClient, transport::HttpBackend}; use jsonrpsee::proc_macros::rpc; use op_alloy_consensus::TxDeposit; use op_alloy_rpc_types_engine::OpPayloadAttributes; +use parking_lot::Mutex; use proxy::{ProxyHandler, start_proxy_server}; use rollup_boost::DebugClient; use rollup_boost::{AuthClientLayer, AuthClientService}; @@ -21,6 +22,7 @@ use rollup_boost::{EngineApiClient, OpExecutionPayloadEnvelope, Version}; use rollup_boost::{NewPayload, PayloadSource}; use services::op_reth::{AUTH_RPC_PORT, OpRethConfig, OpRethImage, OpRethMehods, P2P_PORT}; use services::rollup_boost::{RollupBoost, RollupBoostConfig}; +use std::collections::HashSet; use std::net::TcpListener; use std::path::PathBuf; use std::str::FromStr; @@ -477,15 +479,17 @@ fn create_deposit_tx() -> Bytes { buffer_without_header.to_vec().into() } -fn get_available_port() -> u16 { +pub fn get_available_port() -> u16 { + static CLAIMED_PORTS: LazyLock>> = + LazyLock::new(|| Mutex::new(HashSet::new())); loop { let port: u16 = rand::random_range(1000..20000); - if port_is_available(port) { - return port; + match TcpListener::bind(("127.0.0.1", port)) { + Ok(_) => { + CLAIMED_PORTS.lock().insert(port); + return port; + } + Err(_) => continue, } } } - -fn port_is_available(port: u16) -> bool { - TcpListener::bind(("127.0.0.1", port)).is_ok() -} From 7a7af6d3ab72c5cdf98d83e31a78114973d13c3b Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Fri, 4 Apr 2025 11:43:17 -0700 Subject: [PATCH 18/20] random network string --- tests/common/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/common/mod.rs b/tests/common/mod.rs index adc09f9e..81c89189 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -43,7 +43,6 @@ pub const JWT_SECRET: &str = "688f5d737bad920bdfb2fc2f488d6b6209eebda1dae949a8de pub const L2_P2P_ENODE: &str = "3479db4d9217fb5d7a8ed4d61ac36e120b05d36c2eefb795dc42ff2e971f251a2315f5649ea1833271e020b9adc98d5db9973c7ed92d6b2f1f2223088c3d852f"; pub static TEST_DATA: LazyLock = LazyLock::new(|| format!("{}/tests/common/test_data", env!("CARGO_MANIFEST_DIR"))); -pub const NETWORK: &str = "devnet"; pub mod proxy; pub mod services; @@ -221,6 +220,7 @@ impl RollupBoostTestHarnessBuilder { } pub async fn build(self) -> eyre::Result { + let network = rand::random::().to_string(); let l2_log_consumer = self.log_consumer("l2").await?; let builder_log_consumer = self.log_consumer("builder").await?; let rollup_boost_log_file_path = self.file_path("rollup_boost")?; @@ -235,7 +235,7 @@ impl RollupBoostTestHarnessBuilder { .with_mapped_port(l2_p2p_port, ContainerPort::Tcp(P2P_PORT)) .with_mapped_port(l2_p2p_port, ContainerPort::Udp(P2P_PORT)) .with_mapped_port(get_available_port(), ContainerPort::Tcp(AUTH_RPC_PORT)) - .with_network(NETWORK) + .with_network(&network) .with_log_consumer(l2_log_consumer) .start() .await?; @@ -253,7 +253,7 @@ impl RollupBoostTestHarnessBuilder { .with_mapped_port(builder_p2p_port, ContainerPort::Tcp(P2P_PORT)) .with_mapped_port(builder_p2p_port, ContainerPort::Udp(P2P_PORT)) .with_mapped_port(get_available_port(), ContainerPort::Tcp(AUTH_RPC_PORT)) - .with_network(NETWORK) + .with_network(&network) .with_log_consumer(builder_log_consumer) .start() .await?; From 20001688564bcf267853a22aa9387cbbf02932ba Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Fri, 4 Apr 2025 11:51:10 -0700 Subject: [PATCH 19/20] check Claimed Ports before inserting --- src/client/rpc.rs | 8 ++------ tests/common/mod.rs | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/client/rpc.rs b/src/client/rpc.rs index 2d094712..37a7a786 100644 --- a/src/client/rpc.rs +++ b/src/client/rpc.rs @@ -388,12 +388,8 @@ pub mod tests { LazyLock::new(|| Mutex::new(HashSet::new())); loop { let port: u16 = rand::random_range(1000..20000); - match TcpListener::bind(("127.0.0.1", port)) { - Ok(_) => { - CLAIMED_PORTS.lock().insert(port); - return port; - } - Err(_) => continue, + if TcpListener::bind(("127.0.0.1", port)).is_ok() && CLAIMED_PORTS.lock().insert(port) { + return port; } } } diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 81c89189..929f71dd 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -484,12 +484,8 @@ pub fn get_available_port() -> u16 { LazyLock::new(|| Mutex::new(HashSet::new())); loop { let port: u16 = rand::random_range(1000..20000); - match TcpListener::bind(("127.0.0.1", port)) { - Ok(_) => { - CLAIMED_PORTS.lock().insert(port); - return port; - } - Err(_) => continue, + if TcpListener::bind(("127.0.0.1", port)).is_ok() && CLAIMED_PORTS.lock().insert(port) { + return port; } } } From ecb42cf9200b0f408146dba0a2295c6e2976123d Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Fri, 4 Apr 2025 12:03:59 -0700 Subject: [PATCH 20/20] improve forward_miner_set_gas_price test reliability --- src/proxy.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/proxy.rs b/src/proxy.rs index fabba3d0..d0bfcd40 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -639,7 +639,6 @@ mod tests { #[tokio::test] async fn test_forward_miner_set_gas_price() -> eyre::Result<()> { - tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; let test_harness = TestHarness::new().await?; let gas_price = U128::ZERO; @@ -651,6 +650,7 @@ mod tests { .await?; let expected_price = json!(gas_price); + tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; // Assert the builder received the correct payload let builder = &test_harness.builder;