From be7ad75e665819fcf9c03f62817ed487b9119116 Mon Sep 17 00:00:00 2001 From: Konstantin Zolotarev Date: Mon, 23 Sep 2024 22:00:31 +0300 Subject: [PATCH 01/40] TMP commit --- Cargo.lock | 3677 ++++++++++++++++-------------------- Cargo.toml | 32 +- abi/ScribeOptimistic.json | 1531 --------------- challenger/Cargo.toml | 27 - challenger/src/contract.rs | 212 --- challenger/src/lib.rs | 879 --------- challenger/src/metrics.rs | 67 - src/main.rs | 143 +- src/wallet.rs | 37 +- 9 files changed, 1742 insertions(+), 4863 deletions(-) delete mode 100644 abi/ScribeOptimistic.json delete mode 100644 challenger/Cargo.toml delete mode 100644 challenger/src/contract.rs delete mode 100644 challenger/src/lib.rs delete mode 100644 challenger/src/metrics.rs diff --git a/Cargo.lock b/Cargo.lock index b4cb412..1ebb3f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,30 +2,20 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "Inflector" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" -dependencies = [ - "lazy_static", - "regex", -] - [[package]] name = "addr2line" -version = "0.21.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aes" @@ -52,347 +42,1001 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] -name = "android-tzdata" -version = "0.1.1" +name = "allocator-api2" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] -name = "android_system_properties" -version = "0.1.5" +name = "alloy" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +checksum = "8367891bf380210abb0d6aa30c5f85a9080cb4a066c4d5c5acadad630823751b" +dependencies = [ + "alloy-consensus", + "alloy-contract", + "alloy-core", + "alloy-eips", + "alloy-genesis", + "alloy-network", + "alloy-provider", + "alloy-pubsub", + "alloy-rpc-client", + "alloy-rpc-types", + "alloy-serde", + "alloy-signer", + "alloy-signer-local", + "alloy-transport", + "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", +] + +[[package]] +name = "alloy-chains" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf770dad29577cd3580f3dd09005799224a912b8cdfdd6dc04d030d42b3df4e" dependencies = [ - "libc", + "num_enum", + "strum", ] [[package]] -name = "anstream" -version = "0.6.13" +name = "alloy-consensus" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +checksum = "629b62e38d471cc15fea534eb7283d2f8a4e8bdb1811bcc5d66dda6cfce6fae1" dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "utf8parse", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "c-kzg", + "serde", ] [[package]] -name = "anstyle" -version = "1.0.6" +name = "alloy-contract" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "0eefe64fd344cffa9cf9e3435ec4e93e6e9c3481bc37269af988bf497faf4a6a" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-network", + "alloy-network-primitives", + "alloy-primitives", + "alloy-provider", + "alloy-pubsub", + "alloy-rpc-types-eth", + "alloy-sol-types", + "alloy-transport", + "futures", + "futures-util", + "thiserror", +] [[package]] -name = "anstyle-parse" -version = "0.2.3" +name = "alloy-core" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "88b095eb0533144b4497e84a9cc3e44a5c2e3754a3983c0376a55a2f9183a53e" dependencies = [ - "utf8parse", + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-types", ] [[package]] -name = "anstyle-query" -version = "1.0.2" +name = "alloy-dyn-abi" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "4004925bff5ba0a11739ae84dbb6601a981ea692f3bd45b626935ee90a6b8471" dependencies = [ - "windows-sys 0.52.0", + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-type-parser", + "alloy-sol-types", + "const-hex", + "itoa", + "serde", + "serde_json", + "winnow", ] [[package]] -name = "anstyle-wincon" -version = "3.0.2" +name = "alloy-eip2930" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "0069cf0642457f87a01a014f6dc29d5d893cd4fd8fddf0c3cdfad1bb3ebafc41" dependencies = [ - "anstyle", - "windows-sys 0.52.0", + "alloy-primitives", + "alloy-rlp", + "serde", ] [[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" - -[[package]] -name = "ascii-canvas" -version = "3.0.0" +name = "alloy-eip7702" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" +checksum = "37d319bb544ca6caeab58c39cea8921c55d924d4f68f2c60f24f914673f9a74a" dependencies = [ - "term", + "alloy-primitives", + "alloy-rlp", + "k256", + "serde", ] [[package]] -name = "async-trait" -version = "0.1.77" +name = "alloy-eips" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +checksum = "f923dd5fca5f67a43d81ed3ebad0880bd41f6dd0ada930030353ac356c54cd0f" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", + "alloy-eip2930", + "alloy-eip7702", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "c-kzg", + "derive_more", + "once_cell", + "serde", + "sha2", ] [[package]] -name = "async_io_stream" -version = "0.3.3" +name = "alloy-genesis" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" +checksum = "3a7a18afb0b318616b6b2b0e2e7ac5529d32a966c673b48091c9919e284e6aca" dependencies = [ - "futures", - "pharos", - "rustc_version", + "alloy-primitives", + "alloy-serde", + "serde", ] [[package]] -name = "auto_impl" -version = "1.2.0" +name = "alloy-json-abi" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" +checksum = "9996daf962fd0a90d3c93b388033228865953b92de7bb1959b891d78750a4091" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", + "alloy-primitives", + "alloy-sol-type-parser", + "serde", + "serde_json", ] [[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "backtrace" -version = "0.3.69" +name = "alloy-json-rpc" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "d3c717b5298fad078cd3a418335b266eba91b511383ca9bd497f742d5975d5ab" dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", + "alloy-primitives", + "alloy-sol-types", + "serde", + "serde_json", + "thiserror", + "tracing", ] [[package]] -name = "base16ct" -version = "0.2.0" +name = "alloy-network" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +checksum = "fb3705ce7d8602132bcf5ac7a1dd293a42adc2f183abf5907c30ac535ceca049" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-json-rpc", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "alloy-signer", + "alloy-sol-types", + "async-trait", + "auto_impl", + "futures-utils-wasm", + "thiserror", +] [[package]] -name = "base64" -version = "0.13.1" +name = "alloy-network-primitives" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +checksum = "94ad40869867ed2d9cd3842b1e800889e5b49e6b92da346e93862b4a741bedf3" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-serde", + "serde", +] [[package]] -name = "base64" -version = "0.21.7" +name = "alloy-primitives" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +checksum = "411aff151f2a73124ee473708e82ed51b2535f68928b6a1caa8bc1246ae6f7cd" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "hex-literal", + "itoa", + "k256", + "keccak-asm", + "proptest", + "rand", + "ruint", + "serde", + "tiny-keccak", +] [[package]] -name = "base64" -version = "0.22.0" +name = "alloy-provider" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" +checksum = "927f708dd457ed63420400ee5f06945df9632d5d101851952056840426a10dc5" +dependencies = [ + "alloy-chains", + "alloy-consensus", + "alloy-eips", + "alloy-json-rpc", + "alloy-network", + "alloy-network-primitives", + "alloy-primitives", + "alloy-pubsub", + "alloy-rpc-client", + "alloy-rpc-types-eth", + "alloy-transport", + "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", + "async-stream", + "async-trait", + "auto_impl", + "dashmap", + "futures", + "futures-utils-wasm", + "lru", + "pin-project", + "reqwest", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "url", +] [[package]] -name = "base64ct" -version = "1.6.0" +name = "alloy-pubsub" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +checksum = "2d05f63677e210d758cd5d6d1ce10f20c980c3560ccfbe79ba1997791862a04f" +dependencies = [ + "alloy-json-rpc", + "alloy-primitives", + "alloy-transport", + "bimap", + "futures", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower 0.5.1", + "tracing", +] [[package]] -name = "bech32" -version = "0.9.1" +name = "alloy-rlp" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +checksum = "26154390b1d205a4a7ac7352aa2eb4f81f391399d4e2f546fb81a2f8bb383f62" +dependencies = [ + "alloy-rlp-derive", + "arrayvec", + "bytes", +] [[package]] -name = "bindgen" -version = "0.69.4" +name = "alloy-rlp-derive" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +checksum = "4d0f2d905ebd295e7effec65e5f6868d153936130ae718352771de3e7d03c75c" dependencies = [ - "bitflags 2.4.2", - "cexpr", - "clang-sys", - "itertools", - "lazy_static", - "lazycell", "proc-macro2", "quote", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.52", + "syn 2.0.77", ] [[package]] -name = "bit-set" -version = "0.5.3" +name = "alloy-rpc-client" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +checksum = "7d82952dca71173813d4e5733e2c986d8b04aea9e0f3b0a576664c232ad050a5" dependencies = [ - "bit-vec", + "alloy-json-rpc", + "alloy-primitives", + "alloy-pubsub", + "alloy-transport", + "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", + "futures", + "pin-project", + "reqwest", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower 0.5.1", + "tracing", + "url", ] [[package]] -name = "bit-vec" -version = "0.6.3" +name = "alloy-rpc-types" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +checksum = "64333d639f2a0cf73491813c629a405744e16343a4bc5640931be707c345ecc5" +dependencies = [ + "alloy-rpc-types-engine", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", +] [[package]] -name = "bitflags" -version = "1.3.2" +name = "alloy-rpc-types-engine" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "1464c4dd646e1bdfde86ae65ce5ba168dbb29180b478011fe87117ae46b1629b" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "derive_more", +] [[package]] -name = "bitflags" -version = "2.4.2" +name = "alloy-rpc-types-eth" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "83aa984386deda02482660aa31cb8ca1e63d533f1c31a52d7d181ac5ec68e9b8" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "alloy-sol-types", + "cfg-if", + "derive_more", + "hashbrown", + "itertools 0.13.0", + "serde", + "serde_json", +] [[package]] -name = "bitvec" -version = "1.0.1" +name = "alloy-serde" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +checksum = "731f75ec5d383107fd745d781619bd9cedf145836c51ecb991623d41278e71fa" dependencies = [ - "funty", - "radium", - "tap", - "wyz", + "alloy-primitives", + "serde", + "serde_json", ] [[package]] -name = "block-buffer" -version = "0.10.4" +name = "alloy-signer" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +checksum = "307324cca94354cd654d6713629f0383ec037e1ff9e3e3d547212471209860c0" dependencies = [ - "generic-array", + "alloy-primitives", + "async-trait", + "auto_impl", + "elliptic-curve", + "k256", + "thiserror", ] [[package]] -name = "bs58" -version = "0.5.0" +name = "alloy-signer-local" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" +checksum = "9fabe917ab1778e760b4701628d1cae8e028ee9d52ac6307de4e1e9286ab6b5f" dependencies = [ - "sha2", - "tinyvec", + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "elliptic-curve", + "eth-keystore", + "k256", + "rand", + "thiserror", ] [[package]] -name = "bumpalo" -version = "3.15.4" +name = "alloy-sol-macro" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" +checksum = "0458ccb02a564228fcd76efb8eb5a520521a8347becde37b402afec9a1b83859" +dependencies = [ + "alloy-sol-macro-expander", + "alloy-sol-macro-input", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.77", +] [[package]] -name = "byte-slice-cast" -version = "1.2.2" +name = "alloy-sol-macro-expander" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" +checksum = "2bc65475025fc1e84bf86fc840f04f63fcccdcf3cf12053c99918e4054dfbc69" +dependencies = [ + "alloy-json-abi", + "alloy-sol-macro-input", + "const-hex", + "heck", + "indexmap", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.77", + "syn-solidity", + "tiny-keccak", +] [[package]] -name = "byteorder" -version = "1.5.0" +name = "alloy-sol-macro-input" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +checksum = "6ed10f0715a0b69fde3236ff3b9ae5f6f7c97db5a387747100070d3016b9266b" +dependencies = [ + "alloy-json-abi", + "const-hex", + "dunce", + "heck", + "proc-macro2", + "quote", + "serde_json", + "syn 2.0.77", + "syn-solidity", +] [[package]] -name = "bytes" -version = "1.5.0" +name = "alloy-sol-type-parser" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "3edae8ea1de519ccba896b6834dec874230f72fe695ff3c9c118e90ec7cff783" dependencies = [ "serde", + "winnow", ] [[package]] -name = "bzip2" -version = "0.4.4" +name = "alloy-sol-types" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +checksum = "1eb88e4da0a1b697ed6a9f811fdba223cf4d5c21410804fd1707836af73a462b" dependencies = [ - "bzip2-sys", - "libc", + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-macro", + "const-hex", + "serde", ] [[package]] -name = "bzip2-sys" -version = "0.1.11+1.0.8" +name = "alloy-transport" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +checksum = "33616b2edf7454302a1d48084db185e52c309f73f6c10be99b0fe39354b3f1e9" dependencies = [ - "cc", + "alloy-json-rpc", + "base64 0.22.1", + "futures-util", + "futures-utils-wasm", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower 0.5.1", + "tracing", + "url", +] + +[[package]] +name = "alloy-transport-http" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a944f5310c690b62bbb3e7e5ce34527cbd36b2d18532a797af123271ce595a49" +dependencies = [ + "alloy-json-rpc", + "alloy-transport", + "reqwest", + "serde_json", + "tower 0.5.1", + "tracing", + "url", +] + +[[package]] +name = "alloy-transport-ipc" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09fd8491249f74d16ec979b1f5672377b12ebb818e6056478ffa386954dbd350" +dependencies = [ + "alloy-json-rpc", + "alloy-pubsub", + "alloy-transport", + "bytes", + "futures", + "interprocess", + "pin-project", + "serde_json", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "alloy-transport-ws" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9704761f6297fe482276bee7f77a93cb42bd541c2bd6c1c560b6f3a9ece672e" +dependencies = [ + "alloy-pubsub", + "alloy-transport", + "futures", + "http 1.1.0", + "rustls", + "serde_json", + "tokio", + "tokio-tungstenite 0.23.1", + "tracing", + "ws_stream_wasm", +] + +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.1", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "async-trait" +version = "0.1.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "async_io_stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" +dependencies = [ + "futures", + "pharos", + "rustc_version 0.4.1", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "auto_impl" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", "libc", - "pkg-config", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base16ct" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bimap" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" + +[[package]] +name = "bindgen" +version = "0.69.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.77", ] [[package]] -name = "camino" -version = "1.1.6" +name = "bit-set" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ - "serde", + "bit-vec", ] [[package]] -name = "cargo-platform" -version = "0.1.7" +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "694c8807f2ae16faecc43dc17d74b3eb042482789fd0eb64b39a2e04e087053f" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blst" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4378725facc195f1a538864863f6de233b500a8862747e7f165078a419d5e874" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" dependencies = [ "serde", ] [[package]] -name = "cargo_metadata" -version = "0.18.1" +name = "c-kzg" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +checksum = "f0307f72feab3300336fb803a57134159f6e20139af1357f36c54cb90d8e8928" dependencies = [ - "camino", - "cargo-platform", - "semver", + "blst", + "cc", + "glob", + "hex", + "libc", + "once_cell", "serde", - "serde_json", - "thiserror", ] [[package]] name = "cc" -version = "1.0.90" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" dependencies = [ - "jobserver", - "libc", + "shlex", ] [[package]] @@ -414,52 +1058,19 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" name = "challenger" version = "0.1.0" dependencies = [ - "challenger-lib", + "alloy", "clap", "env_logger", - "ethers", "eyre", "log", "metrics-exporter-prometheus", "metrics-process", "rpassword", - "tokio", - "warp", -] - -[[package]] -name = "challenger-lib" -version = "0.1.0" -dependencies = [ - "async-trait", - "chrono", - "env_logger", - "ethers", - "eyre", - "futures", - "hex", - "hex-literal", - "lazy_static", - "log", - "metrics", - "mockall", - "prometheus", + "scribe", "tokio", "tokio-util", -] - -[[package]] -name = "chrono" -version = "0.4.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "wasm-bindgen", - "windows-targets 0.52.4", + "tower 0.5.1", + "warp", ] [[package]] @@ -474,9 +1085,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", @@ -485,9 +1096,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.2" +version = "4.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b230ab84b0ffdf890d5a10abdbc8b83ae1c4918275daea1ab8801f71536b2651" +checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" dependencies = [ "clap_builder", "clap_derive", @@ -495,9 +1106,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" dependencies = [ "anstream", "anstyle", @@ -507,85 +1118,33 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.0" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.77", ] [[package]] name = "clap_lex" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" - -[[package]] -name = "coins-bip32" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b6be4a5df2098cd811f3194f64ddb96c267606bffd9689ac7b0160097b01ad3" -dependencies = [ - "bs58", - "coins-core", - "digest", - "hmac", - "k256", - "serde", - "sha2", - "thiserror", -] - -[[package]] -name = "coins-bip39" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db8fba409ce3dc04f7d804074039eb68b960b0829161f8e06c95fea3f122528" -dependencies = [ - "bitvec", - "coins-bip32", - "hmac", - "once_cell", - "pbkdf2 0.12.2", - "rand", - "sha2", - "thiserror", -] - -[[package]] -name = "coins-core" -version = "0.8.7" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5286a0843c21f8367f7be734f89df9b822e0321d8bcce8d6e735aadff7d74979" -dependencies = [ - "base64 0.21.7", - "bech32", - "bs58", - "digest", - "generic-array", - "hex", - "ripemd", - "serde", - "serde_derive", - "sha2", - "sha3", - "thiserror", -] +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "const-hex" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbd12d49ab0eaf8193ba9175e45f56bbc2e4b27d57b8cfe62aa47942a46b9a9" +checksum = "94fb8a24a26d37e1ffd45343323dc9fe6654ceea44c12f2fcb3d7ac29e610bc6" dependencies = [ "cfg-if", "cpufeatures", @@ -600,12 +1159,6 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - [[package]] name = "core-foundation" version = "0.9.4" @@ -618,38 +1171,19 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] -[[package]] -name = "crc32fast" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - [[package]] name = "crossbeam-epoch" version = "0.9.18" @@ -661,9 +1195,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crunchy" @@ -702,36 +1236,41 @@ dependencies = [ "cipher", ] +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "data-encoding" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "der" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "zeroize", ] [[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", -] - -[[package]] -name = "derive_more" -version = "0.99.17" +name = "derivative" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2", "quote", @@ -739,70 +1278,58 @@ dependencies = [ ] [[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", - "subtle", -] - -[[package]] -name = "dirs" -version = "5.0.1" +name = "derive_more" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" dependencies = [ - "dirs-sys", + "derive_more-impl", ] [[package]] -name = "dirs-next" -version = "2.0.0" +name = "derive_more-impl" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ - "cfg-if", - "dirs-sys-next", + "proc-macro2", + "quote", + "syn 2.0.77", + "unicode-xid", ] [[package]] -name = "dirs-sys" -version = "0.4.1" +name = "digest" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", + "generic-array", ] [[package]] -name = "dirs-sys-next" -version = "0.1.2" +name = "digest" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "libc", - "redox_users", - "winapi", + "block-buffer", + "const-oid", + "crypto-common", + "subtle", ] [[package]] -name = "downcast" -version = "0.11.0" +name = "doctest-file" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" +checksum = "aac81fa3e28d21450aa4d2ac065992ba96a1d7303efbce51a95f4fd175b67562" [[package]] name = "dunce" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "ecdsa" @@ -811,7 +1338,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", - "digest", + "digest 0.10.7", "elliptic-curve", "rfc6979", "signature", @@ -819,421 +1346,98 @@ dependencies = [ ] [[package]] -name = "either" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" - -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest", - "ff", - "generic-array", - "group", - "pkcs8", - "rand_core", - "sec1", - "subtle", - "zeroize", -] - -[[package]] -name = "ena" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" -dependencies = [ - "log", -] - -[[package]] -name = "encoding_rs" -version = "0.8.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "enr" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a3d8dc56e02f954cac8eb489772c552c473346fc34f67412bb6244fd647f7e4" -dependencies = [ - "base64 0.21.7", - "bytes", - "hex", - "k256", - "log", - "rand", - "rlp", - "serde", - "sha3", - "zeroize", -] - -[[package]] -name = "env_filter" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "env_logger" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" -dependencies = [ - "anstream", - "anstyle", - "env_filter", - "humantime", - "log", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "errno" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "eth-keystore" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab" -dependencies = [ - "aes", - "ctr", - "digest", - "hex", - "hmac", - "pbkdf2 0.11.0", - "rand", - "scrypt", - "serde", - "serde_json", - "sha2", - "sha3", - "thiserror", - "uuid", -] - -[[package]] -name = "ethabi" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" -dependencies = [ - "ethereum-types", - "hex", - "once_cell", - "regex", - "serde", - "serde_json", - "sha3", - "thiserror", - "uint", -] - -[[package]] -name = "ethbloom" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" -dependencies = [ - "crunchy", - "fixed-hash", - "impl-codec", - "impl-rlp", - "impl-serde", - "scale-info", - "tiny-keccak", -] - -[[package]] -name = "ethereum-types" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" -dependencies = [ - "ethbloom", - "fixed-hash", - "impl-codec", - "impl-rlp", - "impl-serde", - "primitive-types", - "scale-info", - "uint", -] - -[[package]] -name = "ethers" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "816841ea989f0c69e459af1cf23a6b0033b19a55424a1ea3a30099becdb8dec0" -dependencies = [ - "ethers-addressbook", - "ethers-contract", - "ethers-core", - "ethers-etherscan", - "ethers-middleware", - "ethers-providers", - "ethers-signers", - "ethers-solc", -] - -[[package]] -name = "ethers-addressbook" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5495afd16b4faa556c3bba1f21b98b4983e53c1755022377051a975c3b021759" -dependencies = [ - "ethers-core", - "once_cell", - "serde", - "serde_json", -] - -[[package]] -name = "ethers-contract" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fceafa3578c836eeb874af87abacfb041f92b4da0a78a5edd042564b8ecdaaa" -dependencies = [ - "const-hex", - "ethers-contract-abigen", - "ethers-contract-derive", - "ethers-core", - "ethers-providers", - "futures-util", - "once_cell", - "pin-project", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "ethers-contract-abigen" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04ba01fbc2331a38c429eb95d4a570166781f14290ef9fdb144278a90b5a739b" -dependencies = [ - "Inflector", - "const-hex", - "dunce", - "ethers-core", - "ethers-etherscan", - "eyre", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "reqwest", - "serde", - "serde_json", - "syn 2.0.52", - "toml", - "walkdir", -] - -[[package]] -name = "ethers-contract-derive" -version = "2.0.14" +name = "either" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87689dcabc0051cde10caaade298f9e9093d65f6125c14575db3fd8c669a168f" -dependencies = [ - "Inflector", - "const-hex", - "ethers-contract-abigen", - "ethers-core", - "proc-macro2", - "quote", - "serde_json", - "syn 2.0.52", -] +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] -name = "ethers-core" -version = "2.0.14" +name = "elliptic-curve" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d80cc6ad30b14a48ab786523af33b37f28a8623fc06afd55324816ef18fb1f" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ - "arrayvec", - "bytes", - "cargo_metadata", - "chrono", - "const-hex", - "elliptic-curve", - "ethabi", + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", "generic-array", - "k256", - "num_enum", - "once_cell", - "open-fastrlp", - "rand", - "rlp", - "serde", - "serde_json", - "strum", - "syn 2.0.52", - "tempfile", - "thiserror", - "tiny-keccak", - "unicode-xid", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", ] [[package]] -name = "ethers-etherscan" -version = "2.0.14" +name = "encoding_rs" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79e5973c26d4baf0ce55520bd732314328cabe53193286671b47144145b9649" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ - "chrono", - "ethers-core", - "reqwest", - "semver", - "serde", - "serde_json", - "thiserror", - "tracing", + "cfg-if", ] [[package]] -name = "ethers-middleware" -version = "2.0.14" +name = "env_filter" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48f9fdf09aec667c099909d91908d5eaf9be1bd0e2500ba4172c1d28bfaa43de" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" dependencies = [ - "async-trait", - "auto_impl", - "ethers-contract", - "ethers-core", - "ethers-etherscan", - "ethers-providers", - "ethers-signers", - "futures-channel", - "futures-locks", - "futures-util", - "instant", - "reqwest", - "serde", - "serde_json", - "thiserror", - "tokio", - "tracing", - "tracing-futures", - "url", + "log", + "regex", ] [[package]] -name = "ethers-providers" -version = "2.0.14" +name = "env_logger" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6434c9a33891f1effc9c75472e12666db2fa5a0fec4b29af6221680a6fe83ab2" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" dependencies = [ - "async-trait", - "auto_impl", - "base64 0.21.7", - "bytes", - "const-hex", - "enr", - "ethers-core", - "futures-core", - "futures-timer", - "futures-util", - "hashers", - "http 0.2.12", - "instant", - "jsonwebtoken", - "once_cell", - "pin-project", - "reqwest", - "serde", - "serde_json", - "thiserror", - "tokio", - "tokio-tungstenite", - "tracing", - "tracing-futures", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "ws_stream_wasm", + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", ] [[package]] -name = "ethers-signers" -version = "2.0.14" +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "228875491c782ad851773b652dd8ecac62cda8571d3bc32a5853644dd26766c2" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ - "async-trait", - "coins-bip32", - "coins-bip39", - "const-hex", - "elliptic-curve", - "eth-keystore", - "ethers-core", - "rand", - "sha2", - "thiserror", - "tracing", + "libc", + "windows-sys 0.52.0", ] [[package]] -name = "ethers-solc" -version = "2.0.14" +name = "eth-keystore" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66244a771d9163282646dbeffe0e6eca4dda4146b6498644e678ac6089b11edd" +checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab" dependencies = [ - "cfg-if", - "const-hex", - "dirs", - "dunce", - "ethers-core", - "glob", - "home", - "md-5", - "num_cpus", - "once_cell", - "path-slash", - "rayon", - "regex", - "semver", + "aes", + "ctr", + "digest 0.10.7", + "hex", + "hmac", + "pbkdf2", + "rand", + "scrypt", "serde", "serde_json", - "solang-parser", - "svm-rs", + "sha2", + "sha3", "thiserror", - "tiny-keccak", - "tokio", - "tracing", - "walkdir", - "yansi", + "uuid", ] [[package]] @@ -1248,9 +1452,20 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "fastrlp" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] [[package]] name = "ff" @@ -1274,22 +1489,6 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "flate2" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - [[package]] name = "fnv" version = "1.0.7" @@ -1320,22 +1519,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fragile" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" - -[[package]] -name = "fs2" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "funty" version = "2.0.0" @@ -1390,16 +1573,6 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" -[[package]] -name = "futures-locks" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45ec6fe3675af967e67c5536c0b9d44e34e6c52f86bedc4ea49c5317b8e94d06" -dependencies = [ - "futures-channel", - "futures-task", -] - [[package]] name = "futures-macro" version = "0.3.30" @@ -1408,7 +1581,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.77", ] [[package]] @@ -1423,16 +1596,6 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" -[[package]] -name = "futures-timer" -version = "3.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" -dependencies = [ - "gloo-timers", - "send_wrapper 0.4.0", -] - [[package]] name = "futures-util" version = "0.3.30" @@ -1452,13 +1615,10 @@ dependencies = [ ] [[package]] -name = "fxhash" -version = "0.2.1" +name = "futures-utils-wasm" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] +checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" [[package]] name = "generic-array" @@ -1473,9 +1633,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -1484,9 +1644,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" [[package]] name = "glob" @@ -1494,18 +1654,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" -[[package]] -name = "gloo-timers" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - [[package]] name = "group" version = "0.13.0" @@ -1538,15 +1686,15 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" dependencies = [ + "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "futures-util", "http 1.1.0", "indexmap", "slab", @@ -1557,20 +1705,13 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", -] - -[[package]] -name = "hashers" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2bca93b15ea5a746f220e56587f71e73c6165eab783df9e26590069953e3c30" -dependencies = [ - "fxhash", + "allocator-api2", + "serde", ] [[package]] @@ -1599,9 +1740,9 @@ dependencies = [ [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" @@ -1614,6 +1755,9 @@ name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] [[package]] name = "hex-literal" @@ -1627,16 +1771,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", -] - -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", + "digest 0.10.7", ] [[package]] @@ -1674,9 +1809,9 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http 1.1.0", @@ -1684,22 +1819,22 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", - "futures-core", + "futures-util", "http 1.1.0", - "http-body 1.0.0", + "http-body 1.0.1", "pin-project-lite", ] [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "httpdate" @@ -1715,9 +1850,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.28" +version = "0.14.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" dependencies = [ "bytes", "futures-channel", @@ -1739,16 +1874,16 @@ dependencies = [ [[package]] name = "hyper" -version = "1.2.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.4", + "h2 0.4.6", "http 1.1.0", - "http-body 1.0.0", + "http-body 1.0.1", "httparse", "httpdate", "itoa", @@ -1758,20 +1893,6 @@ dependencies = [ "want", ] -[[package]] -name = "hyper-rustls" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" -dependencies = [ - "futures-util", - "http 0.2.12", - "hyper 0.14.28", - "rustls", - "tokio", - "tokio-rustls", -] - [[package]] name = "hyper-tls" version = "0.6.0" @@ -1780,7 +1901,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.2.0", + "hyper 1.4.1", "hyper-util", "native-tls", "tokio", @@ -1790,47 +1911,24 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.3" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" dependencies = [ "bytes", "futures-channel", "futures-util", "http 1.1.0", - "http-body 1.0.0", - "hyper 1.2.0", + "http-body 1.0.1", + "hyper 1.4.1", "pin-project-lite", "socket2", "tokio", - "tower", + "tower 0.4.13", "tower-service", "tracing", ] -[[package]] -name = "iana-time-zone" -version = "0.1.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core 0.52.0", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - [[package]] name = "idna" version = "0.5.0" @@ -1850,24 +1948,6 @@ dependencies = [ "parity-scale-codec", ] -[[package]] -name = "impl-rlp" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" -dependencies = [ - "rlp", -] - -[[package]] -name = "impl-serde" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" -dependencies = [ - "serde", -] - [[package]] name = "impl-trait-for-tuples" version = "0.2.2" @@ -1887,9 +1967,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "2.2.5" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown", @@ -1905,76 +1985,72 @@ dependencies = [ ] [[package]] -name = "instant" -version = "0.1.12" +name = "interprocess" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "d2f4e4a06d42fab3e85ab1b419ad32b09eab58b901d40c57935ff92db3287a13" dependencies = [ - "cfg-if", + "doctest-file", + "futures-core", + "libc", + "recvmsg", + "tokio", + "widestring", + "windows-sys 0.52.0", ] [[package]] -name = "io-lifetimes" -version = "1.0.11" +name = "ipnet" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys 0.48.0", -] +checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" [[package]] -name = "ipnet" -version = "2.9.0" +name = "is_terminal_polyfill" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itertools" -version = "0.11.0" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] -name = "itoa" -version = "1.0.10" +name = "itertools" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] [[package]] -name = "jobserver" -version = "0.1.28" +name = "itertools" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ - "libc", + "either", ] [[package]] -name = "js-sys" -version = "0.3.69" +name = "itoa" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" -dependencies = [ - "wasm-bindgen", -] +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] -name = "jsonwebtoken" -version = "8.3.0" +name = "js-sys" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ - "base64 0.21.7", - "pem", - "ring 0.16.20", - "serde", - "serde_json", - "simple_asn1", + "wasm-bindgen", ] [[package]] @@ -1988,7 +2064,6 @@ dependencies = [ "elliptic-curve", "once_cell", "sha2", - "signature", ] [[package]] @@ -2001,40 +2076,20 @@ dependencies = [ ] [[package]] -name = "lalrpop" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" -dependencies = [ - "ascii-canvas", - "bit-set", - "ena", - "itertools", - "lalrpop-util", - "petgraph", - "regex", - "regex-syntax", - "string_cache", - "term", - "tiny-keccak", - "unicode-xid", - "walkdir", -] - -[[package]] -name = "lalrpop-util" -version = "0.20.2" +name = "keccak-asm" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" +checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" dependencies = [ - "regex-automata", + "digest 0.10.7", + "sha3-asm", ] [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lazycell" @@ -2044,18 +2099,18 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libloading" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.4", + "windows-targets 0.52.6", ] [[package]] @@ -2066,43 +2121,26 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libproc" -version = "0.14.6" +version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eb6497078a4c9c2aca63df56d8dce6eb4381d53a960f781a3a748f7ea97436d" +checksum = "ae9ea4b75e1a81675429dafe43441df1caea70081e82246a8cccf514884a88bb" dependencies = [ "bindgen", "errno", "libc", ] -[[package]] -name = "libredox" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" -dependencies = [ - "bitflags 2.4.2", - "libc", - "redox_syscall", -] - -[[package]] -name = "linux-raw-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" - [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -2110,34 +2148,33 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] -name = "mach2" -version = "0.4.2" +name = "lru" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" dependencies = [ - "libc", + "hashbrown", ] [[package]] -name = "md-5" -version = "0.10.6" +name = "mach2" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" dependencies = [ - "cfg-if", - "digest", + "libc", ] [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "metrics" @@ -2155,9 +2192,9 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d58e362dc7206e9456ddbcdbd53c71ba441020e62104703075a69151e38d85f" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "http-body-util", - "hyper 1.2.0", + "hyper 1.4.1", "hyper-tls", "hyper-util", "indexmap", @@ -2180,7 +2217,7 @@ dependencies = [ "mach2", "metrics", "once_cell", - "procfs 0.16.0", + "procfs", "rlimit", "windows", ] @@ -2208,9 +2245,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" -version = "2.0.4" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" dependencies = [ "mime", "unicase", @@ -2224,49 +2261,23 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "adler", + "adler2", ] [[package]] name = "mio" -version = "0.8.11" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ + "hermit-abi", "libc", "wasi", - "windows-sys 0.48.0", -] - -[[package]] -name = "mockall" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43766c2b5203b10de348ffe19f7e54564b64f3d6018ff7648d1e2d6d3a0f0a48" -dependencies = [ - "cfg-if", - "downcast", - "fragile", - "lazy_static", - "mockall_derive", - "predicates", - "predicates-tree", -] - -[[package]] -name = "mockall_derive" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cbce79ec385a1d4f54baa90a76401eb15d9cab93685f62e7e9f942aa00ae2" -dependencies = [ - "cfg-if", - "proc-macro2", - "quote", - "syn 2.0.52", + "windows-sys 0.52.0", ] [[package]] @@ -2283,17 +2294,16 @@ dependencies = [ "log", "memchr", "mime", - "spin 0.9.8", + "spin", "version_check", ] [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -2305,12 +2315,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "new_debug_unreachable" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" - [[package]] name = "nom" version = "7.1.3" @@ -2323,21 +2327,14 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ - "autocfg", "num-integer", "num-traits", ] -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - [[package]] name = "num-integer" version = "0.1.46" @@ -2349,9 +2346,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", @@ -2369,30 +2366,29 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.77", ] [[package]] name = "object" -version = "0.32.2" +version = "0.36.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" dependencies = [ "memchr", ] @@ -2403,38 +2399,13 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" -[[package]] -name = "open-fastrlp" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786393f80485445794f6043fd3138854dd109cc6c4bd1a6383db304c9ce9b9ce" -dependencies = [ - "arrayvec", - "auto_impl", - "bytes", - "ethereum-types", - "open-fastrlp-derive", -] - -[[package]] -name = "open-fastrlp-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" -dependencies = [ - "bytes", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "openssl" -version = "0.10.64" +version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ - "bitflags 2.4.2", + "bitflags", "cfg-if", "foreign-types", "libc", @@ -2451,7 +2422,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.77", ] [[package]] @@ -2462,9 +2433,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.102" +version = "0.9.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", @@ -2472,17 +2443,11 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - [[package]] name = "parity-scale-codec" -version = "3.6.9" +version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" +checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" dependencies = [ "arrayvec", "bitvec", @@ -2494,11 +2459,11 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.6.9" +version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" +checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" dependencies = [ - "proc-macro-crate 2.0.0", + "proc-macro-crate", "proc-macro2", "quote", "syn 1.0.109", @@ -2506,9 +2471,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -2516,33 +2481,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] -name = "password-hash" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" -dependencies = [ - "base64ct", - "rand_core", - "subtle", -] - -[[package]] -name = "path-slash" -version = "0.2.1" +name = "paste" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pbkdf2" @@ -2550,29 +2504,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "digest", - "hmac", - "password-hash", - "sha2", -] - -[[package]] -name = "pbkdf2" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" -dependencies = [ - "digest", - "hmac", -] - -[[package]] -name = "pem" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" -dependencies = [ - "base64 0.13.1", + "digest 0.10.7", ] [[package]] @@ -2582,13 +2514,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] -name = "petgraph" -version = "0.6.4" +name = "pest" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +checksum = "fdbef9d1d47087a895abd220ed25eb4ad973a5e26f6a4367b038c25e28dfc2d9" dependencies = [ - "fixedbitset", - "indexmap", + "memchr", + "thiserror", + "ucd-trie", ] [[package]] @@ -2598,58 +2531,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" dependencies = [ "futures", - "rustc_version", -] - -[[package]] -name = "phf" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" -dependencies = [ - "phf_macros", - "phf_shared 0.11.2", -] - -[[package]] -name = "phf_generator" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" -dependencies = [ - "phf_shared 0.11.2", - "rand", -] - -[[package]] -name = "phf_macros" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" -dependencies = [ - "phf_generator", - "phf_shared 0.11.2", - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", -] - -[[package]] -name = "phf_shared" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" -dependencies = [ - "siphasher", + "rustc_version 0.4.1", ] [[package]] @@ -2669,14 +2551,14 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.77", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -2702,62 +2584,17 @@ checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "portable-atomic" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" - -[[package]] -name = "powerfmt" -version = "0.2.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" [[package]] name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - -[[package]] -name = "predicates" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8" -dependencies = [ - "anstyle", - "predicates-core", -] - -[[package]] -name = "predicates-core" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" - -[[package]] -name = "predicates-tree" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" -dependencies = [ - "predicates-core", - "termtree", -] - -[[package]] -name = "prettyplease" -version = "0.2.16" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "proc-macro2", - "syn 2.0.52", + "zerocopy", ] [[package]] @@ -2768,73 +2605,60 @@ checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", "impl-codec", - "impl-rlp", - "impl-serde", - "scale-info", "uint", ] [[package]] name = "proc-macro-crate" -version = "1.3.1" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "once_cell", - "toml_edit 0.19.15", + "toml_edit", ] [[package]] -name = "proc-macro-crate" +name = "proc-macro-error-attr2" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" dependencies = [ - "toml_edit 0.20.7", + "proc-macro2", + "quote", ] [[package]] -name = "proc-macro-crate" -version = "3.1.0" +name = "proc-macro-error2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" dependencies = [ - "toml_edit 0.21.1", + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.77", ] [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] -[[package]] -name = "procfs" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de8dacb0873f77e6aefc6d71e044761fcc68060290f5b1089fcdf84626bb69" -dependencies = [ - "bitflags 1.3.2", - "byteorder", - "hex", - "lazy_static", - "rustix 0.36.17", -] - [[package]] name = "procfs" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "731e0d9356b0c25f16f33b5be79b1c57b562f141ebfcdb0ad8ac2c13a24293b4" dependencies = [ - "bitflags 2.4.2", + "bitflags", "hex", "lazy_static", "procfs-core", - "rustix 0.38.31", + "rustix", ] [[package]] @@ -2843,49 +2667,30 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d3554923a69f4ce04c4a754260c338f505ce22642d3830e049a399fc2059a29" dependencies = [ - "bitflags 2.4.2", - "hex", -] - -[[package]] -name = "prometheus" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" -dependencies = [ - "cfg-if", - "fnv", - "lazy_static", - "libc", - "memchr", - "parking_lot", - "procfs 0.14.2", - "protobuf", - "thiserror", + "bitflags", + "hex", ] [[package]] name = "proptest" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" dependencies = [ - "bitflags 2.4.2", + "bit-set", + "bit-vec", + "bitflags", "lazy_static", "num-traits", "rand", "rand_chacha", "rand_xorshift", "regex-syntax", + "rusty-fork", + "tempfile", "unarray", ] -[[package]] -name = "protobuf" -version = "2.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" - [[package]] name = "quanta" version = "0.12.3" @@ -2901,11 +2706,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quote" -version = "1.0.35" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -2957,58 +2768,33 @@ dependencies = [ [[package]] name = "raw-cpuid" -version = "11.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d86a7c4638d42c44551f4791a20e687dbb4c3de1f33c43dd71e355cd429def1" -dependencies = [ - "bitflags 2.4.2", -] - -[[package]] -name = "rayon" -version = "1.9.0" +version = "11.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" +checksum = "cb9ee317cfe3fbd54b36a511efc1edd42e216903c9cd575e686dd68a2ba90d8d" dependencies = [ - "either", - "rayon-core", + "bitflags", ] [[package]] -name = "rayon-core" -version = "1.12.1" +name = "recvmsg" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] +checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" [[package]] name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_users" -version = "0.4.4" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" dependencies = [ - "getrandom", - "libredox", - "thiserror", + "bitflags", ] [[package]] name = "regex" -version = "1.10.3" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -3018,9 +2804,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -3029,49 +2815,47 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "reqwest" -version = "0.11.24" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" +checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "bytes", - "encoding_rs", "futures-core", "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.28", - "hyper-rustls", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-tls", + "hyper-util", "ipnet", "js-sys", "log", "mime", + "native-tls", "once_cell", "percent-encoding", "pin-project-lite", - "rustls", "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", - "system-configuration", + "sync_wrapper 1.0.1", "tokio", - "tokio-rustls", + "tokio-native-tls", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", - "winreg", + "windows-registry", ] [[package]] @@ -3084,21 +2868,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin 0.5.2", - "untrusted 0.7.1", - "web-sys", - "winapi", -] - [[package]] name = "ring" version = "0.17.8" @@ -3109,25 +2878,16 @@ dependencies = [ "cfg-if", "getrandom", "libc", - "spin 0.9.8", - "untrusted 0.9.0", + "spin", + "untrusted", "windows-sys 0.52.0", ] -[[package]] -name = "ripemd" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" -dependencies = [ - "digest", -] - [[package]] name = "rlimit" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3560f70f30a0f16d11d01ed078a07740fe6b489667abc7c7b029155d9f21c3d8" +checksum = "7043b63bd0cd1aaa628e476b80e6d4023a3b50eb32789f2728908107bd0c793a" dependencies = [ "libc", ] @@ -3139,21 +2899,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" dependencies = [ "bytes", - "rlp-derive", "rustc-hex", ] -[[package]] -name = "rlp-derive" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "rpassword" version = "7.3.1" @@ -3175,11 +2923,41 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "ruint" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3cc4c2511671f327125da14133d0c5c5d137f006a1017a16f557bc85b16286" +dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "bytes", + "fastrlp", + "num-bigint", + "num-traits", + "parity-scale-codec", + "primitive-types", + "proptest", + "rand", + "rlp", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" + [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -3195,132 +2973,116 @@ checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" dependencies = [ - "semver", + "semver 0.11.0", ] [[package]] -name = "rustix" -version = "0.36.17" +name = "rustc_version" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "305efbd14fde4139eb501df5f136994bb520b033fa9fbdce287507dc23b8c7ed" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.1.4", - "windows-sys 0.45.0", + "semver 1.0.23", ] [[package]] name = "rustix" -version = "0.38.31" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ - "bitflags 2.4.2", + "bitflags", "errno", "libc", - "linux-raw-sys 0.4.13", + "linux-raw-sys", "windows-sys 0.52.0", ] [[package]] name = "rustls" -version = "0.21.10" +version = "0.23.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" dependencies = [ - "log", - "ring 0.17.8", + "once_cell", + "ring", + "rustls-pki-types", "rustls-webpki", - "sct", + "subtle", + "zeroize", ] [[package]] name = "rustls-pemfile" -version = "1.0.4" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", + "rustls-pki-types", ] +[[package]] +name = "rustls-pki-types" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" + [[package]] name = "rustls-webpki" -version = "0.101.7" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ - "ring 0.17.8", - "untrusted 0.9.0", + "ring", + "rustls-pki-types", + "untrusted", ] [[package]] name = "rustversion" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" - -[[package]] -name = "ryu" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" - -[[package]] -name = "salsa20" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" -dependencies = [ - "cipher", -] +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] -name = "same-file" -version = "1.0.6" +name = "rusty-fork" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" dependencies = [ - "winapi-util", + "fnv", + "quick-error", + "tempfile", + "wait-timeout", ] [[package]] -name = "scale-info" -version = "2.10.0" +name = "ryu" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7d66a1128282b7ef025a8ead62a4a9fcf017382ec53b8ffbf4d7bf77bd3c60" -dependencies = [ - "cfg-if", - "derive_more", - "parity-scale-codec", - "scale-info-derive", -] +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] -name = "scale-info-derive" -version = "2.10.0" +name = "salsa20" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 1.0.109", + "cipher", ] [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3335,6 +3097,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scribe" +version = "0.1.0" +dependencies = [ + "alloy", + "eyre", + "log", + "metrics", + "tokio", + "tokio-util", + "tower 0.5.1", +] + [[package]] name = "scrypt" version = "0.10.0" @@ -3342,21 +3117,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f9e24d2b632954ded8ab2ef9fea0a0c769ea56ea98bddbafbad22caeeadf45d" dependencies = [ "hmac", - "pbkdf2 0.11.0", + "pbkdf2", "salsa20", "sha2", ] -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring 0.17.8", - "untrusted 0.9.0", -] - [[package]] name = "sec1" version = "0.7.3" @@ -3373,11 +3138,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 1.3.2", + "bitflags", "core-foundation", "core-foundation-sys", "libc", @@ -3386,9 +3151,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" dependencies = [ "core-foundation-sys", "libc", @@ -3396,18 +3161,27 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.22" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" dependencies = [ - "serde", + "semver-parser", ] [[package]] -name = "send_wrapper" -version = "0.4.0" +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "semver-parser" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] [[package]] name = "send_wrapper" @@ -3417,44 +3191,36 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.197" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.77", ] [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] -[[package]] -name = "serde_spanned" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" -dependencies = [ - "serde", -] - [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -3475,7 +3241,7 @@ checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", ] [[package]] @@ -3486,7 +3252,7 @@ checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", ] [[package]] @@ -3495,10 +3261,20 @@ version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "digest", + "digest 0.10.7", "keccak", ] +[[package]] +name = "sha3-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" +dependencies = [ + "cc", + "cfg-if", +] + [[package]] name = "shlex" version = "1.3.0" @@ -3507,9 +3283,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -3520,28 +3296,10 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "digest", + "digest 0.10.7", "rand_core", ] -[[package]] -name = "simple_asn1" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" -dependencies = [ - "num-bigint", - "num-traits", - "thiserror", - "time", -] - -[[package]] -name = "siphasher" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" - [[package]] name = "sketches-ddsketch" version = "0.2.2" @@ -3559,40 +3317,20 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", "windows-sys 0.52.0", ] -[[package]] -name = "solang-parser" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c425ce1c59f4b154717592f0bdf4715c3a1d55058883622d3157e1f0908a5b26" -dependencies = [ - "itertools", - "lalrpop", - "lalrpop-util", - "phf", - "thiserror", - "unicode-xid", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spin" version = "0.9.8" @@ -3615,72 +3353,39 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "string_cache" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot", - "phf_shared 0.10.0", - "precomputed-hash", -] - [[package]] name = "strsim" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.26.1" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "723b93e8addf9aa965ebe2d11da6d7540fa2283fcea14b3371ff055f7ba13f5f" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.26.1" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a3417fc93d76740d974a01654a09777cb500428cc874ca9f45edfe0c4d4cd18" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ "heck", "proc-macro2", "quote", "rustversion", - "syn 2.0.52", + "syn 2.0.77", ] [[package]] name = "subtle" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" - -[[package]] -name = "svm-rs" -version = "0.3.5" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11297baafe5fa0c99d5722458eac6a5e25c01eb1b8e5cd137f54079093daa7a4" -dependencies = [ - "dirs", - "fs2", - "hex", - "once_cell", - "reqwest", - "semver", - "serde", - "serde_json", - "sha2", - "thiserror", - "url", - "zip", -] +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" @@ -3695,9 +3400,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.52" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", @@ -3705,30 +3410,30 @@ dependencies = [ ] [[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "system-configuration" -version = "0.5.1" +name = "syn-solidity" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "4b95156f8b577cb59dc0b1df15c6f29a10afc5f8a7ac9786b0b5c68c19149278" dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", + "paste", + "proc-macro2", + "quote", + "syn 2.0.77", ] [[package]] -name = "system-configuration-sys" -version = "0.5.0" +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sync_wrapper" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" dependencies = [ - "core-foundation-sys", - "libc", + "futures-core", ] [[package]] @@ -3739,82 +3444,44 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.10.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand", - "rustix 0.38.31", - "windows-sys 0.52.0", -] - -[[package]] -name = "term" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" -dependencies = [ - "dirs-next", - "rustversion", - "winapi", + "once_cell", + "rustix", + "windows-sys 0.59.0", ] -[[package]] -name = "termtree" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" - [[package]] name = "thiserror" -version = "1.0.57" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.57" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", -] - -[[package]] -name = "time" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", + "syn 2.0.77", ] [[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.17" +name = "threadpool" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" dependencies = [ - "num-conv", - "time-core", + "num_cpus", ] [[package]] @@ -3828,9 +3495,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -3843,32 +3510,31 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.36.0" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.77", ] [[package]] @@ -3883,148 +3549,126 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.24.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ "rustls", + "rustls-pki-types", "tokio", ] [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" dependencies = [ "futures-core", "pin-project-lite", "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.21.0", ] [[package]] name = "tokio-tungstenite" -version = "0.20.1" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" +checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" dependencies = [ "futures-util", "log", "rustls", + "rustls-pki-types", "tokio", "tokio-rustls", - "tungstenite", + "tungstenite 0.23.0", "webpki-roots", ] [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", -] - -[[package]] -name = "toml" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.22.6", ] [[package]] name = "toml_datetime" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap", - "toml_datetime", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.20.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" -dependencies = [ - "indexmap", - "toml_datetime", - "winnow 0.5.40", -] +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.21.1" +version = "0.22.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" dependencies = [ "indexmap", "toml_datetime", - "winnow 0.5.40", + "winnow", ] [[package]] -name = "toml_edit" -version = "0.22.6" +name = "tower" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "winnow 0.6.5", + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", ] [[package]] name = "tower" -version = "0.4.13" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" dependencies = [ "futures-core", "futures-util", - "pin-project", "pin-project-lite", + "sync_wrapper 0.1.2", "tokio", "tower-layer", "tower-service", - "tracing", ] [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -4046,7 +3690,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.77", ] [[package]] @@ -4059,38 +3703,47 @@ dependencies = [ ] [[package]] -name = "tracing-futures" +name = "try-lock" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] -name = "try-lock" -version = "0.2.5" +name = "tungstenite" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.1.0", + "httparse", + "log", + "rand", + "sha1", + "thiserror", + "url", + "utf-8", +] [[package]] name = "tungstenite" -version = "0.20.1" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" +checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" dependencies = [ "byteorder", "bytes", "data-encoding", - "http 0.2.12", + "http 1.1.0", "httparse", "log", "rand", "rustls", + "rustls-pki-types", "sha1", "thiserror", - "url", "utf-8", ] @@ -4100,6 +3753,12 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + [[package]] name = "uint" version = "0.9.5" @@ -4135,30 +3794,24 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] [[package]] name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "untrusted" -version = "0.7.1" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "untrusted" @@ -4168,9 +3821,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -4185,9 +3838,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" @@ -4199,6 +3852,12 @@ dependencies = [ "serde", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vcpkg" version = "0.2.15" @@ -4207,18 +3866,17 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] -name = "walkdir" -version = "2.5.0" +name = "wait-timeout" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" dependencies = [ - "same-file", - "winapi-util", + "libc", ] [[package]] @@ -4232,30 +3890,28 @@ dependencies = [ [[package]] name = "warp" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e92e22e03ff1230c03a1a8ee37d2f89cd489e2e541b7550d6afad96faed169" +checksum = "4378d202ff965b011c64817db11d5829506d3404edeadb61f190d111da3f231c" dependencies = [ "bytes", "futures-channel", "futures-util", "headers", "http 0.2.12", - "hyper 0.14.28", + "hyper 0.14.30", "log", "mime", "mime_guess", "multer", "percent-encoding", "pin-project", - "rustls-pemfile", "scoped-tls", "serde", "serde_json", "serde_urlencoded", "tokio", - "tokio-stream", - "tokio-tungstenite", + "tokio-tungstenite 0.21.0", "tokio-util", "tower-service", "tracing", @@ -4269,34 +3925,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.77", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" dependencies = [ "cfg-if", "js-sys", @@ -4306,9 +3963,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4316,28 +3973,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.77", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" dependencies = [ "js-sys", "wasm-bindgen", @@ -4345,9 +4002,18 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.4" +version = "0.26.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "widestring" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" [[package]] name = "winapi" @@ -4365,15 +4031,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -4386,45 +4043,57 @@ version = "0.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" dependencies = [ - "windows-core 0.54.0", - "windows-targets 0.52.4", + "windows-core", + "windows-targets 0.52.6", ] [[package]] name = "windows-core" -version = "0.52.0" +version = "0.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" dependencies = [ - "windows-targets 0.52.4", + "windows-result 0.1.2", + "windows-targets 0.52.6", ] [[package]] -name = "windows-core" -version = "0.54.0" +name = "windows-registry" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" dependencies = [ - "windows-result", - "windows-targets 0.52.4", + "windows-result 0.2.0", + "windows-strings", + "windows-targets 0.52.6", ] [[package]] name = "windows-result" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd19df78e5168dfb0aedc343d1d1b8d422ab2db6756d2dc3fef75035402a3f64" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.6", ] [[package]] -name = "windows-sys" -version = "0.45.0" +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-targets 0.42.2", + "windows-result 0.2.0", + "windows-targets 0.52.6", ] [[package]] @@ -4442,22 +4111,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.6", ] [[package]] -name = "windows-targets" -version = "0.42.2" +name = "windows-sys" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets 0.52.6", ] [[package]] @@ -4477,25 +4140,20 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows_aarch64_gnullvm" -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" @@ -4504,15 +4162,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -4522,15 +4174,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -4540,15 +4186,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] -name = "windows_i686_msvc" -version = "0.42.2" +name = "windows_i686_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -4558,15 +4204,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -4576,15 +4216,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -4594,15 +4228,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -4612,38 +4240,19 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" - -[[package]] -name = "winnow" -version = "0.5.40" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.5" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "ws_stream_wasm" version = "0.7.4" @@ -4655,8 +4264,8 @@ dependencies = [ "js-sys", "log", "pharos", - "rustc_version", - "send_wrapper 0.6.0", + "rustc_version 0.4.1", + "send_wrapper", "thiserror", "wasm-bindgen", "wasm-bindgen-futures", @@ -4672,83 +4281,43 @@ dependencies = [ "tap", ] -[[package]] -name = "yansi" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" - [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.77", ] [[package]] name = "zeroize" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" - -[[package]] -name = "zip" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" -dependencies = [ - "aes", - "byteorder", - "bzip2", - "constant_time_eq", - "crc32fast", - "crossbeam-utils", - "flate2", - "hmac", - "pbkdf2 0.11.0", - "sha1", - "time", - "zstd", -] - -[[package]] -name = "zstd" -version = "0.11.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "5.0.2+zstd.1.5.2" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ - "libc", - "zstd-sys", + "zeroize_derive", ] [[package]] -name = "zstd-sys" -version = "2.0.9+zstd.1.5.5" +name = "zeroize_derive" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "cc", - "pkg-config", + "proc-macro2", + "quote", + "syn 2.0.77", ] diff --git a/Cargo.toml b/Cargo.toml index e7d1d40..5c04970 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,25 +4,23 @@ version = "0.1.0" edition = "2021" [workspace] +# members = ["crates/*"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [workspace.dependencies] -challenger-lib = { path = "challenger" } - -ethers = "2.0" -tokio = { version = "1", features = ["full"] } -tokio-util = "0.7.10" -hex-literal = "0.4.1" +scribe = { path = "./crates/scribe" } +log = { version = "0.4.20", features = ["kv"] } env_logger = "0.11.3" -futures = "0.3.28" eyre = "0.6.8" -log = { version = "0.4.20", features = ["kv"] } -hex = "0.4.3" -chrono = "0.4.26" -lazy_static = "1.4.0" -async-trait = "0.1.73" + +tokio = { version = "1", features = ["full"] } +tokio-util = "0.7.12" + +alloy = { version = "0.3.6", features = ["full", "signer-keystore"] } warp = "0.3" +tower = { version = "0.5.1", features = ["retry"] } + metrics-process = "1.2.1" metrics-exporter-prometheus = { version = "0.14.0", features = [ "http-listener", @@ -30,13 +28,17 @@ metrics-exporter-prometheus = { version = "0.14.0", features = [ metrics = "0.22.3" [dependencies] -challenger-lib.workspace = true -tokio = { workspace = true, features = ["full"] } +scribe = {workspace = true} +alloy = { workspace = true, features = ["full", "signer-keystore"] } env_logger = { workspace = true } log = { workspace = true, features = ["kv"] } eyre = { workspace = true } -ethers = { workspace = true } warp = { workspace = true } +tower = { workspace = true, features = ["retry"] } + +tokio = { workspace = true, features = ["full"] } +tokio-util = { workspace = true } + metrics-process = { workspace = true } metrics-exporter-prometheus = { workspace = true } diff --git a/abi/ScribeOptimistic.json b/abi/ScribeOptimistic.json deleted file mode 100644 index 1950706..0000000 --- a/abi/ScribeOptimistic.json +++ /dev/null @@ -1,1531 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "address", - "name": "initialAuthed", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "wat_", - "type": "bytes32" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "numberSigners", - "type": "uint8" - }, - { - "internalType": "uint8", - "name": "bar", - "type": "uint8" - } - ], - "name": "BarNotReached", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "givenAge", - "type": "uint32" - }, - { - "internalType": "uint32", - "name": "currentTimestamp", - "type": "uint32" - } - ], - "name": "FutureMessage", - "type": "error" - }, - { - "inputs": [], - "name": "InChallengePeriod", - "type": "error" - }, - { - "inputs": [], - "name": "NoOpPokeToChallenge", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "caller", - "type": "address" - } - ], - "name": "NotAuthorized", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "caller", - "type": "address" - } - ], - "name": "NotTolled", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint160", - "name": "gotHash", - "type": "uint160" - }, - { - "internalType": "uint160", - "name": "wantHash", - "type": "uint160" - } - ], - "name": "SchnorrDataMismatch", - "type": "error" - }, - { - "inputs": [], - "name": "SchnorrSignatureInvalid", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "signer", - "type": "address" - } - ], - "name": "SignerNotFeed", - "type": "error" - }, - { - "inputs": [], - "name": "SignersNotOrdered", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "givenAge", - "type": "uint32" - }, - { - "internalType": "uint32", - "name": "currentAge", - "type": "uint32" - } - ], - "name": "StaleMessage", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "caller", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "who", - "type": "address" - } - ], - "name": "AuthGranted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "caller", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "who", - "type": "address" - } - ], - "name": "AuthRenounced", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "caller", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint8", - "name": "oldBar", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "uint8", - "name": "newBar", - "type": "uint8" - } - ], - "name": "BarUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "caller", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "feed", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "index", - "type": "uint256" - } - ], - "name": "FeedDropped", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "caller", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "feed", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "index", - "type": "uint256" - } - ], - "name": "FeedLifted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "caller", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "oldMaxChallengeReward", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newMaxChallengeReward", - "type": "uint256" - } - ], - "name": "MaxChallengeRewardUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "caller", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint16", - "name": "oldOpChallengePeriod", - "type": "uint16" - }, - { - "indexed": false, - "internalType": "uint16", - "name": "newOpChallengePeriod", - "type": "uint16" - } - ], - "name": "OpChallengePeriodUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "challenger", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "reward", - "type": "uint256" - } - ], - "name": "OpChallengeRewardPaid", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "caller", - "type": "address" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "schnorrErr", - "type": "bytes" - } - ], - "name": "OpPokeChallengedSuccessfully", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "caller", - "type": "address" - } - ], - "name": "OpPokeChallengedUnsuccessfully", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "caller", - "type": "address" - }, - { - "components": [ - { - "internalType": "uint128", - "name": "val", - "type": "uint128" - }, - { - "internalType": "uint32", - "name": "age", - "type": "uint32" - } - ], - "indexed": false, - "internalType": "struct IScribe.PokeData", - "name": "pokeData", - "type": "tuple" - } - ], - "name": "OpPokeDataDropped", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "caller", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "opFeed", - "type": "address" - }, - { - "components": [ - { - "internalType": "bytes32", - "name": "signature", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "commitment", - "type": "address" - }, - { - "internalType": "bytes", - "name": "signersBlob", - "type": "bytes" - } - ], - "indexed": false, - "internalType": "struct IScribe.SchnorrData", - "name": "schnorrData", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "uint128", - "name": "val", - "type": "uint128" - }, - { - "internalType": "uint32", - "name": "age", - "type": "uint32" - } - ], - "indexed": false, - "internalType": "struct IScribe.PokeData", - "name": "pokeData", - "type": "tuple" - } - ], - "name": "OpPoked", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "caller", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint128", - "name": "val", - "type": "uint128" - }, - { - "indexed": false, - "internalType": "uint32", - "name": "age", - "type": "uint32" - } - ], - "name": "Poked", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "caller", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "who", - "type": "address" - } - ], - "name": "TollGranted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "caller", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "who", - "type": "address" - } - ], - "name": "TollRenounced", - "type": "event" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "who", - "type": "address" - } - ], - "name": "authed", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "authed", - "outputs": [ - { - "internalType": "address[]", - "name": "", - "type": "address[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "bar", - "outputs": [ - { - "internalType": "uint8", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "who", - "type": "address" - } - ], - "name": "bud", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "challengeReward", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "uint128", - "name": "val", - "type": "uint128" - }, - { - "internalType": "uint32", - "name": "age", - "type": "uint32" - } - ], - "internalType": "struct IScribe.PokeData", - "name": "pokeData", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "bytes32", - "name": "signature", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "commitment", - "type": "address" - }, - { - "internalType": "bytes", - "name": "signersBlob", - "type": "bytes" - } - ], - "internalType": "struct IScribe.SchnorrData", - "name": "schnorrData", - "type": "tuple" - } - ], - "name": "constructOpPokeMessage", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "uint128", - "name": "val", - "type": "uint128" - }, - { - "internalType": "uint32", - "name": "age", - "type": "uint32" - } - ], - "internalType": "struct IScribe.PokeData", - "name": "pokeData", - "type": "tuple" - } - ], - "name": "constructPokeMessage", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "decimals", - "outputs": [ - { - "internalType": "uint8", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "who", - "type": "address" - } - ], - "name": "deny", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "who", - "type": "address" - } - ], - "name": "diss", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "feedIndex", - "type": "uint256" - } - ], - "name": "drop", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256[]", - "name": "feedIndexes", - "type": "uint256[]" - } - ], - "name": "drop", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "feedRegistrationMessage", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "who", - "type": "address" - } - ], - "name": "feeds", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "index", - "type": "uint256" - } - ], - "name": "feeds", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - }, - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "feeds", - "outputs": [ - { - "internalType": "address[]", - "name": "", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "", - "type": "uint256[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "message", - "type": "bytes32" - }, - { - "components": [ - { - "internalType": "bytes32", - "name": "signature", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "commitment", - "type": "address" - }, - { - "internalType": "bytes", - "name": "signersBlob", - "type": "bytes" - } - ], - "internalType": "struct IScribe.SchnorrData", - "name": "schnorrData", - "type": "tuple" - } - ], - "name": "isAcceptableSchnorrSignatureNow", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "who", - "type": "address" - } - ], - "name": "kiss", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "latestRoundData", - "outputs": [ - { - "internalType": "uint80", - "name": "roundId", - "type": "uint80" - }, - { - "internalType": "int256", - "name": "answer", - "type": "int256" - }, - { - "internalType": "uint256", - "name": "startedAt", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "updatedAt", - "type": "uint256" - }, - { - "internalType": "uint80", - "name": "answeredInRound", - "type": "uint80" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "x", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "y", - "type": "uint256" - } - ], - "internalType": "struct LibSecp256k1.Point", - "name": "pubKey", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "uint8", - "name": "v", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "r", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "s", - "type": "bytes32" - } - ], - "internalType": "struct IScribe.ECDSAData", - "name": "ecdsaData", - "type": "tuple" - } - ], - "name": "lift", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "x", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "y", - "type": "uint256" - } - ], - "internalType": "struct LibSecp256k1.Point[]", - "name": "pubKeys", - "type": "tuple[]" - }, - { - "components": [ - { - "internalType": "uint8", - "name": "v", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "r", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "s", - "type": "bytes32" - } - ], - "internalType": "struct IScribe.ECDSAData[]", - "name": "ecdsaDatas", - "type": "tuple[]" - } - ], - "name": "lift", - "outputs": [ - { - "internalType": "uint256[]", - "name": "", - "type": "uint256[]" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "maxChallengeReward", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "maxFeeds", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "bytes32", - "name": "signature", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "commitment", - "type": "address" - }, - { - "internalType": "bytes", - "name": "signersBlob", - "type": "bytes" - } - ], - "internalType": "struct IScribe.SchnorrData", - "name": "schnorrData", - "type": "tuple" - } - ], - "name": "opChallenge", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "opChallengePeriod", - "outputs": [ - { - "internalType": "uint16", - "name": "", - "type": "uint16" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "opFeedIndex", - "outputs": [ - { - "internalType": "uint8", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "uint128", - "name": "val", - "type": "uint128" - }, - { - "internalType": "uint32", - "name": "age", - "type": "uint32" - } - ], - "internalType": "struct IScribe.PokeData", - "name": "pokeData", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "bytes32", - "name": "signature", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "commitment", - "type": "address" - }, - { - "internalType": "bytes", - "name": "signersBlob", - "type": "bytes" - } - ], - "internalType": "struct IScribe.SchnorrData", - "name": "schnorrData", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "uint8", - "name": "v", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "r", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "s", - "type": "bytes32" - } - ], - "internalType": "struct IScribe.ECDSAData", - "name": "ecdsaData", - "type": "tuple" - } - ], - "name": "opPoke", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "uint128", - "name": "val", - "type": "uint128" - }, - { - "internalType": "uint32", - "name": "age", - "type": "uint32" - } - ], - "internalType": "struct IScribe.PokeData", - "name": "pokeData", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "bytes32", - "name": "signature", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "commitment", - "type": "address" - }, - { - "internalType": "bytes", - "name": "signersBlob", - "type": "bytes" - } - ], - "internalType": "struct IScribe.SchnorrData", - "name": "schnorrData", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "uint8", - "name": "v", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "r", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "s", - "type": "bytes32" - } - ], - "internalType": "struct IScribe.ECDSAData", - "name": "ecdsaData", - "type": "tuple" - } - ], - "name": "opPoke_optimized_397084999", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [], - "name": "peek", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "uint128", - "name": "val", - "type": "uint128" - }, - { - "internalType": "uint32", - "name": "age", - "type": "uint32" - } - ], - "internalType": "struct IScribe.PokeData", - "name": "pokeData", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "bytes32", - "name": "signature", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "commitment", - "type": "address" - }, - { - "internalType": "bytes", - "name": "signersBlob", - "type": "bytes" - } - ], - "internalType": "struct IScribe.SchnorrData", - "name": "schnorrData", - "type": "tuple" - } - ], - "name": "poke", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "uint128", - "name": "val", - "type": "uint128" - }, - { - "internalType": "uint32", - "name": "age", - "type": "uint32" - } - ], - "internalType": "struct IScribe.PokeData", - "name": "pokeData", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "bytes32", - "name": "signature", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "commitment", - "type": "address" - }, - { - "internalType": "bytes", - "name": "signersBlob", - "type": "bytes" - } - ], - "internalType": "struct IScribe.SchnorrData", - "name": "schnorrData", - "type": "tuple" - } - ], - "name": "poke_optimized_7136211", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "read", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "readWithAge", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "who", - "type": "address" - } - ], - "name": "rely", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "bar_", - "type": "uint8" - } - ], - "name": "setBar", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "maxChallengeReward_", - "type": "uint256" - } - ], - "name": "setMaxChallengeReward", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "opChallengePeriod_", - "type": "uint16" - } - ], - "name": "setOpChallengePeriod", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "who", - "type": "address" - } - ], - "name": "tolled", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "tolled", - "outputs": [ - { - "internalType": "address[]", - "name": "", - "type": "address[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "tryRead", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "tryReadWithAge", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "who", - "type": "address" - } - ], - "name": "wards", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "wat", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "stateMutability": "payable", - "type": "receive" - } -] diff --git a/challenger/Cargo.toml b/challenger/Cargo.toml deleted file mode 100644 index 6107888..0000000 --- a/challenger/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "challenger-lib" -version = "0.1.0" -edition = "2021" - -[lib] -doctest = false - -[dependencies] -ethers = { workspace = true } -tokio = { workspace = true, features = ["full"] } -hex-literal = { workspace = true } -env_logger = { workspace = true } -futures = { workspace = true } -eyre = { workspace = true } -log = { workspace = true, features = ["kv"] } -hex = { workspace = true } -chrono = { workspace = true } -lazy_static = { workspace = true } -tokio-util = { workspace = true } -async-trait = { workspace = true } -metrics = { workspace = true } - -prometheus = { version = "0.13.3", features = ["process"] } - -[dev-dependencies] -mockall = "0.12.1" diff --git a/challenger/src/contract.rs b/challenger/src/contract.rs deleted file mode 100644 index df82d5a..0000000 --- a/challenger/src/contract.rs +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright (C) 2021-2023 Chronicle Labs, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -use async_trait::async_trait; -use std::{fmt::Debug, sync::Arc}; - -use ethers::{ - contract::{abigen, Contract, LogMeta}, - providers::Middleware, - types::{Address, Block, TransactionReceipt, ValueOrArray, H256, U64}, -}; -use eyre::{Result, WrapErr}; -use log::{debug, warn}; - -// Yes it generates smart contract code based on given ABI -abigen!(ScribeOptimistic, "./abi/ScribeOptimistic.json"); - -#[async_trait] -pub trait ScribeOptimisticProvider: Send + Sync { - /// Returns the latest block number from RPC. - async fn get_latest_block_number(&self) -> Result; - - /// Returns block details with given `block_number` from RPC. - async fn get_block(&self, block_number: U64) -> Result>>; - - /// Returns challenge period from ScribeOptimistic smart contract deployed to `address`. - async fn get_challenge_period(&self) -> Result; - - /// Returns list of `OpPokeChallengedSuccessfully` events and log metadata in between `from_block` and `to_block` - /// from smart contract deployed to `address`. - async fn get_successful_challenges( - &self, - from_block: U64, - to_block: U64, - ) -> Result>; - - /// Returns list of `OpPoked` events and log metadata in between `from_block` and `to_block` from smart contract - /// deployed to `address`. - async fn get_op_pokes( - &self, - from_block: U64, - to_block: U64, - ) -> Result>; - - /// Returns true if given `OpPoked` schnorr signature is valid. - async fn is_schnorr_signature_valid(&self, op_poked: OpPokedFilter) -> Result; - - /// Challenges given `OpPoked` event. - async fn challenge(&self, schnorr_data: SchnorrData) -> Result>; - - /// Returns the from account address. - fn get_from(&self) -> Option
{ - warn!( - "get_from() uses default sender address, it's not recommended to use it in production" - ); - Some(Address::zero()) - } -} - -#[derive(Debug)] -pub struct HttpScribeOptimisticProvider { - address: Address, - client: Arc, - contract: ScribeOptimistic, -} - -impl HttpScribeOptimisticProvider { - /// Creates new instance of `HttpScribeOptimisticProvider` under given `client` and `address`. - pub fn new(address: Address, client: Arc) -> Self { - let contract = ScribeOptimistic::new(address, client.clone()); - Self { - address, - client, - contract, - } - } -} - -#[async_trait] -impl ScribeOptimisticProvider for HttpScribeOptimisticProvider -where - M: 'static, - M::Error: 'static, -{ - fn get_from(&self) -> Option
{ - self.client.default_sender() - } - - /// Returns the latest block number from RPC. - async fn get_latest_block_number(&self) -> Result { - self.client - .get_block_number() - .await - .wrap_err("Failed to get latest block number") - } - - /// Returns block details with given `block_number` from RPC. - async fn get_block(&self, block_number: U64) -> Result>> { - self.client - .get_block(block_number) - .await - .wrap_err("Failed to get block details") - } - - /// Returns challenge period from ScribeOptimistic smart contract deployed to `address`. - async fn get_challenge_period(&self) -> Result { - debug!("[{:?}] Getting challenge period", self.address); - - self.contract - .op_challenge_period() - .call() - .await - .wrap_err("Failed to get challenge period") - } - - /// Returns list of `OpPokeChallengedSuccessfully` events and log metadata in between `from_block` and `to_block` - /// from smart contract deployed to `address`. - async fn get_successful_challenges( - &self, - from_block: U64, - to_block: U64, - ) -> Result> { - debug!( - "[{:?}] Searching OpPokeChallengedSuccessfully events from block {:?} to block {:?}", - self.address, from_block, to_block - ); - let event = - Contract::event_of_type::(self.client.clone()) - .address(ValueOrArray::Array(vec![self.address])) - .from_block(from_block) - .to_block(to_block); - - event.query_with_meta().await.wrap_err(format!( - "Failed to get OpPokeChallengedSuccessfully events from block {:?} to block {:?}", - from_block, to_block - )) - } - - /// Returns list of `OpPoked` events and log metadata in between `from_block` and `to_block` from smart contract - /// deployed to `address`. - async fn get_op_pokes( - &self, - from_block: U64, - to_block: U64, - ) -> Result> { - debug!( - "[{:?}] Searching OpPoked events from block {:?} to block {:?}", - self.address, from_block, to_block - ); - let event = Contract::event_of_type::(self.client.clone()) - .address(ValueOrArray::Array(vec![self.address])) - .from_block(from_block) - .to_block(to_block); - - event.query_with_meta().await.wrap_err(format!( - "Failed to get OpPoked events from block {:?} to block {:?}", - from_block, to_block - )) - } - - /// Returns true if given `OpPoked` schnorr signature is valid. - /// Validation process is based on given `OpPoked` event. - /// Validation logic described in here: https://github.com/chronicleprotocol/scribe/blob/main/docs/Scribe.md#verifying-optimistic-pokes - async fn is_schnorr_signature_valid(&self, op_poked: OpPokedFilter) -> Result { - debug!( - "[{:?}] Validating schnorr signature for {:?}", - self.address, op_poked - ); - - let message = self - .contract - .construct_poke_message(op_poked.poke_data) - .call() - .await?; - - self.contract - .is_acceptable_schnorr_signature_now(message, op_poked.schnorr_data) - .call() - .await - .wrap_err("Failed to validate schnorr signature for OpPoked event") - } - - /// Challenges given `OpPoked` event. - /// Executes `opChallenge` function from ScribeOptimistic smart contract with shnorr signature - /// taken from `OpPoked` event. - /// NOTE: You have to validate if schnorr signature is INVALID before calling this function ! - async fn challenge(&self, schnorr_data: SchnorrData) -> Result> { - debug!( - "[{:?}] Challenging schnorr data {:?}", - self.address, schnorr_data - ); - - self.contract - .op_challenge(schnorr_data) - .send() - .await? - .await - .wrap_err("Failed to challenge OpPoked event") - } -} diff --git a/challenger/src/lib.rs b/challenger/src/lib.rs deleted file mode 100644 index 59a9788..0000000 --- a/challenger/src/lib.rs +++ /dev/null @@ -1,879 +0,0 @@ -// Copyright (C) 2021-2023 Chronicle Labs, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -use chrono::{DateTime, Utc}; -use ethers::{ - contract::LogMeta, - core::types::{Address, U64}, -}; -use eyre::{bail, Result}; -use log::{debug, error, info, warn}; -use std::time::Duration; -use tokio::time; - -pub mod contract; -pub mod metrics; - -use contract::{OpPokeChallengedSuccessfullyFilter, OpPokedFilter, ScribeOptimisticProvider}; - -// Note: this is true virtually all of the time but because of leap seconds not always. -// We take minimal time just to be sure, it's always better to check outdated blocks -// rather than miss some. -const SLOT_PERIOD_SECONDS: u16 = 12; - -// Time interval in seconds to reload challenge period from contract. -const DEFAULT_CHALLENGE_PERIOD_RELOAD_INTERVAL: Duration = Duration::from_secs(600); - -// Time interval for checking new pokes in milliseconds. -const DEFAULT_CHECK_INTERVAL_IN_MS: u64 = 30_000; - -// Max number of failures before we stop processing address. -const MAX_FAILURE_COUNT: u8 = 3; - -#[derive(Debug)] -pub struct Challenger { - address: Address, - contract_provider: P, - last_processed_block: Option, - challenge_period_in_sec: u16, - challenge_period_last_updated_at: Option>, - max_failure_count: u8, - failure_count: u8, - tick_interval: Duration, -} - -impl

Challenger

-where - P: ScribeOptimisticProvider + 'static, -{ - pub fn new( - address: Address, - contract_provider: P, - tick_interval: Option, - max_failure_count: Option, - ) -> Self { - Self { - address, - contract_provider, - last_processed_block: None, - challenge_period_in_sec: 0, - challenge_period_last_updated_at: None, - failure_count: 0, - max_failure_count: max_failure_count.unwrap_or(MAX_FAILURE_COUNT), - tick_interval: Duration::from_millis( - tick_interval.unwrap_or(DEFAULT_CHECK_INTERVAL_IN_MS), - ), - } - } - - // Sets last processed block number in challenger - fn set_last_processed_block(&mut self, block: U64) { - self.last_processed_block = Some(block); - - // Updating last scanned block metric - metrics::set_last_scanned_block( - self.address, - self.contract_provider.get_from().unwrap_or_default(), - block.as_u64() as i64, - ); - } - - // Reloads challenge period from contract. - // This function have to be called every N time, because challenge period can be changed by contract owner. - async fn reload_challenge_period(&mut self) -> Result<()> { - let challenge_period_in_sec = self.contract_provider.get_challenge_period().await?; - - debug!( - "[{:?}] Reloaded opChallenge period for contract is {:?}", - self.address, challenge_period_in_sec - ); - self.challenge_period_in_sec = challenge_period_in_sec; - self.challenge_period_last_updated_at = Some(Utc::now()); - - Ok(()) - } - - // Reloads the challenge period from the contract if it has not been updated within the default challenge period reload interval. - async fn reload_challenge_period_if_needed(&mut self) -> Result<()> { - let need_update = match self.challenge_period_last_updated_at { - None => true, - Some(utc) => { - let diff = Utc::now() - utc; - diff.to_std().unwrap() > DEFAULT_CHALLENGE_PERIOD_RELOAD_INTERVAL - } - }; - - if need_update { - self.reload_challenge_period().await.unwrap(); - } - - Ok(()) - } - - // Gets earliest block number we can search for non challenged `opPokes` - async fn get_starting_block_number( - &self, - last_block_number: U64, - challenge_period_in_sec: u16, - ) -> Result { - debug!( - "[{:?}] Calculating starting block number, latest from chain {:?}, period {:?}", - self.address, last_block_number, challenge_period_in_sec - ); - - let blocks_per_period = challenge_period_in_sec / SLOT_PERIOD_SECONDS; - - Ok(last_block_number - blocks_per_period) - } - - // Check if given block_number for log is already non challengeable - async fn is_challengeable( - &self, - block_number: U64, - challenge_period_in_sec: u16, - ) -> Result { - // Checking if log is possible to challenge ? - let block = self - .contract_provider - .get_block(block_number) - .await? - .unwrap(); - - let diff = Utc::now().timestamp() as u64 - block.timestamp.as_u64(); - - Ok(challenge_period_in_sec > diff as u16) - } - - // This function is called every tick. - // Deciedes blocks range we have to process, processing them and if no error happened sets new `last_processed_block` for next tick. - // If error happened on next tick it will again try to process same blocks. - async fn process(&mut self) -> Result<()> { - // Reloads challenge period value - self.reload_challenge_period_if_needed().await.unwrap(); - - // Getting last block from chain - let latest_block_number = self - .contract_provider - .get_latest_block_number() - .await - .unwrap(); - - // Fetching block we have to start with - let from_block = self.last_processed_block.unwrap_or( - self.get_starting_block_number(latest_block_number, self.challenge_period_in_sec) - .await?, - ); - - // In some cases (block drop) our latest processed block can be bigger than latest block from chain, - // in this case we have to skip processing and reset last processed block, so on next tick we will retry. - // Also we returning error to increase failure count. - if from_block > latest_block_number { - // Resetting last processed block with latest chain block - self.set_last_processed_block(latest_block_number); - - bail!( - "Invalid block range {:?} - {:?}, from block is bigger than to block, resetting last processed block to latest block from chain.", - from_block, latest_block_number - ); - } - - // Processing blocks range - self.process_blocks_range(from_block, latest_block_number) - .await?; - - // Updating last processed block with latest chain block - self.set_last_processed_block(latest_block_number); - - Ok(()) - } - - // Validates all `OpPoked` events and challenges them if needed. - async fn process_blocks_range(&mut self, from_block: U64, to_block: U64) -> Result<()> { - debug!( - "[{:?}] Processing blocks range {:?} - {:?}", - self.address, from_block, to_block - ); - - // Fetch list of `OpPokeChallengedSuccessfully` events - let challenges = self - .contract_provider - .get_successful_challenges(from_block, to_block) - .await?; - - // Fetches `OpPoked` events - let op_pokes = self - .contract_provider - .get_op_pokes(from_block, to_block) - .await?; - - // ignoring already challenged pokes - let unchallenged_pokes = reject_challenged_pokes(op_pokes, challenges); - - // Check if we have unchallenged pokes - if unchallenged_pokes.is_empty() { - debug!( - "[{:?}] No unchallenged opPokes found in block range {:?} - {:?}, skipping...", - self.address, from_block, to_block - ); - return Ok(()); - } - - for (poke, meta) in unchallenged_pokes { - let challengeable = self - .is_challengeable(meta.block_number, self.challenge_period_in_sec) - .await?; - - if !challengeable { - error!( - "[{:?}] Block is to old for `opChallenge` block number: {:?}", - self.address, meta.block_number - ); - continue; - } - - let valid = self - .contract_provider - .is_schnorr_signature_valid(poke.clone()) - .await?; - - // If schnorr data is valid, we should not challenge it... - if valid { - debug!( - "[{:?}] Schnorr data for block {:?} is valid, nothing to do...", - self.address, meta.block_number - ); - - continue; - } - - info!( - "[{:?}] Schnorr data for block {:?} is not valid, trying to challenge...", - self.address, meta.block_number - ); - - // TODO: handle error gracefully, we should go further even if error happened - match self.contract_provider.challenge(poke.schnorr_data).await { - Ok(receipt) => { - if let Some(receipt) = receipt { - info!( - "[{:?}] Successfully sent `opChallenge` transaction for OpPoke on block {:?}: {:?}", - self.address, meta.block_number, receipt - ); - // Add challenge to metrics - metrics::inc_challenge_counter( - self.address, - self.contract_provider.get_from().unwrap_or_default(), - receipt.transaction_hash, - ); - } else { - warn!( - "[{:?}] Successfully sent `opChallenge` for block {:?} transaction but no receipt returned", - self.address, meta.block_number - ); - } - } - Err(err) => { - error!( - "[{:?}] Failed to make `opChallenge` call for block {:?}: {:?}", - self.address, meta.block_number, err - ); - } - }; - } - - Ok(()) - } - - /// Starts processing pokes for the given contract address using the specified provider and tick interval. - /// - /// The function uses a tokio::time::interval to run the process method at regular intervals specified by the tick_interval field. - /// - /// # Used arguments - /// - /// * `contract_address` - The address of the contract to process pokes for. - /// * `provider` - The provider to use for interacting with the Ethereum network. - /// * `tick_interval` - The interval at which to check for new pokes. - /// - /// # Examples - /// - /// ``` - /// use eyre::Result; - /// use ethers::providers::{Http, Provider}; - /// use challenger::{Challenger, HttpScribeOptimisticProvider}; - /// use std::time::Duration; - /// - /// #[tokio::main] - /// async fn main() -> Result<()> { - /// let rpc_provider = Provider::::connect("https://mainnet.infura.io/v3/your-project-id").await?; - /// let contract_address = "0x1234567890123456789012345678901234567890".parse()?; - /// let provider = HttpScribeOptimisticProvider::new(contract_address, rpc_provider); - /// let mut challenger = Challenger::new(contract_address, provider, Duration::from_secs(30), None); - /// - /// challenger.start().await? - /// } - /// ``` - pub async fn start(&mut self) -> Result<()> { - let mut interval = time::interval(self.tick_interval); - - loop { - match self.process().await { - Ok(_) => { - // Reset error counter - self.failure_count = 0; - } - Err(err) => { - error!("[{:?}] Failed to process opPokes: {:?}", self.address, err); - - // Increment error counter - metrics::inc_errors_counter( - self.address, - self.contract_provider.get_from().unwrap_or_default(), - &err.to_string(), - ); - - // Increment and check error counter - self.failure_count += 1; - if self.failure_count >= self.max_failure_count { - error!( - "[{:?}] Reached max failure count, stopping processing...", - self.address - ); - return Err(err); - } - } - } - - interval.tick().await; - } - } -} - -// Removes challenged pokes from list of loaded pokes. -// Logic is very simple, if `OpPokeChallengedSuccessfully` event is after `OpPoked` event, then we can safely -// say that `OpPoked` event is already challenged. So we need to validate sequence of events and remove all -// `OpPoked` events that has `OpPokeChallengedSuccessfully` event after it. -fn reject_challenged_pokes( - pokes: Vec<(OpPokedFilter, LogMeta)>, - challenges: Vec<(OpPokeChallengedSuccessfullyFilter, LogMeta)>, -) -> Vec<(OpPokedFilter, LogMeta)> { - if challenges.is_empty() || pokes.is_empty() { - return pokes; - } - let mut result: Vec<(OpPokedFilter, LogMeta)> = vec![]; - - if pokes.len() == 1 { - let (_, meta) = &pokes[0]; - for (_, c_meta) in challenges.clone() { - if c_meta.block_number > meta.block_number { - // empty result - return result; - } - } - return pokes; - } - - 'pokes_loop: for i in 0..pokes.len() { - let (poke, meta) = &pokes.get(i).unwrap(); - // If we do have next poke in list - if let Some((_, next_meta)) = &pokes.get(i + 1) { - for (_, c_meta) in challenges.clone() { - if meta.block_number < c_meta.block_number - && next_meta.block_number > c_meta.block_number - { - // poke already challenged - continue 'pokes_loop; - } - } - } else { - for (_, c_meta) in challenges.clone() { - if c_meta.block_number > meta.block_number { - // poke already challenged - continue 'pokes_loop; - } - } - } - result.push((poke.clone(), meta.clone())); - } - - result -} - -#[cfg(test)] -mod tests { - use super::*; - - use async_trait::async_trait; - use contract::SchnorrData; - use ethers::{ - contract::LogMeta, - types::{Block, TransactionReceipt, H160, H256, U256, U64}, - }; - use eyre::Result; - use mockall::{mock, predicate::*}; - - mock! { - pub TestScribe{} - - #[async_trait] - impl ScribeOptimisticProvider for TestScribe { - async fn get_latest_block_number(&self) -> Result; - - async fn get_block(&self, block_number: U64) -> Result>>; - - async fn get_challenge_period(&self) -> Result; - - async fn get_successful_challenges( - &self, - from_block: U64, - to_block: U64, - ) -> Result>; - - async fn get_op_pokes( - &self, - from_block: U64, - to_block: U64, - ) -> Result>; - - async fn is_schnorr_signature_valid(&self, op_poked: OpPokedFilter) -> Result; - - async fn challenge(&self, schnorr_data: SchnorrData) -> Result>; - } - } - - // Builds new LogMeta with default values, only `block_number` is useful for us. - fn new_log_meta(block_number: U64) -> LogMeta { - LogMeta { - block_number, - address: H160::from_low_u64_be(0), - block_hash: H256::from_low_u64_be(0), - transaction_hash: H256::from_low_u64_be(0), - transaction_index: U64::from(0), - log_index: U256::from(0), - } - } - - #[test] - fn test_reject_challenged_pokes() { - { - // Does nothing if no pokes or challenges - let pokes: Vec<(OpPokedFilter, LogMeta)> = vec![]; - let challenges: Vec<(OpPokeChallengedSuccessfullyFilter, LogMeta)> = vec![]; - - let result = reject_challenged_pokes(pokes.clone(), challenges); - - assert_eq!(result, pokes); - } - - { - // Only 1 poke - returns it back - let pokes: Vec<(OpPokedFilter, LogMeta)> = vec![( - OpPokedFilter { - ..Default::default() - }, - new_log_meta(U64::from(1)), - )]; - let challenges: Vec<(OpPokeChallengedSuccessfullyFilter, LogMeta)> = vec![]; - - let result = reject_challenged_pokes(pokes.clone(), challenges); - assert_eq!(result, pokes); - } - - { - // One poke one challenge after it - let pokes: Vec<(OpPokedFilter, LogMeta)> = vec![( - OpPokedFilter { - ..Default::default() - }, - new_log_meta(U64::from(1)), - )]; - let challenges: Vec<(OpPokeChallengedSuccessfullyFilter, LogMeta)> = vec![( - OpPokeChallengedSuccessfullyFilter { - ..Default::default() - }, - new_log_meta(U64::from(2)), - )]; - - let result = reject_challenged_pokes(pokes, challenges); - assert!(result.is_empty()); - } - - { - // One poke one challenge before it - let pokes: Vec<(OpPokedFilter, LogMeta)> = vec![( - OpPokedFilter { - ..Default::default() - }, - new_log_meta(U64::from(2)), - )]; - let challenges: Vec<(OpPokeChallengedSuccessfullyFilter, LogMeta)> = vec![( - OpPokeChallengedSuccessfullyFilter { - ..Default::default() - }, - new_log_meta(U64::from(1)), - )]; - - let result = reject_challenged_pokes(pokes.clone(), challenges); - assert_eq!(result, pokes); - } - - { - // Multi pokes - one challenge after first poke - let pokes: Vec<(OpPokedFilter, LogMeta)> = vec![ - ( - OpPokedFilter { - ..Default::default() - }, - new_log_meta(U64::from(1)), - ), - ( - OpPokedFilter { - ..Default::default() - }, - new_log_meta(U64::from(3)), - ), - ]; - let challenges: Vec<(OpPokeChallengedSuccessfullyFilter, LogMeta)> = vec![( - OpPokeChallengedSuccessfullyFilter { - ..Default::default() - }, - new_log_meta(U64::from(2)), - )]; - - let result = reject_challenged_pokes(pokes.clone(), challenges); - assert_eq!(result.len(), 1); - - let (_, meta) = result.first().unwrap(); - assert_eq!(meta.block_number, U64::from(3)); - } - - { - // Multi pokes & multi challenges in random order - let pokes: Vec<(OpPokedFilter, LogMeta)> = vec![ - ( - OpPokedFilter { - ..Default::default() - }, - new_log_meta(U64::from(1)), - ), - ( - OpPokedFilter { - ..Default::default() - }, - new_log_meta(U64::from(3)), - ), - ( - OpPokedFilter { - ..Default::default() - }, - new_log_meta(U64::from(4)), - ), - ( - OpPokedFilter { - ..Default::default() - }, - new_log_meta(U64::from(7)), - ), - ]; - let challenges: Vec<(OpPokeChallengedSuccessfullyFilter, LogMeta)> = vec![ - ( - OpPokeChallengedSuccessfullyFilter { - ..Default::default() - }, - new_log_meta(U64::from(2)), - ), - ( - OpPokeChallengedSuccessfullyFilter { - ..Default::default() - }, - new_log_meta(U64::from(5)), - ), - ( - OpPokeChallengedSuccessfullyFilter { - ..Default::default() - }, - new_log_meta(U64::from(6)), - ), - ]; - - let result = reject_challenged_pokes(pokes.clone(), challenges); - assert_eq!(result.len(), 2); - - let (_, meta) = result.first().unwrap(); - assert_eq!(meta.block_number, U64::from(3)); - - let (_, meta2) = result.get(1).unwrap(); - assert_eq!(meta2.block_number, U64::from(7)); - } - } - - #[tokio::test] - async fn test_challenger_returns_error_on_max_failures() { - // Just random address - let address = "0x3D4c07Bd3cf5FB80ACB6Ec31531DBB338329b5F5" - .parse::

() - .unwrap(); - let max_failures: u8 = 3; - - // Setting up mock - let mut mock = MockTestScribe::new(); - mock.expect_get_latest_block_number() - .returning(|| Ok(U64::from(100))); - - mock.expect_get_block().returning(|_| Ok(None)); - - mock.expect_get_challenge_period().returning(|| Ok(600)); - - // Let's fail on this function. - mock.expect_get_successful_challenges() - .times(usize::from(max_failures)) - .returning(|_, _| eyre::bail!("Error challenges")); - - mock.expect_get_op_pokes() - .returning(|_, _| eyre::bail!("Error op_pokes")); - - // Setting up challenger - let mut challenger = Challenger::new(address, mock, Some(10), Some(max_failures)); - - let res = challenger.start().await; - assert!(res.is_err()); - } - - #[tokio::test] - #[should_panic] - async fn test_challenge_period_requires_to_be_fetched() { - // Just random address - let address = "0x3D4c07Bd3cf5FB80ACB6Ec31531DBB338329b5F5" - .parse::
() - .unwrap(); - - // Setting up mock - let mut mock = MockTestScribe::new(); - mock.expect_get_latest_block_number() - .returning(|| Ok(U64::from(100))); - - mock.expect_get_block().returning(|_| Ok(None)); - - mock.expect_get_challenge_period() - .returning(|| eyre::bail!("Error get challenge period")); - - // Let's fail on this function. - mock.expect_get_successful_challenges() - .returning(|_, _| Ok(vec![])); - - mock.expect_get_op_pokes().returning(|_, _| Ok(vec![])); - - // Setting up challenger - let mut challenger = Challenger::new(address, mock, Some(10), None); - - challenger.process().await.unwrap(); - } - - #[tokio::test] - async fn test_last_processed_block_bigger_than_last_chain_block() { - // Just random address - let address = "0x3D4c07Bd3cf5FB80ACB6Ec31531DBB338329b5F5" - .parse::
() - .unwrap(); - let max_failures: u8 = 3; - - // Setting up mock - let mut mock = MockTestScribe::new(); - mock.expect_get_latest_block_number() - .returning(|| Ok(U64::from(1000))); - - mock.expect_get_challenge_period().returning(|| Ok(600)); - mock.expect_get_successful_challenges().never(); - mock.expect_get_op_pokes().never(); - mock.expect_challenge().never(); - mock.expect_get_block().never(); - - // Setting up challenger - let mut challenger = Challenger::new(address, mock, Some(10), Some(max_failures)); - challenger.set_last_processed_block(U64::from(1001)); - - let res = challenger.process().await; - // Should be error in result. - assert!(res.is_err()); - // Check we reseted the last processed block - assert!(challenger.last_processed_block == Some(U64::from(1000))); - } - - #[tokio::test] - async fn test_no_pokes_no_execution() { - // Just random address - let address = "0x3D4c07Bd3cf5FB80ACB6Ec31531DBB338329b5F5" - .parse::
() - .unwrap(); - let max_failures: u8 = 3; - - // Setting up mock - let mut mock = MockTestScribe::new(); - mock.expect_get_latest_block_number() - .returning(|| Ok(U64::from(1000))); - - mock.expect_get_challenge_period().returning(|| Ok(600)); - // Let's fail on this function. - mock.expect_get_successful_challenges() - .returning(|_, _| Ok(vec![])); - mock.expect_get_op_pokes().returning(|_, _| Ok(vec![])); - mock.expect_challenge().never(); - - mock.expect_get_block().never(); - - // Setting up challenger - let mut challenger = Challenger::new(address, mock, Some(10), Some(max_failures)); - - let res = challenger.process().await; - assert!(res.is_ok()); - } - - #[tokio::test] - async fn test_calls_challenge_on_invalid_signature() { - // Just random address - let address = "0x3D4c07Bd3cf5FB80ACB6Ec31531DBB338329b5F5" - .parse::
() - .unwrap(); - let max_failures: u8 = 3; - - // Setting up mock - let mut mock = MockTestScribe::new(); - mock.expect_get_latest_block_number() - .returning(|| Ok(U64::from(1000))); - - mock.expect_get_challenge_period().returning(|| Ok(600)); - // Let's fail on this function. - mock.expect_get_successful_challenges() - .returning(|_, _| Ok(vec![])); - - mock.expect_get_op_pokes().returning(|_, _| { - Ok(vec![( - OpPokedFilter { - ..Default::default() - }, - new_log_meta(U64::from(999)), - )]) - }); - mock.expect_is_schnorr_signature_valid() - .once() - .returning(|_| Ok(false)); - // Called only once ! - mock.expect_challenge().once().returning(|_| Ok(None)); - - mock.expect_get_block().returning(|_| { - Ok(Some(Block { - timestamp: U256::from(Utc::now().timestamp()), - ..Default::default() - })) - }); - - // Setting up challenger - let mut challenger = Challenger::new(address, mock, Some(10), Some(max_failures)); - - let res = challenger.process().await; - assert!(res.is_ok()); - } - - #[tokio::test] - async fn test_ignores_challenge_on_valid_signature() { - // Just random address - let address = "0x3D4c07Bd3cf5FB80ACB6Ec31531DBB338329b5F5" - .parse::
() - .unwrap(); - let max_failures: u8 = 3; - - // Setting up mock - let mut mock = MockTestScribe::new(); - mock.expect_get_latest_block_number() - .returning(|| Ok(U64::from(1000))); - - mock.expect_get_challenge_period().returning(|| Ok(600)); - // Let's fail on this function. - mock.expect_get_successful_challenges() - .returning(|_, _| Ok(vec![])); - - mock.expect_get_op_pokes().returning(|_, _| { - Ok(vec![( - OpPokedFilter { - ..Default::default() - }, - new_log_meta(U64::from(999)), - )]) - }); - mock.expect_is_schnorr_signature_valid() - .once() - .returning(|_| Ok(true)); - // Never called - mock.expect_challenge().never(); - - mock.expect_get_block().returning(|_| { - Ok(Some(Block { - timestamp: U256::from(Utc::now().timestamp()), - ..Default::default() - })) - }); - - // Setting up challenger - let mut challenger = Challenger::new(address, mock, Some(10), Some(max_failures)); - - let res = challenger.process().await; - assert!(res.is_ok()); - } - - #[tokio::test] - async fn test_blocks_older_than_challenge_period_are_ignored() { - // Just random address - let address = "0x3D4c07Bd3cf5FB80ACB6Ec31531DBB338329b5F5" - .parse::
() - .unwrap(); - let max_failures: u8 = 3; - - // Setting up mock - let mut mock = MockTestScribe::new(); - mock.expect_get_latest_block_number() - .returning(|| Ok(U64::from(1000))); - - mock.expect_get_challenge_period().returning(|| Ok(600)); - // Let's fail on this function. - mock.expect_get_successful_challenges() - .returning(|_, _| Ok(vec![])); - - mock.expect_get_op_pokes().returning(|_, _| { - Ok(vec![( - OpPokedFilter { - ..Default::default() - }, - new_log_meta(U64::from(999)), - )]) - }); - // Never called, due to expired timestamp for poke - mock.expect_is_schnorr_signature_valid().never(); - // Never called - mock.expect_challenge().never(); - - // Returning block older than `get_challenge_period` - mock.expect_get_block().once().returning(|_| { - Ok(Some(Block { - timestamp: U256::from(Utc::now().timestamp() - 601), - ..Default::default() - })) - }); - - // Setting up challenger - let mut challenger = Challenger::new(address, mock, Some(10), Some(max_failures)); - - let res = challenger.process().await; - assert!(res.is_ok()); - } -} diff --git a/challenger/src/metrics.rs b/challenger/src/metrics.rs deleted file mode 100644 index 0a53a28..0000000 --- a/challenger/src/metrics.rs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (C) 2021-2023 Chronicle Labs, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -use ethers::types::{Address, H256}; -use metrics::{counter, describe_counter, describe_gauge, gauge}; - -const LAST_SCANNED_BLOCK_GAUGE: &str = "challenger_last_scanned_block"; -const ERRORS_COUNTER: &str = "challenger_errors_total"; -const CHALLENGE_COUNTER: &str = "challenger_challenges_total"; - -/// `set_last_scanned_block` sets the last scanned block for given `address` and `from` account. -pub fn set_last_scanned_block(address: Address, from: Address, block: i64) { - let labels = [ - ("address", format!("{:?}", address)), - (("from"), format!("{:?}", from)), - ]; - gauge!(LAST_SCANNED_BLOCK_GAUGE, &labels).set(block as f64); -} - -/// `inc_errors_counter` increments the errors counter for given `address`, `from` account -pub fn inc_errors_counter(address: Address, from: Address, error: &str) { - let labels = [ - ("address", format!("{:?}", address)), - ("from", format!("{:?}", from)), - ("error", String::from(error)), - ]; - counter!(ERRORS_COUNTER, &labels).increment(1); -} - -/// `inc_challenge_counter` increments the challenges counter for given `address`, `from` account and `tx` hash. -pub fn inc_challenge_counter(address: Address, from: Address, tx: H256) { - let labels = [ - ("address", format!("{:?}", address)), - ("from", format!("{:?}", from)), - ("tx", format!("{:?}", tx)), - ]; - counter!(CHALLENGE_COUNTER, &labels).increment(1); -} - -/// `register_custom_metrics` registers custom metrics to the registry. -/// It have to be called before you start processing & if you plans to serve `/metrics` route. -pub fn describe() { - describe_counter!( - ERRORS_COUNTER, - "Counts different errors in challenger process by given address" - ); - describe_counter!( - CHALLENGE_COUNTER, - "Counts happened challenges for given address and from account" - ); - describe_gauge!( - LAST_SCANNED_BLOCK_GAUGE, - "Keeps track of last scanned block for given address and from account" - ); -} diff --git a/src/main.rs b/src/main.rs index 92d3def..f20b675 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,25 +13,30 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +use alloy::primitives::Address; +use alloy::providers::ProviderBuilder; +use alloy::rpc::client::ClientBuilder; +use alloy::transports::layers::RetryBackoffLayer; use clap::Parser; use env_logger::Env; -use ethers::{ - core::types::Address, - prelude::SignerMiddleware, - providers::{Http, Middleware, Provider}, - signers::Signer, -}; + use eyre::Result; use log::{error, info}; +use scribe::contract::EventWithMetadata; +use scribe::events_listener::Poller; +use scribe::metrics; +use std::net::SocketAddr; +use std::sync::Arc; use std::{env, panic, path::PathBuf, time::Duration}; -use std::{net::SocketAddr, sync::Arc}; +use tokio::time::sleep; +use tokio_util::sync::CancellationToken; mod wallet; -use challenger_lib::{contract::HttpScribeOptimisticProvider, metrics, Challenger}; +// use challenger_lib::{contract::HttpScribeOptimisticProvider, metrics, Challenger}; -use tokio::signal; -use tokio::{task::JoinSet, time}; +use tokio::task::JoinSet; +use tokio::{select, signal}; use wallet::{CustomWallet, KeystoreWallet, PrivateKeyWallet}; @@ -113,35 +118,37 @@ impl CustomWallet for Cli {} #[tokio::main] async fn main() -> Result<()> { // Setting default log level to info - env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); + env_logger::Builder::from_env(Env::default().default_filter_or("info,challenger=debug")).init(); let args = Cli::parse(); - let provider = Provider::::try_from(args.rpc_url.as_str())?; - - info!("Connected to {:?}", provider.url()); - let chain_id = args - .chain_id - .unwrap_or(provider.get_chainid().await?.as_u64()); + log::info!("Using RPC URL: {:?}", &args.rpc_url); - info!("Chain id: {:?}", chain_id); - // Generating signer from given private key - let signer = args.wallet()?.unwrap().with_chain_id(chain_id); + // Building tx signer for provider + let signer = args.wallet()?.unwrap(); info!( - "Using {:?} for signing and chain_id {:?}", - signer.address(), - signer.chain_id() + "Using {:?} for signing transactions.", + signer.default_signer().address() ); - let signer_address = signer.address(); - let client = Arc::new(SignerMiddleware::new(provider, signer)); + // Create new HTTP client with retry backoff layer + let client = ClientBuilder::default() + .layer(RetryBackoffLayer::new(15, 200, 300)) + .http(args.rpc_url.parse()?); + + let provider = Arc::new(ProviderBuilder::new().wallet(signer).on_client(client)); let mut set = JoinSet::new(); + let cancel_token = CancellationToken::new(); // Removing duplicates from list of provided addresses let mut addresses = args.addresses; addresses.dedup(); + let addresses: Vec
= addresses + .iter() + .map(|a| a.parse().unwrap()) + .collect::>(); // Register Prometheus metrics let builder = PrometheusBuilder::new(); @@ -158,56 +165,64 @@ async fn main() -> Result<()> { .install() .expect("failed to install Prometheus recorder"); + log::info!("Starting Prometheus metrics collector on port: {}", port); + // Add challenger metrics description metrics::describe(); - let collector = Collector::new("challenger_"); // Add Prometheus metrics help for process metrics + let collector = Collector::new("challenger_"); collector.describe(); - // Start processing per address - for address in &addresses { - let address = address.parse::
()?; - - let client_clone = client.clone(); - set.spawn(async move { - info!("[{:?}] starting monitoring opPokes", address); - - let contract_provider = HttpScribeOptimisticProvider::new(address, client_clone); - let mut challenger = Challenger::new(address, contract_provider, None, None); - - let res = challenger.start().await; - // Check and add error into metrics - if res.is_err() { - // Increment error counter - metrics::inc_errors_counter( - address, - signer_address, - &res.err().unwrap().to_string(), - ); - } - }); - } + let (tx, rx) = tokio::sync::mpsc::channel::(100); + let mut poller = Poller::new( + addresses, + cancel_token.clone(), + provider.clone(), + tx.clone(), + ); + + // Run events listener process + set.spawn(async move { + if let Err(err) = poller.start().await { + log::error!("Poller error: {:?}", err); + } + }); // Run metrics collector process + let metrics_cancelation_token = cancel_token.clone(); set.spawn(async move { - let mut interval = time::interval(Duration::from_millis(750)); + let duration = Duration::from_millis(750); + log::info!("Starting metrics collector"); loop { - collector.collect(); - interval.tick().await; + select! { + _ = metrics_cancelation_token.cancelled() => { + log::info!("Metrics collector stopped"); + return; + }, + _ = sleep(duration) => { + collector.collect(); + } + } } }); + // TODO: Start challenger and event verifier process + tokio::select! { _ = signal::ctrl_c() => { info!("Received Ctrl-C, shutting down"); + cancel_token.cancel(); }, // some process terminated, no need to wait for others res = set.join_next() => { match res.unwrap() { - Ok(_) => info!("Task terminated without error, shutting down"), + Ok(_) => { + info!("Task terminated without error, shutting down"); + cancel_token.cancel(); + }, Err(e) => { error!("Task terminated with error: {:#?}", e.to_string()); }, @@ -215,8 +230,8 @@ async fn main() -> Result<()> { }, } - // Terminating all remaining tasks - set.shutdown().await; + // Wait for all tasks to finish + set.join_all().await; Ok(()) } @@ -225,6 +240,8 @@ async fn main() -> Result<()> { mod tests { use std::path::PathBuf; + use alloy::primitives::address; + use super::*; #[test] @@ -244,8 +261,8 @@ mod tests { let wallet = cli.wallet().unwrap().unwrap(); assert_eq!( - wallet.address(), - "91543660a715018cb35918add3085d08d7194724".parse().unwrap() + wallet.default_signer().address(), + address!("91543660a715018cb35918add3085d08d7194724") ); // Works with `0x` prefix @@ -264,8 +281,8 @@ mod tests { let wallet = cli.wallet().unwrap().unwrap(); assert_eq!( - wallet.address(), - "91543660a715018cb35918add3085d08d7194724".parse().unwrap() + wallet.default_signer().address(), + address!("91543660a715018cb35918add3085d08d7194724") ); } @@ -291,8 +308,8 @@ mod tests { let wallet = cli.wallet().unwrap().unwrap(); assert_eq!( - wallet.address(), - "ec554aeafe75601aaab43bd4621a22284db566c2".parse().unwrap() + wallet.default_signer().address(), + address!("ec554aeafe75601aaab43bd4621a22284db566c2") ); } @@ -315,8 +332,8 @@ mod tests { let wallet = cli.wallet().unwrap().unwrap(); assert_eq!( - wallet.address(), - "ec554aeafe75601aaab43bd4621a22284db566c2".parse().unwrap() + wallet.default_signer().address(), + address!("ec554aeafe75601aaab43bd4621a22284db566c2") ); } } diff --git a/src/wallet.rs b/src/wallet.rs index b69dfdf..182a817 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -13,7 +13,10 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -use ethers::signers::LocalWallet; +use alloy::{ + network::EthereumWallet, + signers::local::{LocalSigner, PrivateKeySigner}, +}; use eyre::{bail, Result, WrapErr}; use log::debug; use std::{ @@ -26,12 +29,12 @@ pub trait PrivateKeyWallet { fn raw_private_key(&self) -> Option; #[track_caller] - fn from_private_key(&self, private_key: &str) -> Result> { + fn from_private_key(&self, private_key: &str) -> Result> { debug!("Using private key from arguments"); let privk = private_key.trim().strip_prefix("0x").unwrap_or(private_key); - match privk.parse::() { - Ok(wallet) => Ok(Some(wallet)), + match privk.parse::() { + Ok(signer) => Ok(Some(EthereumWallet::new(signer))), Err(err) => bail!("Failed to parse private key: {:?}", err), } } @@ -79,7 +82,7 @@ pub trait KeystoreWallet { keystore_path: Option<&PathBuf>, keystore_password: Option<&String>, keystore_password_file: Option<&PathBuf>, - ) -> Result> { + ) -> Result> { Ok( match (keystore_path, keystore_password, keystore_password_file) { (Some(path), Some(password), _) => { @@ -87,20 +90,20 @@ pub trait KeystoreWallet { debug!("Using Keystore file from `{path:?}` and raw password"); - Some( - LocalWallet::decrypt_keystore(&path, password) - .wrap_err_with(|| format!("Failed to decrypt keystore {path:?}"))?, - ) + let signer = LocalSigner::decrypt_keystore(&path, password) + .wrap_err_with(|| format!("Failed to decrypt keystore {path:?}"))?; + + Some(EthereumWallet::new(signer)) } (Some(path), _, Some(password_file)) => { let path = self.find_keystore_file(path)?; debug!("Using Keystore file from `{path:?}` and password from file"); - Some( - LocalWallet::decrypt_keystore(&path, self.password_from_file(password_file)?) - .wrap_err_with(|| format!("Failed to decrypt keystore {path:?} with password file {password_file:?}"))?, - ) + let signer = LocalSigner::decrypt_keystore(&path, self.password_from_file(password_file)?) + .wrap_err_with(|| format!("Failed to decrypt keystore {path:?} with password file {password_file:?}"))?; + + Some(EthereumWallet::new(signer)) } (Some(path), None, None) => { let path = self.find_keystore_file(path)?; @@ -110,7 +113,11 @@ pub trait KeystoreWallet { ); let password = rpassword::prompt_password("Enter keystore password:")?; - Some(LocalWallet::decrypt_keystore(path, password)?) + + let signer = LocalSigner::decrypt_keystore(&path, password) + .wrap_err_with(|| format!("Failed to decrypt keystore {path:?}"))?; + + Some(EthereumWallet::new(signer)) } (None, _, _) => None, }, @@ -120,7 +127,7 @@ pub trait KeystoreWallet { pub trait CustomWallet: PrivateKeyWallet + KeystoreWallet { /// Generates wallet for future sign - fn wallet(&self) -> Result> { + fn wallet(&self) -> Result> { match (&self.raw_private_key(), &self.keystore_path()) { (Some(secret), _) => self.from_private_key(secret), (_, Some(key)) => self.get_from_keystore( From 7fb89d2da20af330649ec3b644a2c31cc77deeec Mon Sep 17 00:00:00 2001 From: Konstantin Zolotarev Date: Mon, 23 Sep 2024 22:01:00 +0300 Subject: [PATCH 02/40] TMP commit --- challenger_old/Cargo.toml | 27 + challenger_old/src/contract.rs | 212 ++++ challenger_old/src/lib.rs | 879 +++++++++++++ crates/scribe/Cargo.toml | 13 + crates/scribe/abi/ScribeOptimistic.json | 1531 +++++++++++++++++++++++ crates/scribe/src/contract.rs | 138 ++ crates/scribe/src/events_listener.rs | 179 +++ crates/scribe/src/lib.rs | 18 + crates/scribe/src/metrics.rs | 64 + 9 files changed, 3061 insertions(+) create mode 100644 challenger_old/Cargo.toml create mode 100644 challenger_old/src/contract.rs create mode 100644 challenger_old/src/lib.rs create mode 100644 crates/scribe/Cargo.toml create mode 100644 crates/scribe/abi/ScribeOptimistic.json create mode 100644 crates/scribe/src/contract.rs create mode 100644 crates/scribe/src/events_listener.rs create mode 100644 crates/scribe/src/lib.rs create mode 100644 crates/scribe/src/metrics.rs diff --git a/challenger_old/Cargo.toml b/challenger_old/Cargo.toml new file mode 100644 index 0000000..6107888 --- /dev/null +++ b/challenger_old/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "challenger-lib" +version = "0.1.0" +edition = "2021" + +[lib] +doctest = false + +[dependencies] +ethers = { workspace = true } +tokio = { workspace = true, features = ["full"] } +hex-literal = { workspace = true } +env_logger = { workspace = true } +futures = { workspace = true } +eyre = { workspace = true } +log = { workspace = true, features = ["kv"] } +hex = { workspace = true } +chrono = { workspace = true } +lazy_static = { workspace = true } +tokio-util = { workspace = true } +async-trait = { workspace = true } +metrics = { workspace = true } + +prometheus = { version = "0.13.3", features = ["process"] } + +[dev-dependencies] +mockall = "0.12.1" diff --git a/challenger_old/src/contract.rs b/challenger_old/src/contract.rs new file mode 100644 index 0000000..df82d5a --- /dev/null +++ b/challenger_old/src/contract.rs @@ -0,0 +1,212 @@ +// Copyright (C) 2021-2023 Chronicle Labs, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +use async_trait::async_trait; +use std::{fmt::Debug, sync::Arc}; + +use ethers::{ + contract::{abigen, Contract, LogMeta}, + providers::Middleware, + types::{Address, Block, TransactionReceipt, ValueOrArray, H256, U64}, +}; +use eyre::{Result, WrapErr}; +use log::{debug, warn}; + +// Yes it generates smart contract code based on given ABI +abigen!(ScribeOptimistic, "./abi/ScribeOptimistic.json"); + +#[async_trait] +pub trait ScribeOptimisticProvider: Send + Sync { + /// Returns the latest block number from RPC. + async fn get_latest_block_number(&self) -> Result; + + /// Returns block details with given `block_number` from RPC. + async fn get_block(&self, block_number: U64) -> Result>>; + + /// Returns challenge period from ScribeOptimistic smart contract deployed to `address`. + async fn get_challenge_period(&self) -> Result; + + /// Returns list of `OpPokeChallengedSuccessfully` events and log metadata in between `from_block` and `to_block` + /// from smart contract deployed to `address`. + async fn get_successful_challenges( + &self, + from_block: U64, + to_block: U64, + ) -> Result>; + + /// Returns list of `OpPoked` events and log metadata in between `from_block` and `to_block` from smart contract + /// deployed to `address`. + async fn get_op_pokes( + &self, + from_block: U64, + to_block: U64, + ) -> Result>; + + /// Returns true if given `OpPoked` schnorr signature is valid. + async fn is_schnorr_signature_valid(&self, op_poked: OpPokedFilter) -> Result; + + /// Challenges given `OpPoked` event. + async fn challenge(&self, schnorr_data: SchnorrData) -> Result>; + + /// Returns the from account address. + fn get_from(&self) -> Option
{ + warn!( + "get_from() uses default sender address, it's not recommended to use it in production" + ); + Some(Address::zero()) + } +} + +#[derive(Debug)] +pub struct HttpScribeOptimisticProvider { + address: Address, + client: Arc, + contract: ScribeOptimistic, +} + +impl HttpScribeOptimisticProvider { + /// Creates new instance of `HttpScribeOptimisticProvider` under given `client` and `address`. + pub fn new(address: Address, client: Arc) -> Self { + let contract = ScribeOptimistic::new(address, client.clone()); + Self { + address, + client, + contract, + } + } +} + +#[async_trait] +impl ScribeOptimisticProvider for HttpScribeOptimisticProvider +where + M: 'static, + M::Error: 'static, +{ + fn get_from(&self) -> Option
{ + self.client.default_sender() + } + + /// Returns the latest block number from RPC. + async fn get_latest_block_number(&self) -> Result { + self.client + .get_block_number() + .await + .wrap_err("Failed to get latest block number") + } + + /// Returns block details with given `block_number` from RPC. + async fn get_block(&self, block_number: U64) -> Result>> { + self.client + .get_block(block_number) + .await + .wrap_err("Failed to get block details") + } + + /// Returns challenge period from ScribeOptimistic smart contract deployed to `address`. + async fn get_challenge_period(&self) -> Result { + debug!("[{:?}] Getting challenge period", self.address); + + self.contract + .op_challenge_period() + .call() + .await + .wrap_err("Failed to get challenge period") + } + + /// Returns list of `OpPokeChallengedSuccessfully` events and log metadata in between `from_block` and `to_block` + /// from smart contract deployed to `address`. + async fn get_successful_challenges( + &self, + from_block: U64, + to_block: U64, + ) -> Result> { + debug!( + "[{:?}] Searching OpPokeChallengedSuccessfully events from block {:?} to block {:?}", + self.address, from_block, to_block + ); + let event = + Contract::event_of_type::(self.client.clone()) + .address(ValueOrArray::Array(vec![self.address])) + .from_block(from_block) + .to_block(to_block); + + event.query_with_meta().await.wrap_err(format!( + "Failed to get OpPokeChallengedSuccessfully events from block {:?} to block {:?}", + from_block, to_block + )) + } + + /// Returns list of `OpPoked` events and log metadata in between `from_block` and `to_block` from smart contract + /// deployed to `address`. + async fn get_op_pokes( + &self, + from_block: U64, + to_block: U64, + ) -> Result> { + debug!( + "[{:?}] Searching OpPoked events from block {:?} to block {:?}", + self.address, from_block, to_block + ); + let event = Contract::event_of_type::(self.client.clone()) + .address(ValueOrArray::Array(vec![self.address])) + .from_block(from_block) + .to_block(to_block); + + event.query_with_meta().await.wrap_err(format!( + "Failed to get OpPoked events from block {:?} to block {:?}", + from_block, to_block + )) + } + + /// Returns true if given `OpPoked` schnorr signature is valid. + /// Validation process is based on given `OpPoked` event. + /// Validation logic described in here: https://github.com/chronicleprotocol/scribe/blob/main/docs/Scribe.md#verifying-optimistic-pokes + async fn is_schnorr_signature_valid(&self, op_poked: OpPokedFilter) -> Result { + debug!( + "[{:?}] Validating schnorr signature for {:?}", + self.address, op_poked + ); + + let message = self + .contract + .construct_poke_message(op_poked.poke_data) + .call() + .await?; + + self.contract + .is_acceptable_schnorr_signature_now(message, op_poked.schnorr_data) + .call() + .await + .wrap_err("Failed to validate schnorr signature for OpPoked event") + } + + /// Challenges given `OpPoked` event. + /// Executes `opChallenge` function from ScribeOptimistic smart contract with shnorr signature + /// taken from `OpPoked` event. + /// NOTE: You have to validate if schnorr signature is INVALID before calling this function ! + async fn challenge(&self, schnorr_data: SchnorrData) -> Result> { + debug!( + "[{:?}] Challenging schnorr data {:?}", + self.address, schnorr_data + ); + + self.contract + .op_challenge(schnorr_data) + .send() + .await? + .await + .wrap_err("Failed to challenge OpPoked event") + } +} diff --git a/challenger_old/src/lib.rs b/challenger_old/src/lib.rs new file mode 100644 index 0000000..59a9788 --- /dev/null +++ b/challenger_old/src/lib.rs @@ -0,0 +1,879 @@ +// Copyright (C) 2021-2023 Chronicle Labs, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +use chrono::{DateTime, Utc}; +use ethers::{ + contract::LogMeta, + core::types::{Address, U64}, +}; +use eyre::{bail, Result}; +use log::{debug, error, info, warn}; +use std::time::Duration; +use tokio::time; + +pub mod contract; +pub mod metrics; + +use contract::{OpPokeChallengedSuccessfullyFilter, OpPokedFilter, ScribeOptimisticProvider}; + +// Note: this is true virtually all of the time but because of leap seconds not always. +// We take minimal time just to be sure, it's always better to check outdated blocks +// rather than miss some. +const SLOT_PERIOD_SECONDS: u16 = 12; + +// Time interval in seconds to reload challenge period from contract. +const DEFAULT_CHALLENGE_PERIOD_RELOAD_INTERVAL: Duration = Duration::from_secs(600); + +// Time interval for checking new pokes in milliseconds. +const DEFAULT_CHECK_INTERVAL_IN_MS: u64 = 30_000; + +// Max number of failures before we stop processing address. +const MAX_FAILURE_COUNT: u8 = 3; + +#[derive(Debug)] +pub struct Challenger { + address: Address, + contract_provider: P, + last_processed_block: Option, + challenge_period_in_sec: u16, + challenge_period_last_updated_at: Option>, + max_failure_count: u8, + failure_count: u8, + tick_interval: Duration, +} + +impl

Challenger

+where + P: ScribeOptimisticProvider + 'static, +{ + pub fn new( + address: Address, + contract_provider: P, + tick_interval: Option, + max_failure_count: Option, + ) -> Self { + Self { + address, + contract_provider, + last_processed_block: None, + challenge_period_in_sec: 0, + challenge_period_last_updated_at: None, + failure_count: 0, + max_failure_count: max_failure_count.unwrap_or(MAX_FAILURE_COUNT), + tick_interval: Duration::from_millis( + tick_interval.unwrap_or(DEFAULT_CHECK_INTERVAL_IN_MS), + ), + } + } + + // Sets last processed block number in challenger + fn set_last_processed_block(&mut self, block: U64) { + self.last_processed_block = Some(block); + + // Updating last scanned block metric + metrics::set_last_scanned_block( + self.address, + self.contract_provider.get_from().unwrap_or_default(), + block.as_u64() as i64, + ); + } + + // Reloads challenge period from contract. + // This function have to be called every N time, because challenge period can be changed by contract owner. + async fn reload_challenge_period(&mut self) -> Result<()> { + let challenge_period_in_sec = self.contract_provider.get_challenge_period().await?; + + debug!( + "[{:?}] Reloaded opChallenge period for contract is {:?}", + self.address, challenge_period_in_sec + ); + self.challenge_period_in_sec = challenge_period_in_sec; + self.challenge_period_last_updated_at = Some(Utc::now()); + + Ok(()) + } + + // Reloads the challenge period from the contract if it has not been updated within the default challenge period reload interval. + async fn reload_challenge_period_if_needed(&mut self) -> Result<()> { + let need_update = match self.challenge_period_last_updated_at { + None => true, + Some(utc) => { + let diff = Utc::now() - utc; + diff.to_std().unwrap() > DEFAULT_CHALLENGE_PERIOD_RELOAD_INTERVAL + } + }; + + if need_update { + self.reload_challenge_period().await.unwrap(); + } + + Ok(()) + } + + // Gets earliest block number we can search for non challenged `opPokes` + async fn get_starting_block_number( + &self, + last_block_number: U64, + challenge_period_in_sec: u16, + ) -> Result { + debug!( + "[{:?}] Calculating starting block number, latest from chain {:?}, period {:?}", + self.address, last_block_number, challenge_period_in_sec + ); + + let blocks_per_period = challenge_period_in_sec / SLOT_PERIOD_SECONDS; + + Ok(last_block_number - blocks_per_period) + } + + // Check if given block_number for log is already non challengeable + async fn is_challengeable( + &self, + block_number: U64, + challenge_period_in_sec: u16, + ) -> Result { + // Checking if log is possible to challenge ? + let block = self + .contract_provider + .get_block(block_number) + .await? + .unwrap(); + + let diff = Utc::now().timestamp() as u64 - block.timestamp.as_u64(); + + Ok(challenge_period_in_sec > diff as u16) + } + + // This function is called every tick. + // Deciedes blocks range we have to process, processing them and if no error happened sets new `last_processed_block` for next tick. + // If error happened on next tick it will again try to process same blocks. + async fn process(&mut self) -> Result<()> { + // Reloads challenge period value + self.reload_challenge_period_if_needed().await.unwrap(); + + // Getting last block from chain + let latest_block_number = self + .contract_provider + .get_latest_block_number() + .await + .unwrap(); + + // Fetching block we have to start with + let from_block = self.last_processed_block.unwrap_or( + self.get_starting_block_number(latest_block_number, self.challenge_period_in_sec) + .await?, + ); + + // In some cases (block drop) our latest processed block can be bigger than latest block from chain, + // in this case we have to skip processing and reset last processed block, so on next tick we will retry. + // Also we returning error to increase failure count. + if from_block > latest_block_number { + // Resetting last processed block with latest chain block + self.set_last_processed_block(latest_block_number); + + bail!( + "Invalid block range {:?} - {:?}, from block is bigger than to block, resetting last processed block to latest block from chain.", + from_block, latest_block_number + ); + } + + // Processing blocks range + self.process_blocks_range(from_block, latest_block_number) + .await?; + + // Updating last processed block with latest chain block + self.set_last_processed_block(latest_block_number); + + Ok(()) + } + + // Validates all `OpPoked` events and challenges them if needed. + async fn process_blocks_range(&mut self, from_block: U64, to_block: U64) -> Result<()> { + debug!( + "[{:?}] Processing blocks range {:?} - {:?}", + self.address, from_block, to_block + ); + + // Fetch list of `OpPokeChallengedSuccessfully` events + let challenges = self + .contract_provider + .get_successful_challenges(from_block, to_block) + .await?; + + // Fetches `OpPoked` events + let op_pokes = self + .contract_provider + .get_op_pokes(from_block, to_block) + .await?; + + // ignoring already challenged pokes + let unchallenged_pokes = reject_challenged_pokes(op_pokes, challenges); + + // Check if we have unchallenged pokes + if unchallenged_pokes.is_empty() { + debug!( + "[{:?}] No unchallenged opPokes found in block range {:?} - {:?}, skipping...", + self.address, from_block, to_block + ); + return Ok(()); + } + + for (poke, meta) in unchallenged_pokes { + let challengeable = self + .is_challengeable(meta.block_number, self.challenge_period_in_sec) + .await?; + + if !challengeable { + error!( + "[{:?}] Block is to old for `opChallenge` block number: {:?}", + self.address, meta.block_number + ); + continue; + } + + let valid = self + .contract_provider + .is_schnorr_signature_valid(poke.clone()) + .await?; + + // If schnorr data is valid, we should not challenge it... + if valid { + debug!( + "[{:?}] Schnorr data for block {:?} is valid, nothing to do...", + self.address, meta.block_number + ); + + continue; + } + + info!( + "[{:?}] Schnorr data for block {:?} is not valid, trying to challenge...", + self.address, meta.block_number + ); + + // TODO: handle error gracefully, we should go further even if error happened + match self.contract_provider.challenge(poke.schnorr_data).await { + Ok(receipt) => { + if let Some(receipt) = receipt { + info!( + "[{:?}] Successfully sent `opChallenge` transaction for OpPoke on block {:?}: {:?}", + self.address, meta.block_number, receipt + ); + // Add challenge to metrics + metrics::inc_challenge_counter( + self.address, + self.contract_provider.get_from().unwrap_or_default(), + receipt.transaction_hash, + ); + } else { + warn!( + "[{:?}] Successfully sent `opChallenge` for block {:?} transaction but no receipt returned", + self.address, meta.block_number + ); + } + } + Err(err) => { + error!( + "[{:?}] Failed to make `opChallenge` call for block {:?}: {:?}", + self.address, meta.block_number, err + ); + } + }; + } + + Ok(()) + } + + /// Starts processing pokes for the given contract address using the specified provider and tick interval. + /// + /// The function uses a tokio::time::interval to run the process method at regular intervals specified by the tick_interval field. + /// + /// # Used arguments + /// + /// * `contract_address` - The address of the contract to process pokes for. + /// * `provider` - The provider to use for interacting with the Ethereum network. + /// * `tick_interval` - The interval at which to check for new pokes. + /// + /// # Examples + /// + /// ``` + /// use eyre::Result; + /// use ethers::providers::{Http, Provider}; + /// use challenger::{Challenger, HttpScribeOptimisticProvider}; + /// use std::time::Duration; + /// + /// #[tokio::main] + /// async fn main() -> Result<()> { + /// let rpc_provider = Provider::::connect("https://mainnet.infura.io/v3/your-project-id").await?; + /// let contract_address = "0x1234567890123456789012345678901234567890".parse()?; + /// let provider = HttpScribeOptimisticProvider::new(contract_address, rpc_provider); + /// let mut challenger = Challenger::new(contract_address, provider, Duration::from_secs(30), None); + /// + /// challenger.start().await? + /// } + /// ``` + pub async fn start(&mut self) -> Result<()> { + let mut interval = time::interval(self.tick_interval); + + loop { + match self.process().await { + Ok(_) => { + // Reset error counter + self.failure_count = 0; + } + Err(err) => { + error!("[{:?}] Failed to process opPokes: {:?}", self.address, err); + + // Increment error counter + metrics::inc_errors_counter( + self.address, + self.contract_provider.get_from().unwrap_or_default(), + &err.to_string(), + ); + + // Increment and check error counter + self.failure_count += 1; + if self.failure_count >= self.max_failure_count { + error!( + "[{:?}] Reached max failure count, stopping processing...", + self.address + ); + return Err(err); + } + } + } + + interval.tick().await; + } + } +} + +// Removes challenged pokes from list of loaded pokes. +// Logic is very simple, if `OpPokeChallengedSuccessfully` event is after `OpPoked` event, then we can safely +// say that `OpPoked` event is already challenged. So we need to validate sequence of events and remove all +// `OpPoked` events that has `OpPokeChallengedSuccessfully` event after it. +fn reject_challenged_pokes( + pokes: Vec<(OpPokedFilter, LogMeta)>, + challenges: Vec<(OpPokeChallengedSuccessfullyFilter, LogMeta)>, +) -> Vec<(OpPokedFilter, LogMeta)> { + if challenges.is_empty() || pokes.is_empty() { + return pokes; + } + let mut result: Vec<(OpPokedFilter, LogMeta)> = vec![]; + + if pokes.len() == 1 { + let (_, meta) = &pokes[0]; + for (_, c_meta) in challenges.clone() { + if c_meta.block_number > meta.block_number { + // empty result + return result; + } + } + return pokes; + } + + 'pokes_loop: for i in 0..pokes.len() { + let (poke, meta) = &pokes.get(i).unwrap(); + // If we do have next poke in list + if let Some((_, next_meta)) = &pokes.get(i + 1) { + for (_, c_meta) in challenges.clone() { + if meta.block_number < c_meta.block_number + && next_meta.block_number > c_meta.block_number + { + // poke already challenged + continue 'pokes_loop; + } + } + } else { + for (_, c_meta) in challenges.clone() { + if c_meta.block_number > meta.block_number { + // poke already challenged + continue 'pokes_loop; + } + } + } + result.push((poke.clone(), meta.clone())); + } + + result +} + +#[cfg(test)] +mod tests { + use super::*; + + use async_trait::async_trait; + use contract::SchnorrData; + use ethers::{ + contract::LogMeta, + types::{Block, TransactionReceipt, H160, H256, U256, U64}, + }; + use eyre::Result; + use mockall::{mock, predicate::*}; + + mock! { + pub TestScribe{} + + #[async_trait] + impl ScribeOptimisticProvider for TestScribe { + async fn get_latest_block_number(&self) -> Result; + + async fn get_block(&self, block_number: U64) -> Result>>; + + async fn get_challenge_period(&self) -> Result; + + async fn get_successful_challenges( + &self, + from_block: U64, + to_block: U64, + ) -> Result>; + + async fn get_op_pokes( + &self, + from_block: U64, + to_block: U64, + ) -> Result>; + + async fn is_schnorr_signature_valid(&self, op_poked: OpPokedFilter) -> Result; + + async fn challenge(&self, schnorr_data: SchnorrData) -> Result>; + } + } + + // Builds new LogMeta with default values, only `block_number` is useful for us. + fn new_log_meta(block_number: U64) -> LogMeta { + LogMeta { + block_number, + address: H160::from_low_u64_be(0), + block_hash: H256::from_low_u64_be(0), + transaction_hash: H256::from_low_u64_be(0), + transaction_index: U64::from(0), + log_index: U256::from(0), + } + } + + #[test] + fn test_reject_challenged_pokes() { + { + // Does nothing if no pokes or challenges + let pokes: Vec<(OpPokedFilter, LogMeta)> = vec![]; + let challenges: Vec<(OpPokeChallengedSuccessfullyFilter, LogMeta)> = vec![]; + + let result = reject_challenged_pokes(pokes.clone(), challenges); + + assert_eq!(result, pokes); + } + + { + // Only 1 poke - returns it back + let pokes: Vec<(OpPokedFilter, LogMeta)> = vec![( + OpPokedFilter { + ..Default::default() + }, + new_log_meta(U64::from(1)), + )]; + let challenges: Vec<(OpPokeChallengedSuccessfullyFilter, LogMeta)> = vec![]; + + let result = reject_challenged_pokes(pokes.clone(), challenges); + assert_eq!(result, pokes); + } + + { + // One poke one challenge after it + let pokes: Vec<(OpPokedFilter, LogMeta)> = vec![( + OpPokedFilter { + ..Default::default() + }, + new_log_meta(U64::from(1)), + )]; + let challenges: Vec<(OpPokeChallengedSuccessfullyFilter, LogMeta)> = vec![( + OpPokeChallengedSuccessfullyFilter { + ..Default::default() + }, + new_log_meta(U64::from(2)), + )]; + + let result = reject_challenged_pokes(pokes, challenges); + assert!(result.is_empty()); + } + + { + // One poke one challenge before it + let pokes: Vec<(OpPokedFilter, LogMeta)> = vec![( + OpPokedFilter { + ..Default::default() + }, + new_log_meta(U64::from(2)), + )]; + let challenges: Vec<(OpPokeChallengedSuccessfullyFilter, LogMeta)> = vec![( + OpPokeChallengedSuccessfullyFilter { + ..Default::default() + }, + new_log_meta(U64::from(1)), + )]; + + let result = reject_challenged_pokes(pokes.clone(), challenges); + assert_eq!(result, pokes); + } + + { + // Multi pokes - one challenge after first poke + let pokes: Vec<(OpPokedFilter, LogMeta)> = vec![ + ( + OpPokedFilter { + ..Default::default() + }, + new_log_meta(U64::from(1)), + ), + ( + OpPokedFilter { + ..Default::default() + }, + new_log_meta(U64::from(3)), + ), + ]; + let challenges: Vec<(OpPokeChallengedSuccessfullyFilter, LogMeta)> = vec![( + OpPokeChallengedSuccessfullyFilter { + ..Default::default() + }, + new_log_meta(U64::from(2)), + )]; + + let result = reject_challenged_pokes(pokes.clone(), challenges); + assert_eq!(result.len(), 1); + + let (_, meta) = result.first().unwrap(); + assert_eq!(meta.block_number, U64::from(3)); + } + + { + // Multi pokes & multi challenges in random order + let pokes: Vec<(OpPokedFilter, LogMeta)> = vec![ + ( + OpPokedFilter { + ..Default::default() + }, + new_log_meta(U64::from(1)), + ), + ( + OpPokedFilter { + ..Default::default() + }, + new_log_meta(U64::from(3)), + ), + ( + OpPokedFilter { + ..Default::default() + }, + new_log_meta(U64::from(4)), + ), + ( + OpPokedFilter { + ..Default::default() + }, + new_log_meta(U64::from(7)), + ), + ]; + let challenges: Vec<(OpPokeChallengedSuccessfullyFilter, LogMeta)> = vec![ + ( + OpPokeChallengedSuccessfullyFilter { + ..Default::default() + }, + new_log_meta(U64::from(2)), + ), + ( + OpPokeChallengedSuccessfullyFilter { + ..Default::default() + }, + new_log_meta(U64::from(5)), + ), + ( + OpPokeChallengedSuccessfullyFilter { + ..Default::default() + }, + new_log_meta(U64::from(6)), + ), + ]; + + let result = reject_challenged_pokes(pokes.clone(), challenges); + assert_eq!(result.len(), 2); + + let (_, meta) = result.first().unwrap(); + assert_eq!(meta.block_number, U64::from(3)); + + let (_, meta2) = result.get(1).unwrap(); + assert_eq!(meta2.block_number, U64::from(7)); + } + } + + #[tokio::test] + async fn test_challenger_returns_error_on_max_failures() { + // Just random address + let address = "0x3D4c07Bd3cf5FB80ACB6Ec31531DBB338329b5F5" + .parse::

() + .unwrap(); + let max_failures: u8 = 3; + + // Setting up mock + let mut mock = MockTestScribe::new(); + mock.expect_get_latest_block_number() + .returning(|| Ok(U64::from(100))); + + mock.expect_get_block().returning(|_| Ok(None)); + + mock.expect_get_challenge_period().returning(|| Ok(600)); + + // Let's fail on this function. + mock.expect_get_successful_challenges() + .times(usize::from(max_failures)) + .returning(|_, _| eyre::bail!("Error challenges")); + + mock.expect_get_op_pokes() + .returning(|_, _| eyre::bail!("Error op_pokes")); + + // Setting up challenger + let mut challenger = Challenger::new(address, mock, Some(10), Some(max_failures)); + + let res = challenger.start().await; + assert!(res.is_err()); + } + + #[tokio::test] + #[should_panic] + async fn test_challenge_period_requires_to_be_fetched() { + // Just random address + let address = "0x3D4c07Bd3cf5FB80ACB6Ec31531DBB338329b5F5" + .parse::
() + .unwrap(); + + // Setting up mock + let mut mock = MockTestScribe::new(); + mock.expect_get_latest_block_number() + .returning(|| Ok(U64::from(100))); + + mock.expect_get_block().returning(|_| Ok(None)); + + mock.expect_get_challenge_period() + .returning(|| eyre::bail!("Error get challenge period")); + + // Let's fail on this function. + mock.expect_get_successful_challenges() + .returning(|_, _| Ok(vec![])); + + mock.expect_get_op_pokes().returning(|_, _| Ok(vec![])); + + // Setting up challenger + let mut challenger = Challenger::new(address, mock, Some(10), None); + + challenger.process().await.unwrap(); + } + + #[tokio::test] + async fn test_last_processed_block_bigger_than_last_chain_block() { + // Just random address + let address = "0x3D4c07Bd3cf5FB80ACB6Ec31531DBB338329b5F5" + .parse::
() + .unwrap(); + let max_failures: u8 = 3; + + // Setting up mock + let mut mock = MockTestScribe::new(); + mock.expect_get_latest_block_number() + .returning(|| Ok(U64::from(1000))); + + mock.expect_get_challenge_period().returning(|| Ok(600)); + mock.expect_get_successful_challenges().never(); + mock.expect_get_op_pokes().never(); + mock.expect_challenge().never(); + mock.expect_get_block().never(); + + // Setting up challenger + let mut challenger = Challenger::new(address, mock, Some(10), Some(max_failures)); + challenger.set_last_processed_block(U64::from(1001)); + + let res = challenger.process().await; + // Should be error in result. + assert!(res.is_err()); + // Check we reseted the last processed block + assert!(challenger.last_processed_block == Some(U64::from(1000))); + } + + #[tokio::test] + async fn test_no_pokes_no_execution() { + // Just random address + let address = "0x3D4c07Bd3cf5FB80ACB6Ec31531DBB338329b5F5" + .parse::
() + .unwrap(); + let max_failures: u8 = 3; + + // Setting up mock + let mut mock = MockTestScribe::new(); + mock.expect_get_latest_block_number() + .returning(|| Ok(U64::from(1000))); + + mock.expect_get_challenge_period().returning(|| Ok(600)); + // Let's fail on this function. + mock.expect_get_successful_challenges() + .returning(|_, _| Ok(vec![])); + mock.expect_get_op_pokes().returning(|_, _| Ok(vec![])); + mock.expect_challenge().never(); + + mock.expect_get_block().never(); + + // Setting up challenger + let mut challenger = Challenger::new(address, mock, Some(10), Some(max_failures)); + + let res = challenger.process().await; + assert!(res.is_ok()); + } + + #[tokio::test] + async fn test_calls_challenge_on_invalid_signature() { + // Just random address + let address = "0x3D4c07Bd3cf5FB80ACB6Ec31531DBB338329b5F5" + .parse::
() + .unwrap(); + let max_failures: u8 = 3; + + // Setting up mock + let mut mock = MockTestScribe::new(); + mock.expect_get_latest_block_number() + .returning(|| Ok(U64::from(1000))); + + mock.expect_get_challenge_period().returning(|| Ok(600)); + // Let's fail on this function. + mock.expect_get_successful_challenges() + .returning(|_, _| Ok(vec![])); + + mock.expect_get_op_pokes().returning(|_, _| { + Ok(vec![( + OpPokedFilter { + ..Default::default() + }, + new_log_meta(U64::from(999)), + )]) + }); + mock.expect_is_schnorr_signature_valid() + .once() + .returning(|_| Ok(false)); + // Called only once ! + mock.expect_challenge().once().returning(|_| Ok(None)); + + mock.expect_get_block().returning(|_| { + Ok(Some(Block { + timestamp: U256::from(Utc::now().timestamp()), + ..Default::default() + })) + }); + + // Setting up challenger + let mut challenger = Challenger::new(address, mock, Some(10), Some(max_failures)); + + let res = challenger.process().await; + assert!(res.is_ok()); + } + + #[tokio::test] + async fn test_ignores_challenge_on_valid_signature() { + // Just random address + let address = "0x3D4c07Bd3cf5FB80ACB6Ec31531DBB338329b5F5" + .parse::
() + .unwrap(); + let max_failures: u8 = 3; + + // Setting up mock + let mut mock = MockTestScribe::new(); + mock.expect_get_latest_block_number() + .returning(|| Ok(U64::from(1000))); + + mock.expect_get_challenge_period().returning(|| Ok(600)); + // Let's fail on this function. + mock.expect_get_successful_challenges() + .returning(|_, _| Ok(vec![])); + + mock.expect_get_op_pokes().returning(|_, _| { + Ok(vec![( + OpPokedFilter { + ..Default::default() + }, + new_log_meta(U64::from(999)), + )]) + }); + mock.expect_is_schnorr_signature_valid() + .once() + .returning(|_| Ok(true)); + // Never called + mock.expect_challenge().never(); + + mock.expect_get_block().returning(|_| { + Ok(Some(Block { + timestamp: U256::from(Utc::now().timestamp()), + ..Default::default() + })) + }); + + // Setting up challenger + let mut challenger = Challenger::new(address, mock, Some(10), Some(max_failures)); + + let res = challenger.process().await; + assert!(res.is_ok()); + } + + #[tokio::test] + async fn test_blocks_older_than_challenge_period_are_ignored() { + // Just random address + let address = "0x3D4c07Bd3cf5FB80ACB6Ec31531DBB338329b5F5" + .parse::
() + .unwrap(); + let max_failures: u8 = 3; + + // Setting up mock + let mut mock = MockTestScribe::new(); + mock.expect_get_latest_block_number() + .returning(|| Ok(U64::from(1000))); + + mock.expect_get_challenge_period().returning(|| Ok(600)); + // Let's fail on this function. + mock.expect_get_successful_challenges() + .returning(|_, _| Ok(vec![])); + + mock.expect_get_op_pokes().returning(|_, _| { + Ok(vec![( + OpPokedFilter { + ..Default::default() + }, + new_log_meta(U64::from(999)), + )]) + }); + // Never called, due to expired timestamp for poke + mock.expect_is_schnorr_signature_valid().never(); + // Never called + mock.expect_challenge().never(); + + // Returning block older than `get_challenge_period` + mock.expect_get_block().once().returning(|_| { + Ok(Some(Block { + timestamp: U256::from(Utc::now().timestamp() - 601), + ..Default::default() + })) + }); + + // Setting up challenger + let mut challenger = Challenger::new(address, mock, Some(10), Some(max_failures)); + + let res = challenger.process().await; + assert!(res.is_ok()); + } +} diff --git a/crates/scribe/Cargo.toml b/crates/scribe/Cargo.toml new file mode 100644 index 0000000..0cc09e2 --- /dev/null +++ b/crates/scribe/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "scribe" +version = "0.1.0" +edition = "2021" + +[dependencies] +alloy = {workspace = true, features = ["full"]} +eyre = {workspace = true} +log = {workspace = true} +tokio = {workspace = true, features = ["full"]} +tokio-util = {workspace = true} +tower = {workspace = true, features = ["retry"]} +metrics = { workspace = true } diff --git a/crates/scribe/abi/ScribeOptimistic.json b/crates/scribe/abi/ScribeOptimistic.json new file mode 100644 index 0000000..1950706 --- /dev/null +++ b/crates/scribe/abi/ScribeOptimistic.json @@ -0,0 +1,1531 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "initialAuthed", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "wat_", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "numberSigners", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "bar", + "type": "uint8" + } + ], + "name": "BarNotReached", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "givenAge", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "currentTimestamp", + "type": "uint32" + } + ], + "name": "FutureMessage", + "type": "error" + }, + { + "inputs": [], + "name": "InChallengePeriod", + "type": "error" + }, + { + "inputs": [], + "name": "NoOpPokeToChallenge", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address" + } + ], + "name": "NotAuthorized", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address" + } + ], + "name": "NotTolled", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint160", + "name": "gotHash", + "type": "uint160" + }, + { + "internalType": "uint160", + "name": "wantHash", + "type": "uint160" + } + ], + "name": "SchnorrDataMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "SchnorrSignatureInvalid", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + } + ], + "name": "SignerNotFeed", + "type": "error" + }, + { + "inputs": [], + "name": "SignersNotOrdered", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "givenAge", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "currentAge", + "type": "uint32" + } + ], + "name": "StaleMessage", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "who", + "type": "address" + } + ], + "name": "AuthGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "who", + "type": "address" + } + ], + "name": "AuthRenounced", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "oldBar", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "newBar", + "type": "uint8" + } + ], + "name": "BarUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "feed", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "FeedDropped", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "feed", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "FeedLifted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldMaxChallengeReward", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newMaxChallengeReward", + "type": "uint256" + } + ], + "name": "MaxChallengeRewardUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "oldOpChallengePeriod", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "newOpChallengePeriod", + "type": "uint16" + } + ], + "name": "OpChallengePeriodUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "challenger", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "reward", + "type": "uint256" + } + ], + "name": "OpChallengeRewardPaid", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "schnorrErr", + "type": "bytes" + } + ], + "name": "OpPokeChallengedSuccessfully", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "caller", + "type": "address" + } + ], + "name": "OpPokeChallengedUnsuccessfully", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint128", + "name": "val", + "type": "uint128" + }, + { + "internalType": "uint32", + "name": "age", + "type": "uint32" + } + ], + "indexed": false, + "internalType": "struct IScribe.PokeData", + "name": "pokeData", + "type": "tuple" + } + ], + "name": "OpPokeDataDropped", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "opFeed", + "type": "address" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "signature", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "commitment", + "type": "address" + }, + { + "internalType": "bytes", + "name": "signersBlob", + "type": "bytes" + } + ], + "indexed": false, + "internalType": "struct IScribe.SchnorrData", + "name": "schnorrData", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint128", + "name": "val", + "type": "uint128" + }, + { + "internalType": "uint32", + "name": "age", + "type": "uint32" + } + ], + "indexed": false, + "internalType": "struct IScribe.PokeData", + "name": "pokeData", + "type": "tuple" + } + ], + "name": "OpPoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "val", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "age", + "type": "uint32" + } + ], + "name": "Poked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "who", + "type": "address" + } + ], + "name": "TollGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "who", + "type": "address" + } + ], + "name": "TollRenounced", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "who", + "type": "address" + } + ], + "name": "authed", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "authed", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "bar", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "who", + "type": "address" + } + ], + "name": "bud", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "challengeReward", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint128", + "name": "val", + "type": "uint128" + }, + { + "internalType": "uint32", + "name": "age", + "type": "uint32" + } + ], + "internalType": "struct IScribe.PokeData", + "name": "pokeData", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "signature", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "commitment", + "type": "address" + }, + { + "internalType": "bytes", + "name": "signersBlob", + "type": "bytes" + } + ], + "internalType": "struct IScribe.SchnorrData", + "name": "schnorrData", + "type": "tuple" + } + ], + "name": "constructOpPokeMessage", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint128", + "name": "val", + "type": "uint128" + }, + { + "internalType": "uint32", + "name": "age", + "type": "uint32" + } + ], + "internalType": "struct IScribe.PokeData", + "name": "pokeData", + "type": "tuple" + } + ], + "name": "constructPokeMessage", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "who", + "type": "address" + } + ], + "name": "deny", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "who", + "type": "address" + } + ], + "name": "diss", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "feedIndex", + "type": "uint256" + } + ], + "name": "drop", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "feedIndexes", + "type": "uint256[]" + } + ], + "name": "drop", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "feedRegistrationMessage", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "who", + "type": "address" + } + ], + "name": "feeds", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "feeds", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "feeds", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "message", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "signature", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "commitment", + "type": "address" + }, + { + "internalType": "bytes", + "name": "signersBlob", + "type": "bytes" + } + ], + "internalType": "struct IScribe.SchnorrData", + "name": "schnorrData", + "type": "tuple" + } + ], + "name": "isAcceptableSchnorrSignatureNow", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "who", + "type": "address" + } + ], + "name": "kiss", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct LibSecp256k1.Point", + "name": "pubKey", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "internalType": "struct IScribe.ECDSAData", + "name": "ecdsaData", + "type": "tuple" + } + ], + "name": "lift", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "internalType": "struct LibSecp256k1.Point[]", + "name": "pubKeys", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "internalType": "struct IScribe.ECDSAData[]", + "name": "ecdsaDatas", + "type": "tuple[]" + } + ], + "name": "lift", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "maxChallengeReward", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxFeeds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "signature", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "commitment", + "type": "address" + }, + { + "internalType": "bytes", + "name": "signersBlob", + "type": "bytes" + } + ], + "internalType": "struct IScribe.SchnorrData", + "name": "schnorrData", + "type": "tuple" + } + ], + "name": "opChallenge", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "opChallengePeriod", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "opFeedIndex", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint128", + "name": "val", + "type": "uint128" + }, + { + "internalType": "uint32", + "name": "age", + "type": "uint32" + } + ], + "internalType": "struct IScribe.PokeData", + "name": "pokeData", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "signature", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "commitment", + "type": "address" + }, + { + "internalType": "bytes", + "name": "signersBlob", + "type": "bytes" + } + ], + "internalType": "struct IScribe.SchnorrData", + "name": "schnorrData", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "internalType": "struct IScribe.ECDSAData", + "name": "ecdsaData", + "type": "tuple" + } + ], + "name": "opPoke", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint128", + "name": "val", + "type": "uint128" + }, + { + "internalType": "uint32", + "name": "age", + "type": "uint32" + } + ], + "internalType": "struct IScribe.PokeData", + "name": "pokeData", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "signature", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "commitment", + "type": "address" + }, + { + "internalType": "bytes", + "name": "signersBlob", + "type": "bytes" + } + ], + "internalType": "struct IScribe.SchnorrData", + "name": "schnorrData", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "internalType": "struct IScribe.ECDSAData", + "name": "ecdsaData", + "type": "tuple" + } + ], + "name": "opPoke_optimized_397084999", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "peek", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint128", + "name": "val", + "type": "uint128" + }, + { + "internalType": "uint32", + "name": "age", + "type": "uint32" + } + ], + "internalType": "struct IScribe.PokeData", + "name": "pokeData", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "signature", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "commitment", + "type": "address" + }, + { + "internalType": "bytes", + "name": "signersBlob", + "type": "bytes" + } + ], + "internalType": "struct IScribe.SchnorrData", + "name": "schnorrData", + "type": "tuple" + } + ], + "name": "poke", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint128", + "name": "val", + "type": "uint128" + }, + { + "internalType": "uint32", + "name": "age", + "type": "uint32" + } + ], + "internalType": "struct IScribe.PokeData", + "name": "pokeData", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "signature", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "commitment", + "type": "address" + }, + { + "internalType": "bytes", + "name": "signersBlob", + "type": "bytes" + } + ], + "internalType": "struct IScribe.SchnorrData", + "name": "schnorrData", + "type": "tuple" + } + ], + "name": "poke_optimized_7136211", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "read", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "readWithAge", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "who", + "type": "address" + } + ], + "name": "rely", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "bar_", + "type": "uint8" + } + ], + "name": "setBar", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "maxChallengeReward_", + "type": "uint256" + } + ], + "name": "setMaxChallengeReward", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "opChallengePeriod_", + "type": "uint16" + } + ], + "name": "setOpChallengePeriod", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "who", + "type": "address" + } + ], + "name": "tolled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tolled", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tryRead", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tryReadWithAge", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "who", + "type": "address" + } + ], + "name": "wards", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "wat", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/crates/scribe/src/contract.rs b/crates/scribe/src/contract.rs new file mode 100644 index 0000000..451ff96 --- /dev/null +++ b/crates/scribe/src/contract.rs @@ -0,0 +1,138 @@ +// Copyright (C) 2021-2023 Chronicle Labs, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +use std::{fmt::Debug, sync::Arc}; + +use alloy::{ + primitives::{Address, LogData}, + rpc::types::Log, + sol, + sol_types::SolEvent, +}; +use eyre::{bail, Result, WrapErr}; + +use crate::events_listener::RetryProviderWithSigner; + +// Generate the contract bindings for the ScribeOptimistic contract +sol! { + #[allow(missing_docs)] + #[sol(rpc)] + ScribeOptimistic, + "abi/ScribeOptimistic.json" +} + +impl Debug for IScribe::SchnorrData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SchnorrData") + .field("signature", &self.signature) + .field("commitment", &self.commitment) + .field("signersBlob", &self.signersBlob) + .finish() + } +} + +impl Debug for IScribe::PokeData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PokeData") + .field("val", &self.val) + .field("age", &self.age) + .finish() + } +} + +impl Debug for ScribeOptimistic::OpPoked { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("OpPoked") + .field("caller", &self.caller) + .field("opFeed", &self.opFeed) + .field("schnorrData", &self.schnorrData) + .field("pokeData", &self.pokeData) + .finish() + } +} + +impl Debug for ScribeOptimistic::OpPokeChallengedSuccessfully { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("OpPokeChallengedSuccessfully") + .field("caller", &self.caller) + .field("schnorrErr", &self.schnorrErr) + .finish() + } +} + +// Decode a log into a specific event +// Example: +// ```rust +// let event = decode_log::(&log)?; +// ``` +fn decode_log(log: &Log) -> Result { + let log_data: &LogData = log.as_ref(); + + E::decode_raw_log(log_data.topics().iter().copied(), &log_data.data, false) + .wrap_err_with(|| "Failed to decode log") +} + +#[derive(Debug)] +pub enum Event { + OpPoked(ScribeOptimistic::OpPoked), + OpPokeChallengedSuccessfully(ScribeOptimistic::OpPokeChallengedSuccessfully), +} + +impl Event { + /// Creates a new GeneralPokedEvent from a Log + pub fn from_log(log: Log) -> Result { + let Some(topic) = log.topic0() else { + bail!("No topic found in log for tx {:?}", log.transaction_hash) + }; + + match *topic { + ScribeOptimistic::OpPoked::SIGNATURE_HASH => { + let event = decode_log::(&log)?; + Ok(Self::OpPoked(event)) + } + ScribeOptimistic::OpPokeChallengedSuccessfully::SIGNATURE_HASH => { + let event = decode_log::(&log)?; + Ok(Self::OpPokeChallengedSuccessfully(event)) + } + _ => bail!("Unknown event {:#x}", topic), + } + } +} + +#[derive(Debug)] +pub struct EventWithMetadata { + event: Event, + log: Log, + address: Address, +} + +impl EventWithMetadata { + /// Creates a new EventWithMetadata from a Log + pub fn from_log(log: Log) -> Result { + let event = Event::from_log(log.clone())?; + let address = log.address(); + + Ok(Self { + event, + log, + address, + }) + } +} + +pub struct ScribeOptimisticInstance { + address: Address, + provider: Arc, +} diff --git a/crates/scribe/src/events_listener.rs b/crates/scribe/src/events_listener.rs new file mode 100644 index 0000000..b8e987e --- /dev/null +++ b/crates/scribe/src/events_listener.rs @@ -0,0 +1,179 @@ +// Copyright (C) 2021-2023 Chronicle Labs, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +use std::{sync::Arc, time::Duration}; + +use alloy::{ + network::{Ethereum, EthereumWallet}, + primitives::Address, + providers::{ + fillers::{FillProvider, JoinFill, WalletFiller}, + Identity, Provider, RootProvider, WalletProvider, + }, + rpc::types::{Filter, Log}, + sol_types::SolEvent, + transports::{ + http::{Client, Http}, + layers::RetryBackoffService, + }, +}; +use eyre::{Context, Result}; +use tokio::{select, sync::mpsc::Sender}; +use tokio_util::sync::CancellationToken; + +use crate::{ + contract::{ + EventWithMetadata, + ScribeOptimistic::{OpPokeChallengedSuccessfully, OpPoked}, + }, + metrics, +}; + +const POLL_INTERVAL_SEC: u64 = 30; +const MAX_ADDRESS_PER_REQUEST: usize = 50; + +/// The provider type used to interact with the Ethereum network. +pub type RetryProviderWithSigner = FillProvider< + JoinFill>, + RootProvider>>, + RetryBackoffService>, + Ethereum, +>; + +#[derive(Debug, Clone)] +pub struct Poller { + addresses: Vec
, + cancelation_token: CancellationToken, + provider: Arc, + last_processes_block: Option, + tx: Sender, +} + +impl Poller { + pub fn new( + addresses: Vec
, + cancelation_token: CancellationToken, + provider: Arc, + tx: Sender, + ) -> Self { + Self { + addresses, + cancelation_token, + provider, + tx, + last_processes_block: None, + } + } + + async fn query_logs( + &self, + chunk: Vec
, + from_block: u64, + to_block: u64, + ) -> Result> { + let filter = Filter::new() + .address(chunk.to_vec()) + .from_block(from_block) + .to_block(to_block) + .event_signature(vec![ + OpPoked::SIGNATURE_HASH, + OpPokeChallengedSuccessfully::SIGNATURE_HASH, + ]); + + log::trace!("[{:?}] Getting for new events", &chunk); + + self.provider + .get_logs(&filter) + .await + .wrap_err_with(|| format!("Failed to get logs for addresses: {:?}", chunk)) + } + + // Poll for new events in block range `self.last_processes_block..latest_block` + async fn poll(&mut self) -> Result<()> { + log::trace!("Polling for new events"); + + // Get latest block number + let latest_block = self.provider.get_block_number().await.unwrap(); + if latest_block <= self.last_processes_block.unwrap_or(0) { + log::warn!( + "Latest block {:?} is not greater than last processed block {:?}", + latest_block, + self.last_processes_block + ); + return Ok(()); + } + + // Split addresses into chunks of MAX_ADDRESS_PER_REQUEST to optimize amount of requests + for chunk in self.addresses.chunks(MAX_ADDRESS_PER_REQUEST) { + let logs = self + .query_logs( + chunk.to_vec(), + self.last_processes_block.unwrap(), + latest_block, + ) + .await; + + match logs { + Ok(logs) => { + log::debug!("[{:?}] Received {} logs", chunk, logs.len()); + + for log in logs { + match EventWithMetadata::from_log(log) { + Ok(event) => { + log::trace!("[{:?}] Event received: {:?}", chunk, &event); + // Send event to the channel + self.tx.send(event).await?; + } + Err(e) => { + log::error!("[{:?}] Failed to parse log: {:?}", chunk, e); + continue; + } + }; + } + } + Err(e) => { + log::error!("Failed to query logs: {:?}", e); + } + } + } + + self.last_processes_block = Some(latest_block); + // Updating last scanned block metric + metrics::set_last_scanned_block( + self.provider.default_signer_address(), + latest_block as i64, + ); + + Ok(()) + } + + /// Start the event listener + pub async fn start(&mut self) -> Result<()> { + log::debug!("Starting polling events from RPC..."); + + loop { + select! { + _ = self.cancelation_token.cancelled() => { + log::info!("Events listener cancelled"); + return Ok(()); + } + _ = tokio::time::sleep(Duration::from_secs(POLL_INTERVAL_SEC)) => { + log::trace!("Executing tick for events listener..."); + self.poll().await?; + } + } + } + } +} diff --git a/crates/scribe/src/lib.rs b/crates/scribe/src/lib.rs new file mode 100644 index 0000000..afebca3 --- /dev/null +++ b/crates/scribe/src/lib.rs @@ -0,0 +1,18 @@ +// Copyright (C) 2021-2023 Chronicle Labs, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +pub mod contract; +pub mod events_listener; +pub mod metrics; diff --git a/crates/scribe/src/metrics.rs b/crates/scribe/src/metrics.rs new file mode 100644 index 0000000..e7bfd60 --- /dev/null +++ b/crates/scribe/src/metrics.rs @@ -0,0 +1,64 @@ +// Copyright (C) 2021-2023 Chronicle Labs, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +use alloy::primitives::{Address, B256}; +use metrics::{counter, describe_counter, describe_gauge, gauge}; + +const LAST_SCANNED_BLOCK_GAUGE: &str = "challenger_last_scanned_block"; +const ERRORS_COUNTER: &str = "challenger_errors_total"; +const CHALLENGE_COUNTER: &str = "challenger_challenges_total"; + +/// `set_last_scanned_block` sets the last scanned block for given `address` and `from` account. +pub fn set_last_scanned_block(from: Address, block: i64) { + let labels = [(("from"), format!("{:?}", from))]; + gauge!(LAST_SCANNED_BLOCK_GAUGE, &labels).set(block as f64); +} + +/// `inc_errors_counter` increments the errors counter for given `address`, `from` account +pub fn inc_errors_counter(address: Address, from: Address, error: &str) { + let labels = [ + ("address", format!("{:?}", address)), + ("from", format!("{:?}", from)), + ("error", String::from(error)), + ]; + counter!(ERRORS_COUNTER, &labels).increment(1); +} + +/// `inc_challenge_counter` increments the challenges counter for given `address`, `from` account and `tx` hash. +pub fn inc_challenge_counter(address: Address, from: Address, tx: B256) { + let labels = [ + ("address", format!("{:?}", address)), + ("from", format!("{:?}", from)), + ("tx", format!("{:?}", tx)), + ]; + counter!(CHALLENGE_COUNTER, &labels).increment(1); +} + +/// `register_custom_metrics` registers custom metrics to the registry. +/// It have to be called before you start processing & if you plans to serve `/metrics` route. +pub fn describe() { + describe_counter!( + ERRORS_COUNTER, + "Counts different errors in challenger process by given address" + ); + describe_counter!( + CHALLENGE_COUNTER, + "Counts happened challenges for given address and from account" + ); + describe_gauge!( + LAST_SCANNED_BLOCK_GAUGE, + "Keeps track of last scanned block for given address and from account" + ); +} From 6a9bd9cd5662b2225cf8d458be8943d545b28f02 Mon Sep 17 00:00:00 2001 From: Konstantin Zolotarev Date: Mon, 30 Sep 2024 14:08:55 +0300 Subject: [PATCH 03/40] Added tmp code --- crates/scribe/src/contract.rs | 122 +++++++++++++++++---------- crates/scribe/src/deployment.rs | 55 ++++++++++++ crates/scribe/src/events_listener.rs | 7 +- crates/scribe/src/lib.rs | 1 + src/main.rs | 1 + 5 files changed, 140 insertions(+), 46 deletions(-) create mode 100644 crates/scribe/src/deployment.rs diff --git a/crates/scribe/src/contract.rs b/crates/scribe/src/contract.rs index 451ff96..2b6aa18 100644 --- a/crates/scribe/src/contract.rs +++ b/crates/scribe/src/contract.rs @@ -16,62 +16,26 @@ use std::{fmt::Debug, sync::Arc}; use alloy::{ - primitives::{Address, LogData}, + primitives::{Address, FixedBytes, LogData}, rpc::types::Log, sol, sol_types::SolEvent, }; use eyre::{bail, Result, WrapErr}; +use IScribe::SchnorrData; +use ScribeOptimistic::{OpPoked, ScribeOptimisticInstance}; -use crate::events_listener::RetryProviderWithSigner; +use crate::events_listener::{RetryProviderWithSigner, RpcRetryProvider}; // Generate the contract bindings for the ScribeOptimistic contract sol! { #[allow(missing_docs)] #[sol(rpc)] + #[derive(Debug)] ScribeOptimistic, "abi/ScribeOptimistic.json" } -impl Debug for IScribe::SchnorrData { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("SchnorrData") - .field("signature", &self.signature) - .field("commitment", &self.commitment) - .field("signersBlob", &self.signersBlob) - .finish() - } -} - -impl Debug for IScribe::PokeData { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("PokeData") - .field("val", &self.val) - .field("age", &self.age) - .finish() - } -} - -impl Debug for ScribeOptimistic::OpPoked { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("OpPoked") - .field("caller", &self.caller) - .field("opFeed", &self.opFeed) - .field("schnorrData", &self.schnorrData) - .field("pokeData", &self.pokeData) - .finish() - } -} - -impl Debug for ScribeOptimistic::OpPokeChallengedSuccessfully { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("OpPokeChallengedSuccessfully") - .field("caller", &self.caller) - .field("schnorrErr", &self.schnorrErr) - .finish() - } -} - // Decode a log into a specific event // Example: // ```rust @@ -132,7 +96,77 @@ impl EventWithMetadata { } } -pub struct ScribeOptimisticInstance { - address: Address, - provider: Arc, +#[allow(async_fn_in_trait)] +pub trait ScribeOptimisticProvider { + /// Returns challenge period from ScribeOptimistic smart contract deployed to `address`. + async fn get_challenge_period(&self) -> Result; + + /// Returns true if given `OpPoked` schnorr signature is valid. + async fn is_schnorr_signature_valid(&self, op_poked: OpPoked) -> Result; + + /// Challenges given `OpPoked` event. + async fn challenge(&self, schnorr_data: SchnorrData) -> Result>; + + /// Returns the address of the contract. + fn address(&self) -> &Address; +} + +#[derive(Debug)] +pub struct ScribeOptimisticProviderInstance { + // address: Address, + // provider: Arc, + contract: ScribeOptimisticInstance>, +} + +impl ScribeOptimisticProviderInstance { + /// Creates a new ScribeOptimisticInstance + pub fn new(address: Address, provider: Arc) -> Self { + let contract = ScribeOptimistic::new(address, provider.clone()); + Self { + // address, + // provider, + contract, + } + } +} + +impl ScribeOptimisticProvider for ScribeOptimisticProviderInstance { + async fn get_challenge_period(&self) -> Result { + Ok(self.contract.opChallengePeriod().call().await?._0) + } + + async fn is_schnorr_signature_valid(&self, op_poked: OpPoked) -> Result { + log::trace!("{:?} Validating OpPoke signature", self.contract.address()); + + let message = self + .contract + .constructPokeMessage(op_poked.pokeData) + .call() + .await? + ._0; + + let acceptable = self + .contract + .isAcceptableSchnorrSignatureNow(message, op_poked.schnorrData) + .call() + .await? + ._0; + + Ok(acceptable) + } + + async fn challenge(&self, schnorr_data: SchnorrData) -> Result> { + log::debug!("{:?} Challenging OpPoke", self.contract.address()); + self.contract + .opChallenge(schnorr_data) + .send() + .await? + .watch() + .await + .wrap_err("Failed to challenge") + } + + fn address(&self) -> &Address { + self.contract.address() + } } diff --git a/crates/scribe/src/deployment.rs b/crates/scribe/src/deployment.rs new file mode 100644 index 0000000..1444877 --- /dev/null +++ b/crates/scribe/src/deployment.rs @@ -0,0 +1,55 @@ +// Copyright (C) 2021-2023 Chronicle Labs, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +use eyre::Result; +use tokio::sync::mpsc::Receiver; +use tokio_util::sync::CancellationToken; + +use crate::contract::{EventWithMetadata, ScribeOptimisticProvider}; + +pub struct ContractHandler +where + C: ScribeOptimisticProvider, +{ + pub contract: C, + cancel: CancellationToken, + rx: Receiver, +} + +impl ContractHandler { + pub fn new(cancel: CancellationToken, contract: C, rx: Receiver) -> Self { + Self { + cancel, + contract, + rx, + } + } + + pub async fn start(&mut self) -> Result<()> { + loop { + tokio::select! { + _ = self.cancel.cancelled() => { + log::info!("[{:?}] Cancellation requested, stopping contract handler", self.contract.address()); + break; + } + event = self.rx.recv() => { + log::debug!("[{:?}] Received event: {:?}", self.contract.address(), event); + // TODO: Handle the event + } + } + } + todo!() + } +} diff --git a/crates/scribe/src/events_listener.rs b/crates/scribe/src/events_listener.rs index b8e987e..a42b686 100644 --- a/crates/scribe/src/events_listener.rs +++ b/crates/scribe/src/events_listener.rs @@ -45,10 +45,13 @@ const POLL_INTERVAL_SEC: u64 = 30; const MAX_ADDRESS_PER_REQUEST: usize = 50; /// The provider type used to interact with the Ethereum network. +pub type RpcRetryProvider = RetryBackoffService>; + +/// The provider type used to interact with the Ethereum network with a signer. pub type RetryProviderWithSigner = FillProvider< JoinFill>, - RootProvider>>, - RetryBackoffService>, + RootProvider, + RpcRetryProvider, Ethereum, >; diff --git a/crates/scribe/src/lib.rs b/crates/scribe/src/lib.rs index afebca3..665e4b8 100644 --- a/crates/scribe/src/lib.rs +++ b/crates/scribe/src/lib.rs @@ -14,5 +14,6 @@ // along with this program. If not, see . pub mod contract; +pub mod deployment; pub mod events_listener; pub mod metrics; diff --git a/src/main.rs b/src/main.rs index f20b675..93a08fd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -184,6 +184,7 @@ async fn main() -> Result<()> { // Run events listener process set.spawn(async move { + log::info!("Starting events listener"); if let Err(err) = poller.start().await { log::error!("Poller error: {:?}", err); } From aaa67f33993568b731eaf195c2aeaff76a6dec68 Mon Sep 17 00:00:00 2001 From: Konstantin Zolotarev Date: Mon, 30 Sep 2024 14:23:51 +0300 Subject: [PATCH 04/40] Added event handler description --- crates/scribe/src/deployment.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/crates/scribe/src/deployment.rs b/crates/scribe/src/deployment.rs index 1444877..95ad564 100644 --- a/crates/scribe/src/deployment.rs +++ b/crates/scribe/src/deployment.rs @@ -47,6 +47,18 @@ impl ContractHandler { event = self.rx.recv() => { log::debug!("[{:?}] Received event: {:?}", self.contract.address(), event); // TODO: Handle the event + // if opPoked + // start new process (process need to wait 200ms before start OpPoked validation) (need to wait beacuse next event can be ChallengeSuccessful and we wouldn't need to do anything) + // if next received event is ChallengeSuccessful -> terminate just started OpPoked process + // if no ChallengeSuccessful received in 200ms -> validate OpPoked + // if OpPoked is valid -> do nothing, all ok + // if OpPoked is invalid -> need to challenge it + // + // Challenge process have to be: + // 1. Send private challenge transaction + // 2. Wait for ChallengeSuccessful event for next 3-4 blocks + // 3. If ChallengeSuccessful received -> all ok, do nothing + // 4. If no ChallengeSuccessful received -> send public challenge transaction } } } From e4db1fd3ccbaf85445d19417fd1664a46db96a47 Mon Sep 17 00:00:00 2001 From: Max Wickham Date: Wed, 2 Oct 2024 14:09:14 +0100 Subject: [PATCH 05/40] broken --- Cargo.toml | 2 +- crates/scribe/abi/ScribeOptimistic.json | 206 ++++++++++---- crates/scribe/src/contract.rs | 39 ++- crates/scribe/src/deployment.rs | 67 ----- crates/scribe/src/event_handler.rs | 359 ++++++++++++++++++++++++ crates/scribe/src/events_listener.rs | 32 ++- crates/scribe/src/lib.rs | 2 +- src/main.rs | 62 +++- 8 files changed, 609 insertions(+), 160 deletions(-) delete mode 100644 crates/scribe/src/deployment.rs create mode 100644 crates/scribe/src/event_handler.rs diff --git a/Cargo.toml b/Cargo.toml index 5c04970..9a555f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [workspace] -# members = ["crates/*"] +members = ["crates/*"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/scribe/abi/ScribeOptimistic.json b/crates/scribe/abi/ScribeOptimistic.json index 1950706..30eb160 100644 --- a/crates/scribe/abi/ScribeOptimistic.json +++ b/crates/scribe/abi/ScribeOptimistic.json @@ -31,6 +31,17 @@ "name": "BarNotReached", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "feedId", + "type": "uint8" + } + ], + "name": "DoubleSigningAttempted", + "type": "error" + }, { "inputs": [ { @@ -52,6 +63,17 @@ "name": "InChallengePeriod", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "feedId", + "type": "uint8" + } + ], + "name": "InvalidFeedId", + "type": "error" + }, { "inputs": [], "name": "NoOpPokeToChallenge", @@ -111,11 +133,6 @@ "name": "SignerNotFeed", "type": "error" }, - { - "inputs": [], - "name": "SignersNotOrdered", - "type": "error" - }, { "inputs": [ { @@ -209,12 +226,6 @@ "internalType": "address", "name": "feed", "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "index", - "type": "uint256" } ], "name": "FeedDropped", @@ -234,12 +245,6 @@ "internalType": "address", "name": "feed", "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "index", - "type": "uint256" } ], "name": "FeedLifted", @@ -304,6 +309,29 @@ "name": "challenger", "type": "address" }, + { + "components": [ + { + "internalType": "bytes32", + "name": "signature", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "commitment", + "type": "address" + }, + { + "internalType": "bytes", + "name": "feedIds", + "type": "bytes" + } + ], + "indexed": false, + "internalType": "struct IScribe.SchnorrData", + "name": "schnorrData", + "type": "tuple" + }, { "indexed": false, "internalType": "uint256", @@ -323,6 +351,29 @@ "name": "caller", "type": "address" }, + { + "components": [ + { + "internalType": "bytes32", + "name": "signature", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "commitment", + "type": "address" + }, + { + "internalType": "bytes", + "name": "feedIds", + "type": "bytes" + } + ], + "indexed": false, + "internalType": "struct IScribe.SchnorrData", + "name": "schnorrData", + "type": "tuple" + }, { "indexed": false, "internalType": "bytes", @@ -341,6 +392,29 @@ "internalType": "address", "name": "caller", "type": "address" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "signature", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "commitment", + "type": "address" + }, + { + "internalType": "bytes", + "name": "feedIds", + "type": "bytes" + } + ], + "indexed": false, + "internalType": "struct IScribe.SchnorrData", + "name": "schnorrData", + "type": "tuple" } ], "name": "OpPokeChallengedUnsuccessfully", @@ -406,7 +480,7 @@ }, { "internalType": "bytes", - "name": "signersBlob", + "name": "feedIds", "type": "bytes" } ], @@ -610,7 +684,7 @@ }, { "internalType": "bytes", - "name": "signersBlob", + "name": "feedIds", "type": "bytes" } ], @@ -703,9 +777,9 @@ { "inputs": [ { - "internalType": "uint256", - "name": "feedIndex", - "type": "uint256" + "internalType": "uint8[]", + "name": "feedIds", + "type": "uint8[]" } ], "name": "drop", @@ -716,9 +790,9 @@ { "inputs": [ { - "internalType": "uint256[]", - "name": "feedIndexes", - "type": "uint256[]" + "internalType": "uint8", + "name": "feedId", + "type": "uint8" } ], "name": "drop", @@ -753,11 +827,6 @@ "internalType": "bool", "name": "", "type": "bool" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" } ], "stateMutability": "view", @@ -766,9 +835,9 @@ { "inputs": [ { - "internalType": "uint256", - "name": "index", - "type": "uint256" + "internalType": "uint8", + "name": "feedId", + "type": "uint8" } ], "name": "feeds", @@ -795,11 +864,6 @@ "internalType": "address[]", "name": "", "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "", - "type": "uint256[]" } ], "stateMutability": "view", @@ -826,7 +890,7 @@ }, { "internalType": "bytes", - "name": "signersBlob", + "name": "feedIds", "type": "bytes" } ], @@ -859,6 +923,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "latestRoundData", @@ -937,9 +1014,9 @@ "name": "lift", "outputs": [ { - "internalType": "uint256", + "internalType": "uint8", "name": "", - "type": "uint256" + "type": "uint8" } ], "stateMutability": "nonpayable", @@ -990,9 +1067,9 @@ "name": "lift", "outputs": [ { - "internalType": "uint256[]", + "internalType": "uint8[]", "name": "", - "type": "uint256[]" + "type": "uint8[]" } ], "stateMutability": "nonpayable", @@ -1011,19 +1088,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "maxFeeds", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [ { @@ -1040,7 +1104,7 @@ }, { "internalType": "bytes", - "name": "signersBlob", + "name": "feedIds", "type": "bytes" } ], @@ -1075,7 +1139,7 @@ }, { "inputs": [], - "name": "opFeedIndex", + "name": "opFeedId", "outputs": [ { "internalType": "uint8", @@ -1119,7 +1183,7 @@ }, { "internalType": "bytes", - "name": "signersBlob", + "name": "feedIds", "type": "bytes" } ], @@ -1188,7 +1252,7 @@ }, { "internalType": "bytes", - "name": "signersBlob", + "name": "feedIds", "type": "bytes" } ], @@ -1242,6 +1306,24 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "peep", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -1275,7 +1357,7 @@ }, { "internalType": "bytes", - "name": "signersBlob", + "name": "feedIds", "type": "bytes" } ], @@ -1322,7 +1404,7 @@ }, { "internalType": "bytes", - "name": "signersBlob", + "name": "feedIds", "type": "bytes" } ], diff --git a/crates/scribe/src/contract.rs b/crates/scribe/src/contract.rs index 2b6aa18..4bcbcad 100644 --- a/crates/scribe/src/contract.rs +++ b/crates/scribe/src/contract.rs @@ -16,10 +16,7 @@ use std::{fmt::Debug, sync::Arc}; use alloy::{ - primitives::{Address, FixedBytes, LogData}, - rpc::types::Log, - sol, - sol_types::SolEvent, + primitives::{Address, FixedBytes, LogData}, providers::Provider, rpc::types::{Log, TransactionRequest}, sol, sol_types::SolEvent }; use eyre::{bail, Result, WrapErr}; use IScribe::SchnorrData; @@ -77,15 +74,15 @@ impl Event { #[derive(Debug)] pub struct EventWithMetadata { - event: Event, + pub event: Event, log: Log, - address: Address, + pub address: Address, } impl EventWithMetadata { /// Creates a new EventWithMetadata from a Log pub fn from_log(log: Log) -> Result { - let event = Event::from_log(log.clone())?; + let event: Event = Event::from_log(log.clone())?; let address = log.address(); Ok(Self { @@ -109,13 +106,16 @@ pub trait ScribeOptimisticProvider { /// Returns the address of the contract. fn address(&self) -> &Address; + + /// Returns a new provider with the same signer. + fn get_new_provider(&self) -> Arc; } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ScribeOptimisticProviderInstance { // address: Address, // provider: Arc, - contract: ScribeOptimisticInstance>, + pub contract: ScribeOptimisticInstance>, } impl ScribeOptimisticProviderInstance { @@ -155,11 +155,21 @@ impl ScribeOptimisticProvider for ScribeOptimisticProviderInstance { Ok(acceptable) } + // fn challenge_tx(&self, schnorr_data: SchnorrData) -> TransactionRequest { + // self.contract + // .opChallenge(schnorr_data) + // .gas(200000).into_transaction_request() + // } + async fn challenge(&self, schnorr_data: SchnorrData) -> Result> { log::debug!("{:?} Challenging OpPoke", self.contract.address()); - self.contract - .opChallenge(schnorr_data) - .send() + let from_address = self.contract.address(); + log::info!("Challenging from address: {:?}", from_address); + let transaction = self.contract + .opChallenge(schnorr_data) + // TODO set gas limit properly + .gas(200000); + transaction.send() .await? .watch() .await @@ -169,4 +179,9 @@ impl ScribeOptimisticProvider for ScribeOptimisticProviderInstance { fn address(&self) -> &Address { self.contract.address() } + + fn get_new_provider(&self) -> Arc { + self.contract.provider().clone() + } + } diff --git a/crates/scribe/src/deployment.rs b/crates/scribe/src/deployment.rs deleted file mode 100644 index 95ad564..0000000 --- a/crates/scribe/src/deployment.rs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (C) 2021-2023 Chronicle Labs, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -use eyre::Result; -use tokio::sync::mpsc::Receiver; -use tokio_util::sync::CancellationToken; - -use crate::contract::{EventWithMetadata, ScribeOptimisticProvider}; - -pub struct ContractHandler -where - C: ScribeOptimisticProvider, -{ - pub contract: C, - cancel: CancellationToken, - rx: Receiver, -} - -impl ContractHandler { - pub fn new(cancel: CancellationToken, contract: C, rx: Receiver) -> Self { - Self { - cancel, - contract, - rx, - } - } - - pub async fn start(&mut self) -> Result<()> { - loop { - tokio::select! { - _ = self.cancel.cancelled() => { - log::info!("[{:?}] Cancellation requested, stopping contract handler", self.contract.address()); - break; - } - event = self.rx.recv() => { - log::debug!("[{:?}] Received event: {:?}", self.contract.address(), event); - // TODO: Handle the event - // if opPoked - // start new process (process need to wait 200ms before start OpPoked validation) (need to wait beacuse next event can be ChallengeSuccessful and we wouldn't need to do anything) - // if next received event is ChallengeSuccessful -> terminate just started OpPoked process - // if no ChallengeSuccessful received in 200ms -> validate OpPoked - // if OpPoked is valid -> do nothing, all ok - // if OpPoked is invalid -> need to challenge it - // - // Challenge process have to be: - // 1. Send private challenge transaction - // 2. Wait for ChallengeSuccessful event for next 3-4 blocks - // 3. If ChallengeSuccessful received -> all ok, do nothing - // 4. If no ChallengeSuccessful received -> send public challenge transaction - } - } - } - todo!() - } -} diff --git a/crates/scribe/src/event_handler.rs b/crates/scribe/src/event_handler.rs new file mode 100644 index 0000000..584c052 --- /dev/null +++ b/crates/scribe/src/event_handler.rs @@ -0,0 +1,359 @@ +// Copyright (C) 2021-2023 Chronicle Labs, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +use std::collections::HashMap; +use std::sync::Arc; +use std::time::Duration; + +use alloy::network::TransactionBuilder; +use alloy::primitives::Address; +use alloy::providers::ProviderBuilder; +use eyre::Result; +use tokio::sync::mpsc::{Receiver, Sender}; +use tokio::sync::Mutex; +use tokio::task::JoinSet; +use tokio_util::sync::CancellationToken; + +use crate::contract::{Event, ScribeOptimistic}; +use crate::contract::IScribe::SchnorrData; +use crate::contract::{ + EventWithMetadata, ScribeOptimisticProvider, ScribeOptimisticProviderInstance, +}; +use crate::events_listener::RetryProviderWithSigner; + +const CHALLENGE_POKE_DELAY_MS: u64 = 200; +const FLASHBOT_CHALLENGE_RETRY_COUNT: u64 = 3; +const CLASSIC_CHALLENGE_RETRY_COUNT: u64 = 3; + +// Take event logs and distribute them to the appropriate contract handler +// Each scribe address has its own contract handler instance +pub struct EventDistributor { + addresses: Vec
, + cancel: CancellationToken, + provider: Arc, + flashbot_provider: Arc, + rx: Receiver, + txs: HashMap>, +} + +impl EventDistributor { + pub fn new( + addresses: Vec
, + cancel: CancellationToken, + provider: Arc, + flashbot_provider: Arc, + rx: Receiver, + ) -> Self { + Self { + addresses, + cancel, + provider, + flashbot_provider, + rx, + txs: HashMap::new(), + } + } + + pub async fn start(&mut self) -> Result<()> { + let mut contract_handler_set = JoinSet::new(); + // Create a contract handler for each scribe address + for address in &self.addresses { + let (tx, rx) = tokio::sync::mpsc::channel::(100); + self.txs.insert(address.clone(), tx); + + let mut contract_handler = ContractEventHandler::new( + ScribeOptimisticProviderInstance::new(address.clone(), self.provider.clone()), + ScribeOptimisticProviderInstance::new(address.clone(), self.flashbot_provider.clone()), + self.cancel.clone(), + rx, + ); + contract_handler_set.spawn(async move { + let _ = contract_handler.start().await; + }); + } + + loop { + tokio::select! { + _ = self.cancel.cancelled() => { + break; + } + event = self.rx.recv() => { + match event { + Some(event) => { + event.address; + // Send the event to the appropriate contract handler + match self.txs.get(&event.address) { + Some(tx) => { + let _ = tx.send(event).await; + } + _ => {} + } + } + _ => {} + } + } + } + } + // Wait for all tasks to finish + contract_handler_set.join_all().await; + Ok(()) + } +} + +struct ContractEventHandler +where +C: ScribeOptimisticProvider + Clone + std::marker::Send + 'static+ std::marker::Sync, +{ + pub contract: C, + pub flashbot_contract: C, + // provider: Arc, + // flashbot_provider: Arc, + cancel: CancellationToken, + rx: Receiver, + cancel_challenge: Option, +} + +impl ContractEventHandler { + pub fn new( + contract: C, + flashbot_contract: C, + cancel: CancellationToken, + rx: Receiver, + ) -> Self { + Self { + contract, + flashbot_contract, + cancel, + rx, + cancel_challenge: None, + } + } + + pub async fn start(&mut self) -> Result<()> { + loop { + tokio::select! { + _ = self.cancel.cancelled() => { + log::info!("[{:?}] Cancellation requested, stopping contract handler", self.contract.address()); + break; + } + event = self.rx.recv() => { + log::debug!("[{:?}] Received event: {:?}", self.contract.address(), event); + + match event { + Some(event) => { + match event.event { + Event::OpPoked(op_poked) => { + log::debug!("[{:?}] OpPoked received", self.contract.address()); + + // Cancel challenge if any + self.cancel_challenge().await; + log::debug!("Challenge cancelled existing"); + + // Check if the schnorr signature is valid + let is_valid = self.contract.is_schnorr_signature_valid(op_poked.clone()).await?; + if !is_valid { + // Create a new challenge handler instance + // self.cancel_challenge = Some(CancellationToken::new()); + // let challenge_handler = Some(Arc::new(Mutex::new(ChallengeHandler::new( + // op_poked.schnorrData.clone(), + // self.cancel.clone(), + // self.cancel_challenge.as_ref().unwrap().clone(), + // self.contract.clone(), + // self.flashbot_contract.clone(), + // )))); + + + // // Spawn the asynchronous task + // tokio::spawn({ + // async move { + // let handler = challenge_handler.as_ref().unwrap().lock().await; + // let _ = handler.start().await; + // }}); + + let challenge_handler = ChallengeHandler::new( + op_poked.schnorrData.clone(), + self.cancel.clone(), + self.cancel_challenge.as_ref().unwrap().clone(), + self.contract.clone(), + self.flashbot_contract.clone(), + ); + let mut contract_handler_set = JoinSet::new(); + contract_handler_set.spawn({ + let challenge_handler = challenge_handler.clone(); + async move { + // let handler = challenge_handler.as_ref().unwrap().lock().await; + let _ = challenge_handler.start().await; + }}); + log::debug!("Spawned New challneger"); + } + } + Event::OpPokeChallengedSuccessfully(_) => { + self.cancel_challenge().await; + } + _ => { + // if event is None + } + } + } + _ => { + // if event is None + } + } + + // TODO: Handle the event + // if opPoked + // start new process (process need to wait 200ms before start OpPoked validation) (need to wait beacuse next event can be ChallengeSuccessful and we wouldn't need to do anything) + // if next received event is ChallengeSuccessful -> terminate just started OpPoked process + // if no ChallengeSuccessful received in 200ms -> validate OpPoked + // if OpPoked is valid -> do nothing, all ok + // if OpPoked is invalid -> need to challenge it + // + // Challenge process have to be: + // 1. Send private challenge transaction + // 2. Wait for ChallengeSuccessful event for next 3-4 blocks + // 3. If ChallengeSuccessful received -> all ok, do nothing + // 4. If no ChallengeSuccessful received -> send public challenge transaction + } + } + } + todo!() + } + + async fn cancel_challenge(&mut self) { + match &self.cancel_challenge { + Some(cancel) => { + cancel.cancel(); + self.cancel_challenge = None; + } + _ => {} + } + } +} + +// Challenge a single OpPoked event after a delay +// If the challenge is not cancelled within the delay time period, the challenge is performed +// +// where +// C: ScribeOptimisticProvider, +#[derive(Clone)] +struct ChallengeHandler +where +C: ScribeOptimisticProvider + std::marker::Send + Clone, { + pub schnorr_data: SchnorrData, + pub global_cancel: CancellationToken, + pub cancel: CancellationToken, + pub contract: C, + pub flashbot_contract: C, + // address: Address, + + // provider: Arc, + // flashbot_provider: Arc, +} + +impl ChallengeHandler { + pub fn new( + schnorr_data: SchnorrData, + global_cancel: CancellationToken, + cancel: CancellationToken, + contract: C, + flashbot_contract: C, + // address: Address, + // provider: Arc, + // flashbot_provider: Arc, + ) -> Self { + Self { + schnorr_data, + global_cancel, + cancel, + // address, + // provider, + // flashbot_provider, + contract, + flashbot_contract, + } + } + + pub async fn start(&self) -> Result<()> { + log::debug!("Challenge started"); + // Perform the challenge after 200ms + + let mut challenge_attempts: u64 = 0; + loop { + tokio::select! { + // Check if the challenge has been cancelled + _ = self.cancel.cancelled() => { + log::debug!("Challenge cancelled"); + break; + } + // Check if the global cancel command sent + _ = self.global_cancel.cancelled() => { + break; + } + _ = tokio::time::sleep(Duration::from_millis(CHALLENGE_POKE_DELAY_MS)) => { + const RETRY_RANGE_END: u64 = CLASSIC_CHALLENGE_RETRY_COUNT+FLASHBOT_CHALLENGE_RETRY_COUNT; + match challenge_attempts { + 0..FLASHBOT_CHALLENGE_RETRY_COUNT => { + // let contract = ScribeOptimisticProviderInstance::new(self.address, self.flashbot_provider.clone()); + let result = self.contract.challenge(self.schnorr_data.clone()).await; + log::debug!("Challenge result: {:?}", result); + match result { + Ok(tx_hash) => { + log::debug!("Flashbot transaction sent: {:?}", tx_hash); + break; + } + Err(e) => { + log::error!("Failed to send flashbot transaction: {:?}", e); + } + } + } + FLASHBOT_CHALLENGE_RETRY_COUNT..RETRY_RANGE_END => { + // let contract = ScribeOptimisticProviderInstance::new(self.address, self.provider.clone()); + let result = self.flashbot_contract.challenge(self.schnorr_data.clone()).await; + log::debug!("Challenge result: {:?}", result); + match result { + Ok(tx_hash) => { + log::debug!("Challenge transaction sent: {:?}", tx_hash); + break; + } + Err(e) => { + log::error!("Failed to send challenge transaction: {:?}", e); + } + } + } + _ => { + log::debug!("Challenge failed"); + break; + } + } + challenge_attempts += 1; + + + // // TODO count the number of challenges using flashbot + // // TODO count the number of public challenges + // // TODO print error if no callenge successful event is received + // log::debug!("Performing challenge"); + // // perform challenge + // let result = ScribeOptimisticProviderInstance::new(self.address, self.provider.clone()) + // .challenge(self.schnorr_data.clone()) + // .await; + // // TODO maybe move the received eth to a different address + // log::debug!("Challenge result: {:?}", result); + // break; + } + } + } + Ok(()) + } +} diff --git a/crates/scribe/src/events_listener.rs b/crates/scribe/src/events_listener.rs index a42b686..52705aa 100644 --- a/crates/scribe/src/events_listener.rs +++ b/crates/scribe/src/events_listener.rs @@ -19,7 +19,10 @@ use alloy::{ network::{Ethereum, EthereumWallet}, primitives::Address, providers::{ - fillers::{FillProvider, JoinFill, WalletFiller}, + fillers::{ + BlobGasFiller, CachedNonceManager, ChainIdFiller, FillProvider, GasFiller, JoinFill, + NonceFiller, WalletFiller, + }, Identity, Provider, RootProvider, WalletProvider, }, rpc::types::{Filter, Log}, @@ -49,9 +52,18 @@ pub type RpcRetryProvider = RetryBackoffService>; /// The provider type used to interact with the Ethereum network with a signer. pub type RetryProviderWithSigner = FillProvider< - JoinFill>, - RootProvider, - RpcRetryProvider, + JoinFill< + JoinFill< + JoinFill< + Identity, + JoinFill>>, + >, + ChainIdFiller, + >, + WalletFiller, + >, + RootProvider>>, + RetryBackoffService>, Ethereum, >; @@ -109,6 +121,10 @@ impl Poller { // Get latest block number let latest_block = self.provider.get_block_number().await.unwrap(); + if None == self.last_processes_block { + self.last_processes_block = Some(latest_block - 100); + } + // TODO remove this line if latest_block <= self.last_processes_block.unwrap_or(0) { log::warn!( "Latest block {:?} is not greater than last processed block {:?}", @@ -135,7 +151,7 @@ impl Poller { for log in logs { match EventWithMetadata::from_log(log) { Ok(event) => { - log::trace!("[{:?}] Event received: {:?}", chunk, &event); + log::debug!("[{:?}] Event received: {:?}", chunk, &event); // Send event to the channel self.tx.send(event).await?; } @@ -164,16 +180,16 @@ impl Poller { /// Start the event listener pub async fn start(&mut self) -> Result<()> { - log::debug!("Starting polling events from RPC..."); + log::info!("Starting polling events from RPC..."); loop { select! { _ = self.cancelation_token.cancelled() => { - log::info!("Events listener cancelled"); + log::info!("Challenger cancelled"); return Ok(()); } _ = tokio::time::sleep(Duration::from_secs(POLL_INTERVAL_SEC)) => { - log::trace!("Executing tick for events listener..."); + log::info!("Executing tick for events listener..."); self.poll().await?; } } diff --git a/crates/scribe/src/lib.rs b/crates/scribe/src/lib.rs index 665e4b8..1c191f5 100644 --- a/crates/scribe/src/lib.rs +++ b/crates/scribe/src/lib.rs @@ -14,6 +14,6 @@ // along with this program. If not, see . pub mod contract; -pub mod deployment; +pub mod event_handler; pub mod events_listener; pub mod metrics; diff --git a/src/main.rs b/src/main.rs index 93a08fd..595519a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,7 @@ // along with this program. If not, see . use alloy::primitives::Address; +use alloy::providers::fillers::ChainIdFiller; use alloy::providers::ProviderBuilder; use alloy::rpc::client::ClientBuilder; use alloy::transports::layers::RetryBackoffLayer; @@ -31,9 +32,9 @@ use std::{env, panic, path::PathBuf, time::Duration}; use tokio::time::sleep; use tokio_util::sync::CancellationToken; -mod wallet; +use scribe::event_handler; -// use challenger_lib::{contract::HttpScribeOptimisticProvider, metrics, Challenger}; +mod wallet; use tokio::task::JoinSet; use tokio::{select, signal}; @@ -126,7 +127,6 @@ async fn main() -> Result<()> { // Building tx signer for provider let signer = args.wallet()?.unwrap(); - info!( "Using {:?} for signing transactions.", signer.default_signer().address() @@ -137,7 +137,33 @@ async fn main() -> Result<()> { .layer(RetryBackoffLayer::new(15, 200, 300)) .http(args.rpc_url.parse()?); - let provider = Arc::new(ProviderBuilder::new().wallet(signer).on_client(client)); + let provider = Arc::new( + ProviderBuilder::new() + // Add gas automatic gas field completion + .with_recommended_fillers() + // Add chain id request from rpc + .filler(ChainIdFiller::new(args.chain_id)) + // Add default signer + .wallet(signer.clone()) + .on_client(client), + ); + + // Create new HTTP client for flashbots + let flashbot_client = ClientBuilder::default() + .layer(RetryBackoffLayer::new(15, 200, 300)) + .http(args.rpc_url.parse()?); + + let flashbot_provider = Arc::new( + ProviderBuilder::new() + // Add gas automatic gas field completion + .with_recommended_fillers() + // Add chain id request from rpc + .filler(ChainIdFiller::new(args.chain_id)) + // Add default signer + .wallet(signer.clone()) + .on_client(flashbot_client), + ); + let mut set = JoinSet::new(); let cancel_token = CancellationToken::new(); @@ -160,10 +186,9 @@ async fn main() -> Result<()> { let addr = SocketAddr::from(([0, 0, 0, 0], port)); - builder - .with_http_listener(addr) - .install() - .expect("failed to install Prometheus recorder"); + let _ = builder.with_http_listener(addr).install(); + + // .expect("failed to install Prometheus recorder"); log::info!("Starting Prometheus metrics collector on port: {}", port); @@ -175,13 +200,24 @@ async fn main() -> Result<()> { collector.describe(); let (tx, rx) = tokio::sync::mpsc::channel::(100); + + // Create events listener let mut poller = Poller::new( - addresses, + addresses.clone(), cancel_token.clone(), provider.clone(), tx.clone(), ); + // Create event handler + let mut event_distributor = event_handler::EventDistributor::new( + addresses.clone(), + cancel_token.clone(), + provider.clone(), + flashbot_provider.clone(), + rx, + ); + // Run events listener process set.spawn(async move { log::info!("Starting events listener"); @@ -190,6 +226,14 @@ async fn main() -> Result<()> { } }); + // Run event handler process + set.spawn(async move { + log::info!("Starting log handler"); + if let Err(err) = event_distributor.start().await { + log::error!("Log Handler error: {:?}", err); + } + }); + // Run metrics collector process let metrics_cancelation_token = cancel_token.clone(); set.spawn(async move { From 5388ea0070457a38f46600089d0a9a60a2809efd Mon Sep 17 00:00:00 2001 From: Max Wickham Date: Wed, 2 Oct 2024 14:19:53 +0100 Subject: [PATCH 06/40] flashbots --- crates/scribe/src/event_handler.rs | 119 ++++++++++++----------------- 1 file changed, 47 insertions(+), 72 deletions(-) diff --git a/crates/scribe/src/event_handler.rs b/crates/scribe/src/event_handler.rs index 584c052..d53261f 100644 --- a/crates/scribe/src/event_handler.rs +++ b/crates/scribe/src/event_handler.rs @@ -74,8 +74,9 @@ impl EventDistributor { self.txs.insert(address.clone(), tx); let mut contract_handler = ContractEventHandler::new( - ScribeOptimisticProviderInstance::new(address.clone(), self.provider.clone()), - ScribeOptimisticProviderInstance::new(address.clone(), self.flashbot_provider.clone()), + address.clone(), + self.provider.clone(), + self.flashbot_provider.clone(), self.cancel.clone(), rx, ); @@ -112,29 +113,28 @@ impl EventDistributor { } } -struct ContractEventHandler -where -C: ScribeOptimisticProvider + Clone + std::marker::Send + 'static+ std::marker::Sync, +struct ContractEventHandler { - pub contract: C, - pub flashbot_contract: C, - // provider: Arc, - // flashbot_provider: Arc, + scribe_address: Address, + provider: Arc, + flashbot_provider: Arc, cancel: CancellationToken, rx: Receiver, cancel_challenge: Option, } -impl ContractEventHandler { +impl ContractEventHandler { pub fn new( - contract: C, - flashbot_contract: C, + scribe_address: Address, + provider: Arc, + flashbot_provider: Arc, cancel: CancellationToken, rx: Receiver, ) -> Self { Self { - contract, - flashbot_contract, + scribe_address, + provider, + flashbot_provider, cancel, rx, cancel_challenge: None, @@ -145,57 +145,42 @@ impl loop { tokio::select! { _ = self.cancel.cancelled() => { - log::info!("[{:?}] Cancellation requested, stopping contract handler", self.contract.address()); + log::info!("[{:?}] Cancellation requested, stopping contract handler", self.scribe_address); break; } event = self.rx.recv() => { - log::debug!("[{:?}] Received event: {:?}", self.contract.address(), event); + log::debug!("[{:?}] Received event: {:?}", self.scribe_address, event); match event { Some(event) => { match event.event { Event::OpPoked(op_poked) => { - log::debug!("[{:?}] OpPoked received", self.contract.address()); + log::debug!("[{:?}] OpPoked received", self.scribe_address); // Cancel challenge if any self.cancel_challenge().await; log::debug!("Challenge cancelled existing"); // Check if the schnorr signature is valid - let is_valid = self.contract.is_schnorr_signature_valid(op_poked.clone()).await?; + let is_valid = ScribeOptimisticProviderInstance::new(self.scribe_address, self.flashbot_provider.clone()).is_schnorr_signature_valid(op_poked.clone()).await?; + // let is_valid = self.contract.is_schnorr_signature_valid(op_poked.clone()).await?; if !is_valid { // Create a new challenge handler instance - // self.cancel_challenge = Some(CancellationToken::new()); - // let challenge_handler = Some(Arc::new(Mutex::new(ChallengeHandler::new( - // op_poked.schnorrData.clone(), - // self.cancel.clone(), - // self.cancel_challenge.as_ref().unwrap().clone(), - // self.contract.clone(), - // self.flashbot_contract.clone(), - // )))); - - - // // Spawn the asynchronous task - // tokio::spawn({ - // async move { - // let handler = challenge_handler.as_ref().unwrap().lock().await; - // let _ = handler.start().await; - // }}); - - let challenge_handler = ChallengeHandler::new( - op_poked.schnorrData.clone(), + self.cancel_challenge = Some(CancellationToken::new()); + let challenge_handler = Some(Arc::new(Mutex::new(ChallengeHandler::new( + op_poked.schnorrData, self.cancel.clone(), self.cancel_challenge.as_ref().unwrap().clone(), - self.contract.clone(), - self.flashbot_contract.clone(), - ); - let mut contract_handler_set = JoinSet::new(); - contract_handler_set.spawn({ - let challenge_handler = challenge_handler.clone(); - async move { - // let handler = challenge_handler.as_ref().unwrap().lock().await; - let _ = challenge_handler.start().await; - }}); + self.scribe_address, + self.provider.clone(), + self.flashbot_provider.clone(), + )))); + + // Spawn the asynchronous task + tokio::spawn(async move { + let handler = challenge_handler.as_ref().unwrap().lock().await; + let _ = handler.start().await; + }); log::debug!("Spawned New challneger"); } } @@ -247,41 +232,31 @@ impl // // where // C: ScribeOptimisticProvider, -#[derive(Clone)] -struct ChallengeHandler -where -C: ScribeOptimisticProvider + std::marker::Send + Clone, { +struct ChallengeHandler { pub schnorr_data: SchnorrData, pub global_cancel: CancellationToken, pub cancel: CancellationToken, - pub contract: C, - pub flashbot_contract: C, - // address: Address, - - // provider: Arc, - // flashbot_provider: Arc, + address: Address, + provider: Arc, + flashbot_provider: Arc, } -impl ChallengeHandler { +impl ChallengeHandler { pub fn new( schnorr_data: SchnorrData, global_cancel: CancellationToken, cancel: CancellationToken, - contract: C, - flashbot_contract: C, - // address: Address, - // provider: Arc, - // flashbot_provider: Arc, + address: Address, + provider: Arc, + flashbot_provider: Arc, ) -> Self { Self { schnorr_data, global_cancel, cancel, - // address, - // provider, - // flashbot_provider, - contract, - flashbot_contract, + address, + provider, + flashbot_provider, } } @@ -305,8 +280,8 @@ impl ChallengeHandler const RETRY_RANGE_END: u64 = CLASSIC_CHALLENGE_RETRY_COUNT+FLASHBOT_CHALLENGE_RETRY_COUNT; match challenge_attempts { 0..FLASHBOT_CHALLENGE_RETRY_COUNT => { - // let contract = ScribeOptimisticProviderInstance::new(self.address, self.flashbot_provider.clone()); - let result = self.contract.challenge(self.schnorr_data.clone()).await; + let contract = ScribeOptimisticProviderInstance::new(self.address, self.flashbot_provider.clone()); + let result = contract.challenge(self.schnorr_data.clone()).await; log::debug!("Challenge result: {:?}", result); match result { Ok(tx_hash) => { @@ -319,8 +294,8 @@ impl ChallengeHandler } } FLASHBOT_CHALLENGE_RETRY_COUNT..RETRY_RANGE_END => { - // let contract = ScribeOptimisticProviderInstance::new(self.address, self.provider.clone()); - let result = self.flashbot_contract.challenge(self.schnorr_data.clone()).await; + let contract = ScribeOptimisticProviderInstance::new(self.address, self.provider.clone()); + let result = contract.challenge(self.schnorr_data.clone()).await; log::debug!("Challenge result: {:?}", result); match result { Ok(tx_hash) => { From 61131a13ffc4a60c4d0643209e05bd999b7e3aa2 Mon Sep 17 00:00:00 2001 From: Max Wickham Date: Wed, 2 Oct 2024 21:02:50 +0100 Subject: [PATCH 07/40] functional --- crates/scribe/src/contract.rs | 10 --- crates/scribe/src/event_handler.rs | 119 +++++++++++------------------ src/main.rs | 8 +- 3 files changed, 48 insertions(+), 89 deletions(-) diff --git a/crates/scribe/src/contract.rs b/crates/scribe/src/contract.rs index 4bcbcad..18b2a62 100644 --- a/crates/scribe/src/contract.rs +++ b/crates/scribe/src/contract.rs @@ -113,8 +113,6 @@ pub trait ScribeOptimisticProvider { #[derive(Debug, Clone)] pub struct ScribeOptimisticProviderInstance { - // address: Address, - // provider: Arc, pub contract: ScribeOptimisticInstance>, } @@ -123,8 +121,6 @@ impl ScribeOptimisticProviderInstance { pub fn new(address: Address, provider: Arc) -> Self { let contract = ScribeOptimistic::new(address, provider.clone()); Self { - // address, - // provider, contract, } } @@ -155,12 +151,6 @@ impl ScribeOptimisticProvider for ScribeOptimisticProviderInstance { Ok(acceptable) } - // fn challenge_tx(&self, schnorr_data: SchnorrData) -> TransactionRequest { - // self.contract - // .opChallenge(schnorr_data) - // .gas(200000).into_transaction_request() - // } - async fn challenge(&self, schnorr_data: SchnorrData) -> Result> { log::debug!("{:?} Challenging OpPoke", self.contract.address()); let from_address = self.contract.address(); diff --git a/crates/scribe/src/event_handler.rs b/crates/scribe/src/event_handler.rs index d53261f..787cd46 100644 --- a/crates/scribe/src/event_handler.rs +++ b/crates/scribe/src/event_handler.rs @@ -17,17 +17,15 @@ use std::collections::HashMap; use std::sync::Arc; use std::time::Duration; -use alloy::network::TransactionBuilder; use alloy::primitives::Address; -use alloy::providers::ProviderBuilder; use eyre::Result; use tokio::sync::mpsc::{Receiver, Sender}; use tokio::sync::Mutex; use tokio::task::JoinSet; use tokio_util::sync::CancellationToken; -use crate::contract::{Event, ScribeOptimistic}; -use crate::contract::IScribe::SchnorrData; +use crate::contract::Event; +use crate::contract::ScribeOptimistic::OpPoked; use crate::contract::{ EventWithMetadata, ScribeOptimisticProvider, ScribeOptimisticProviderInstance, }; @@ -73,7 +71,7 @@ impl EventDistributor { let (tx, rx) = tokio::sync::mpsc::channel::(100); self.txs.insert(address.clone(), tx); - let mut contract_handler = ContractEventHandler::new( + let mut contract_handler = EventHandler::new( address.clone(), self.provider.clone(), self.flashbot_provider.clone(), @@ -113,8 +111,8 @@ impl EventDistributor { } } -struct ContractEventHandler -{ +// Receive events for a specific scribe address and create or cancel challenge processes +struct EventHandler { scribe_address: Address, provider: Arc, flashbot_provider: Arc, @@ -123,7 +121,7 @@ struct ContractEventHandler cancel_challenge: Option, } -impl ContractEventHandler { +impl EventHandler { pub fn new( scribe_address: Address, provider: Arc, @@ -156,66 +154,44 @@ impl ContractEventHandler { match event.event { Event::OpPoked(op_poked) => { log::debug!("[{:?}] OpPoked received", self.scribe_address); - - // Cancel challenge if any - self.cancel_challenge().await; - log::debug!("Challenge cancelled existing"); - - // Check if the schnorr signature is valid - let is_valid = ScribeOptimisticProviderInstance::new(self.scribe_address, self.flashbot_provider.clone()).is_schnorr_signature_valid(op_poked.clone()).await?; - // let is_valid = self.contract.is_schnorr_signature_valid(op_poked.clone()).await?; - if !is_valid { - // Create a new challenge handler instance - self.cancel_challenge = Some(CancellationToken::new()); - let challenge_handler = Some(Arc::new(Mutex::new(ChallengeHandler::new( - op_poked.schnorrData, - self.cancel.clone(), - self.cancel_challenge.as_ref().unwrap().clone(), - self.scribe_address, - self.provider.clone(), - self.flashbot_provider.clone(), - )))); - - // Spawn the asynchronous task - tokio::spawn(async move { - let handler = challenge_handler.as_ref().unwrap().lock().await; - let _ = handler.start().await; - }); - log::debug!("Spawned New challneger"); - } + // Spawn a new challenge handler + self.spawn_challenge(op_poked).await; } Event::OpPokeChallengedSuccessfully(_) => { self.cancel_challenge().await; } - _ => { - // if event is None - } } } _ => { // if event is None } } - - // TODO: Handle the event - // if opPoked - // start new process (process need to wait 200ms before start OpPoked validation) (need to wait beacuse next event can be ChallengeSuccessful and we wouldn't need to do anything) - // if next received event is ChallengeSuccessful -> terminate just started OpPoked process - // if no ChallengeSuccessful received in 200ms -> validate OpPoked - // if OpPoked is valid -> do nothing, all ok - // if OpPoked is invalid -> need to challenge it - // - // Challenge process have to be: - // 1. Send private challenge transaction - // 2. Wait for ChallengeSuccessful event for next 3-4 blocks - // 3. If ChallengeSuccessful received -> all ok, do nothing - // 4. If no ChallengeSuccessful received -> send public challenge transaction } } } todo!() } + async fn spawn_challenge(&mut self, op_poked: OpPoked) { + // Ensure there is no existing challenge + self.cancel_challenge().await; + self.cancel_challenge = Some(CancellationToken::new()); + // Create a new challenger instance + let challenge_handler = Some(Arc::new(Mutex::new(ChallengeHandler::new( + op_poked, + self.cancel.clone(), + self.cancel_challenge.as_ref().unwrap().clone(), + self.scribe_address, + self.provider.clone(), + self.flashbot_provider.clone(), + )))); + // Spawn the asynchronous task + tokio::spawn(async move { + let handler = challenge_handler.as_ref().unwrap().lock().await; + let _ = handler.start().await; + }); + log::debug!("Spawned New challenger"); + } async fn cancel_challenge(&mut self) { match &self.cancel_challenge { Some(cancel) => { @@ -227,23 +203,22 @@ impl ContractEventHandler { } } -// Challenge a single OpPoked event after a delay -// If the challenge is not cancelled within the delay time period, the challenge is performed -// -// where -// C: ScribeOptimisticProvider, +// Handle the challenge process for a specific OpPoked event after a delay +// If cancelled before end of delay or inbewteen retries stop process +// First try challenge with flashbot provider, then with normal provider struct ChallengeHandler { - pub schnorr_data: SchnorrData, + pub op_poked: OpPoked, pub global_cancel: CancellationToken, pub cancel: CancellationToken, address: Address, + // TODO replace with ScribeOptimisticProviderInstance provider: Arc, flashbot_provider: Arc, } impl ChallengeHandler { pub fn new( - schnorr_data: SchnorrData, + op_poked: OpPoked, global_cancel: CancellationToken, cancel: CancellationToken, address: Address, @@ -251,7 +226,7 @@ impl ChallengeHandler { flashbot_provider: Arc, ) -> Self { Self { - schnorr_data, + op_poked, global_cancel, cancel, address, @@ -264,6 +239,7 @@ impl ChallengeHandler { log::debug!("Challenge started"); // Perform the challenge after 200ms + // TODO: better way to retry !!!!!!!!!!!!!! let mut challenge_attempts: u64 = 0; loop { tokio::select! { @@ -277,11 +253,17 @@ impl ChallengeHandler { break; } _ = tokio::time::sleep(Duration::from_millis(CHALLENGE_POKE_DELAY_MS)) => { + // Verify that the challenge is valid + let is_valid = ScribeOptimisticProviderInstance::new(self.address, self.flashbot_provider.clone()).is_schnorr_signature_valid(self.op_poked.clone()).await?; + if is_valid { + log::debug!("OpPoked is valid, no need to challenge"); + break; + } const RETRY_RANGE_END: u64 = CLASSIC_CHALLENGE_RETRY_COUNT+FLASHBOT_CHALLENGE_RETRY_COUNT; match challenge_attempts { 0..FLASHBOT_CHALLENGE_RETRY_COUNT => { let contract = ScribeOptimisticProviderInstance::new(self.address, self.flashbot_provider.clone()); - let result = contract.challenge(self.schnorr_data.clone()).await; + let result = contract.challenge(self.op_poked.schnorrData.clone()).await; log::debug!("Challenge result: {:?}", result); match result { Ok(tx_hash) => { @@ -295,7 +277,7 @@ impl ChallengeHandler { } FLASHBOT_CHALLENGE_RETRY_COUNT..RETRY_RANGE_END => { let contract = ScribeOptimisticProviderInstance::new(self.address, self.provider.clone()); - let result = contract.challenge(self.schnorr_data.clone()).await; + let result = contract.challenge(self.op_poked.schnorrData.clone()).await; log::debug!("Challenge result: {:?}", result); match result { Ok(tx_hash) => { @@ -313,19 +295,6 @@ impl ChallengeHandler { } } challenge_attempts += 1; - - - // // TODO count the number of challenges using flashbot - // // TODO count the number of public challenges - // // TODO print error if no callenge successful event is received - // log::debug!("Performing challenge"); - // // perform challenge - // let result = ScribeOptimisticProviderInstance::new(self.address, self.provider.clone()) - // .challenge(self.schnorr_data.clone()) - // .await; - // // TODO maybe move the received eth to a different address - // log::debug!("Challenge result: {:?}", result); - // break; } } } diff --git a/src/main.rs b/src/main.rs index 595519a..46f5828 100644 --- a/src/main.rs +++ b/src/main.rs @@ -149,6 +149,8 @@ async fn main() -> Result<()> { ); // Create new HTTP client for flashbots + // TODO add flashbots url to args + // TODO add correct gas handling etc. let flashbot_client = ClientBuilder::default() .layer(RetryBackoffLayer::new(15, 200, 300)) .http(args.rpc_url.parse()?); @@ -209,7 +211,7 @@ async fn main() -> Result<()> { tx.clone(), ); - // Create event handler + // Create event distributor let mut event_distributor = event_handler::EventDistributor::new( addresses.clone(), cancel_token.clone(), @@ -226,7 +228,7 @@ async fn main() -> Result<()> { } }); - // Run event handler process + // Run event distributor process set.spawn(async move { log::info!("Starting log handler"); if let Err(err) = event_distributor.start().await { @@ -253,8 +255,6 @@ async fn main() -> Result<()> { } }); - // TODO: Start challenger and event verifier process - tokio::select! { _ = signal::ctrl_c() => { info!("Received Ctrl-C, shutting down"); From 4ee4c33af2115911a94c7e781e47b1ae7f4aae68 Mon Sep 17 00:00:00 2001 From: Konstantin Zolotarev Date: Thu, 3 Oct 2024 17:52:31 +0300 Subject: [PATCH 08/40] Added challenge period draft + some logs --- crates/scribe/src/event_handler.rs | 34 ++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/crates/scribe/src/event_handler.rs b/crates/scribe/src/event_handler.rs index 787cd46..093440d 100644 --- a/crates/scribe/src/event_handler.rs +++ b/crates/scribe/src/event_handler.rs @@ -18,7 +18,7 @@ use std::sync::Arc; use std::time::Duration; use alloy::primitives::Address; -use eyre::Result; +use eyre::{Context, Result}; use tokio::sync::mpsc::{Receiver, Sender}; use tokio::sync::Mutex; use tokio::task::JoinSet; @@ -97,7 +97,9 @@ impl EventDistributor { Some(tx) => { let _ = tx.send(event).await; } - _ => {} + _ => { + log::warn!("Received event for unknown address: {:?}", event.address); + } } } _ => {} @@ -119,6 +121,7 @@ struct EventHandler { cancel: CancellationToken, rx: Receiver, cancel_challenge: Option, + challenge_period: Option, } impl EventHandler { @@ -136,10 +139,14 @@ impl EventHandler { cancel, rx, cancel_challenge: None, + challenge_period: None, } } pub async fn start(&mut self) -> Result<()> { + // We have to fail if no challenge period is fetched + self.fetch_challenge_period().await.unwrap(); + loop { tokio::select! { _ = self.cancel.cancelled() => { @@ -172,6 +179,21 @@ impl EventHandler { todo!() } + async fn fetch_challenge_period(&mut self) -> Result<()> { + // TODO: Add expiration time for the challenge period + if self.challenge_period.is_some() { + return Ok(()); + } + + let period = + ScribeOptimisticProviderInstance::new(self.scribe_address, self.provider.clone()) + .get_challenge_period() + .await?; + + self.challenge_period = Some(period); + Ok(()) + } + async fn spawn_challenge(&mut self, op_poked: OpPoked) { // Ensure there is no existing challenge self.cancel_challenge().await; @@ -185,6 +207,8 @@ impl EventHandler { self.provider.clone(), self.flashbot_provider.clone(), )))); + // TODO: Validate challenge period first... + // Spawn the asynchronous task tokio::spawn(async move { let handler = challenge_handler.as_ref().unwrap().lock().await; @@ -192,6 +216,7 @@ impl EventHandler { }); log::debug!("Spawned New challenger"); } + async fn cancel_challenge(&mut self) { match &self.cancel_challenge { Some(cancel) => { @@ -250,11 +275,12 @@ impl ChallengeHandler { } // Check if the global cancel command sent _ = self.global_cancel.cancelled() => { + log::debug!("Global cancel command received"); break; } _ = tokio::time::sleep(Duration::from_millis(CHALLENGE_POKE_DELAY_MS)) => { - // Verify that the challenge is valid - let is_valid = ScribeOptimisticProviderInstance::new(self.address, self.flashbot_provider.clone()).is_schnorr_signature_valid(self.op_poked.clone()).await?; + // Verify that the OpPoked is valid + let is_valid = ScribeOptimisticProviderInstance::new(self.address, self.provider.clone()).is_schnorr_signature_valid(self.op_poked.clone()).await?; if is_valid { log::debug!("OpPoked is valid, no need to challenge"); break; From a18030fc94d431973f9bc89376b79c84c9759e28 Mon Sep 17 00:00:00 2001 From: Max Wickham Date: Fri, 4 Oct 2024 11:11:42 +0100 Subject: [PATCH 09/40] cleanup --- crates/scribe/src/event_handler.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/scribe/src/event_handler.rs b/crates/scribe/src/event_handler.rs index 787cd46..069628d 100644 --- a/crates/scribe/src/event_handler.rs +++ b/crates/scribe/src/event_handler.rs @@ -175,11 +175,13 @@ impl EventHandler { async fn spawn_challenge(&mut self, op_poked: OpPoked) { // Ensure there is no existing challenge self.cancel_challenge().await; + // Create a new cancellation token self.cancel_challenge = Some(CancellationToken::new()); // Create a new challenger instance let challenge_handler = Some(Arc::new(Mutex::new(ChallengeHandler::new( op_poked, self.cancel.clone(), + // cancel_challenge garunteed to be Some self.cancel_challenge.as_ref().unwrap().clone(), self.scribe_address, self.provider.clone(), @@ -211,7 +213,7 @@ struct ChallengeHandler { pub global_cancel: CancellationToken, pub cancel: CancellationToken, address: Address, - // TODO replace with ScribeOptimisticProviderInstance + // TODO replace with ScribeOptimisticProviderInstances provider: Arc, flashbot_provider: Arc, } From a85c2c2e60c898363e35699caaaf4b4e40635773 Mon Sep 17 00:00:00 2001 From: Max Wickham Date: Fri, 4 Oct 2024 11:43:04 +0100 Subject: [PATCH 10/40] challenge period --- Cargo.lock | 65 +++++++++++++++++++++- Cargo.toml | 1 + crates/scribe/Cargo.toml | 1 + crates/scribe/src/contract.rs | 2 +- crates/scribe/src/event_handler.rs | 87 ++++++++++++++++++++---------- 5 files changed, 125 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1ebb3f8..b38f967 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -623,6 +623,21 @@ dependencies = [ "ws_stream_wasm", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.15" @@ -1059,6 +1074,7 @@ name = "challenger" version = "0.1.0" dependencies = [ "alloy", + "chrono", "clap", "env_logger", "eyre", @@ -1073,6 +1089,20 @@ dependencies = [ "warp", ] +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.6", +] + [[package]] name = "cipher" version = "0.4.4" @@ -1929,6 +1959,29 @@ dependencies = [ "tracing", ] +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core 0.52.0", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "idna" version = "0.5.0" @@ -3102,6 +3155,7 @@ name = "scribe" version = "0.1.0" dependencies = [ "alloy", + "chrono", "eyre", "log", "metrics", @@ -4043,7 +4097,16 @@ version = "0.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" dependencies = [ - "windows-core", + "windows-core 0.54.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ "windows-targets 0.52.6", ] diff --git a/Cargo.toml b/Cargo.toml index 9a555f8..f51eee8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,3 +44,4 @@ metrics-exporter-prometheus = { workspace = true } clap = { version = "4.3.21", features = ["derive", "env"] } rpassword = "7.2.0" +chrono = "0.4.38" diff --git a/crates/scribe/Cargo.toml b/crates/scribe/Cargo.toml index 0cc09e2..a7e657f 100644 --- a/crates/scribe/Cargo.toml +++ b/crates/scribe/Cargo.toml @@ -11,3 +11,4 @@ tokio = {workspace = true, features = ["full"]} tokio-util = {workspace = true} tower = {workspace = true, features = ["retry"]} metrics = { workspace = true } +chrono = "0.4.38" diff --git a/crates/scribe/src/contract.rs b/crates/scribe/src/contract.rs index 18b2a62..7d127e3 100644 --- a/crates/scribe/src/contract.rs +++ b/crates/scribe/src/contract.rs @@ -75,7 +75,7 @@ impl Event { #[derive(Debug)] pub struct EventWithMetadata { pub event: Event, - log: Log, + pub log: Log, pub address: Address, } diff --git a/crates/scribe/src/event_handler.rs b/crates/scribe/src/event_handler.rs index c8dc509..9b18c2f 100644 --- a/crates/scribe/src/event_handler.rs +++ b/crates/scribe/src/event_handler.rs @@ -18,8 +18,8 @@ use std::sync::Arc; use std::time::Duration; use alloy::primitives::Address; -use eyre::{Context, Result}; -use tokio::sync::mpsc::{Receiver, Sender}; +use eyre::{ Context, Result }; +use tokio::sync::mpsc::{ Receiver, Sender }; use tokio::sync::Mutex; use tokio::task::JoinSet; use tokio_util::sync::CancellationToken; @@ -27,7 +27,9 @@ use tokio_util::sync::CancellationToken; use crate::contract::Event; use crate::contract::ScribeOptimistic::OpPoked; use crate::contract::{ - EventWithMetadata, ScribeOptimisticProvider, ScribeOptimisticProviderInstance, + EventWithMetadata, + ScribeOptimisticProvider, + ScribeOptimisticProviderInstance, }; use crate::events_listener::RetryProviderWithSigner; @@ -52,7 +54,7 @@ impl EventDistributor { cancel: CancellationToken, provider: Arc, flashbot_provider: Arc, - rx: Receiver, + rx: Receiver ) -> Self { Self { addresses, @@ -76,7 +78,7 @@ impl EventDistributor { self.provider.clone(), self.flashbot_provider.clone(), self.cancel.clone(), - rx, + rx ); contract_handler_set.spawn(async move { let _ = contract_handler.start().await; @@ -121,7 +123,7 @@ struct EventHandler { cancel: CancellationToken, rx: Receiver, cancel_challenge: Option, - challenge_period: Option, + challenge_period: Option, } impl EventHandler { @@ -130,7 +132,7 @@ impl EventHandler { provider: Arc, flashbot_provider: Arc, cancel: CancellationToken, - rx: Receiver, + rx: Receiver ) -> Self { Self { scribe_address, @@ -146,22 +148,34 @@ impl EventHandler { pub async fn start(&mut self) -> Result<()> { // We have to fail if no challenge period is fetched self.fetch_challenge_period().await.unwrap(); - loop { tokio::select! { _ = self.cancel.cancelled() => { - log::info!("[{:?}] Cancellation requested, stopping contract handler", self.scribe_address); + log::info!( + "[{:?}] Cancellation requested, stopping contract handler", + self.scribe_address + ); break; } event = self.rx.recv() => { log::debug!("[{:?}] Received event: {:?}", self.scribe_address, event); - match event { Some(event) => { match event.event { Event::OpPoked(op_poked) => { log::debug!("[{:?}] OpPoked received", self.scribe_address); // Spawn a new challenge handler + // Check if the poke is within the challenge period + let event_timestamp = event.log.block_timestamp.unwrap() as u64; + let current_timestamp = chrono::Utc::now().timestamp() as u64; + if current_timestamp - event_timestamp > + self.challenge_period.unwrap() { + log::debug!( + "[{:?}] OpPoked received outside of challenge period", + self.scribe_address + ); + continue; + } self.spawn_challenge(op_poked).await; } Event::OpPokeChallengedSuccessfully(_) => { @@ -185,12 +199,12 @@ impl EventHandler { return Ok(()); } - let period = - ScribeOptimisticProviderInstance::new(self.scribe_address, self.provider.clone()) - .get_challenge_period() - .await?; + let period = ScribeOptimisticProviderInstance::new( + self.scribe_address, + self.provider.clone() + ).get_challenge_period().await?; - self.challenge_period = Some(period); + self.challenge_period = Some(period as u64); Ok(()) } @@ -200,15 +214,21 @@ impl EventHandler { // Create a new cancellation token self.cancel_challenge = Some(CancellationToken::new()); // Create a new challenger instance - let challenge_handler = Some(Arc::new(Mutex::new(ChallengeHandler::new( - op_poked, - self.cancel.clone(), - // cancel_challenge garunteed to be Some - self.cancel_challenge.as_ref().unwrap().clone(), - self.scribe_address, - self.provider.clone(), - self.flashbot_provider.clone(), - )))); + let challenge_handler = Some( + Arc::new( + Mutex::new( + ChallengeHandler::new( + op_poked, + self.cancel.clone(), + // cancel_challenge garunteed to be Some + self.cancel_challenge.as_ref().unwrap().clone(), + self.scribe_address, + self.provider.clone(), + self.flashbot_provider.clone() + ) + ) + ) + ); // TODO: Validate challenge period first... // Spawn the asynchronous task @@ -250,7 +270,7 @@ impl ChallengeHandler { cancel: CancellationToken, address: Address, provider: Arc, - flashbot_provider: Arc, + flashbot_provider: Arc ) -> Self { Self { op_poked, @@ -282,7 +302,10 @@ impl ChallengeHandler { } _ = tokio::time::sleep(Duration::from_millis(CHALLENGE_POKE_DELAY_MS)) => { // Verify that the OpPoked is valid - let is_valid = ScribeOptimisticProviderInstance::new(self.address, self.provider.clone()).is_schnorr_signature_valid(self.op_poked.clone()).await?; + let is_valid = ScribeOptimisticProviderInstance::new( + self.address, + self.provider.clone() + ).is_schnorr_signature_valid(self.op_poked.clone()).await?; if is_valid { log::debug!("OpPoked is valid, no need to challenge"); break; @@ -290,8 +313,12 @@ impl ChallengeHandler { const RETRY_RANGE_END: u64 = CLASSIC_CHALLENGE_RETRY_COUNT+FLASHBOT_CHALLENGE_RETRY_COUNT; match challenge_attempts { 0..FLASHBOT_CHALLENGE_RETRY_COUNT => { - let contract = ScribeOptimisticProviderInstance::new(self.address, self.flashbot_provider.clone()); - let result = contract.challenge(self.op_poked.schnorrData.clone()).await; + let contract = ScribeOptimisticProviderInstance::new( + self.address, self.flashbot_provider.clone() + ); + let result = contract.challenge( + self.op_poked.schnorrData.clone() + ).await; log::debug!("Challenge result: {:?}", result); match result { Ok(tx_hash) => { @@ -304,7 +331,9 @@ impl ChallengeHandler { } } FLASHBOT_CHALLENGE_RETRY_COUNT..RETRY_RANGE_END => { - let contract = ScribeOptimisticProviderInstance::new(self.address, self.provider.clone()); + let contract = ScribeOptimisticProviderInstance::new( + self.address, self.provider.clone() + ); let result = contract.challenge(self.op_poked.schnorrData.clone()).await; log::debug!("Challenge result: {:?}", result); match result { From fed6bc7ef128cc1a714b73736742f6bec71d4ada Mon Sep 17 00:00:00 2001 From: Max Wickham Date: Fri, 11 Oct 2024 10:49:01 +0100 Subject: [PATCH 11/40] challenge period check --- crates/scribe/src/contract.rs | 2 +- crates/scribe/src/event_handler.rs | 28 +++++++++++++++++++++++++--- src/main.rs | 6 ++++-- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/crates/scribe/src/contract.rs b/crates/scribe/src/contract.rs index 7d127e3..4e82104 100644 --- a/crates/scribe/src/contract.rs +++ b/crates/scribe/src/contract.rs @@ -16,7 +16,7 @@ use std::{fmt::Debug, sync::Arc}; use alloy::{ - primitives::{Address, FixedBytes, LogData}, providers::Provider, rpc::types::{Log, TransactionRequest}, sol, sol_types::SolEvent + primitives::{Address, FixedBytes, LogData}, rpc::types::Log, sol, sol_types::SolEvent }; use eyre::{bail, Result, WrapErr}; use IScribe::SchnorrData; diff --git a/crates/scribe/src/event_handler.rs b/crates/scribe/src/event_handler.rs index 9b18c2f..0080e62 100644 --- a/crates/scribe/src/event_handler.rs +++ b/crates/scribe/src/event_handler.rs @@ -18,7 +18,9 @@ use std::sync::Arc; use std::time::Duration; use alloy::primitives::Address; -use eyre::{ Context, Result }; +use alloy::providers::Provider; +use alloy::rpc::types::BlockTransactionsKind; +use eyre::Result; use tokio::sync::mpsc::{ Receiver, Sender }; use tokio::sync::Mutex; use tokio::task::JoinSet; @@ -100,6 +102,7 @@ impl EventDistributor { let _ = tx.send(event).await; } _ => { + // Should never happen log::warn!("Received event for unknown address: {:?}", event.address); } } @@ -164,10 +167,28 @@ impl EventHandler { match event.event { Event::OpPoked(op_poked) => { log::debug!("[{:?}] OpPoked received", self.scribe_address); - // Spawn a new challenge handler // Check if the poke is within the challenge period - let event_timestamp = event.log.block_timestamp.unwrap() as u64; + let event_timestamp = match event.log.block_timestamp { + Some(timestamp) => timestamp, + None => { + let block = self.provider.get_block( + event.log.block_number.unwrap().into(), + BlockTransactionsKind::Hashes + ).await?; + let block = match block { + Some(block) => block, + None => { + log::error!("Block not found"); + continue; + } + }; + let timestamp = block.header.timestamp; + timestamp + } + }; let current_timestamp = chrono::Utc::now().timestamp() as u64; + log::debug!("[{:?}] OpPoked, event_timestamp: {:?}, current_timestamp: {:?}", + self.scribe_address, event_timestamp, current_timestamp); if current_timestamp - event_timestamp > self.challenge_period.unwrap() { log::debug!( @@ -176,6 +197,7 @@ impl EventHandler { ); continue; } + log::debug!("Spawning challenge..."); self.spawn_challenge(op_poked).await; } Event::OpPokeChallengedSuccessfully(_) => { diff --git a/src/main.rs b/src/main.rs index 46f5828..3dc38ac 100644 --- a/src/main.rs +++ b/src/main.rs @@ -59,6 +59,9 @@ struct Cli { #[arg(long, help = "Node HTTP RPC_URL, normally starts with https://****")] rpc_url: String, + #[arg(long, help = "Flashbot Node HTTP RPC_URL, normally starts with https://****")] + flashbot_rpc_url: String, + #[arg( long = "secret-key", help = "Private key in format `0x******` or `*******`. If provided, no need to use --keystore" @@ -149,11 +152,10 @@ async fn main() -> Result<()> { ); // Create new HTTP client for flashbots - // TODO add flashbots url to args // TODO add correct gas handling etc. let flashbot_client = ClientBuilder::default() .layer(RetryBackoffLayer::new(15, 200, 300)) - .http(args.rpc_url.parse()?); + .http(args.flashbot_rpc_url.parse()?); let flashbot_provider = Arc::new( ProviderBuilder::new() From db56a07d55c973659d11fa462ad10240b6a5ee6c Mon Sep 17 00:00:00 2001 From: Max Wickham Date: Sun, 13 Oct 2024 22:31:32 +0100 Subject: [PATCH 12/40] tests --- Cargo.lock | 490 ++- Cargo.toml | 6 +- crates/scribe/src/event_handler.rs | 15 +- crates/scribe/src/events_listener.rs | 17 +- src/main.rs | 257 ++ tests/fixtures/bytecode/ScribeOptimistic.json | 3815 +++++++++++++++++ 6 files changed, 4492 insertions(+), 108 deletions(-) create mode 100644 tests/fixtures/bytecode/ScribeOptimistic.json diff --git a/Cargo.lock b/Cargo.lock index b38f967..9658d97 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,21 +61,22 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8367891bf380210abb0d6aa30c5f85a9080cb4a066c4d5c5acadad630823751b" dependencies = [ - "alloy-consensus", - "alloy-contract", + "alloy-consensus 0.3.6", + "alloy-contract 0.3.6", "alloy-core", - "alloy-eips", + "alloy-eips 0.3.6", "alloy-genesis", - "alloy-network", - "alloy-provider", + "alloy-network 0.3.6", + "alloy-node-bindings", + "alloy-provider 0.3.6", "alloy-pubsub", - "alloy-rpc-client", + "alloy-rpc-client 0.3.6", "alloy-rpc-types", - "alloy-serde", - "alloy-signer", + "alloy-serde 0.3.6", + "alloy-signer 0.3.6", "alloy-signer-local", - "alloy-transport", - "alloy-transport-http", + "alloy-transport 0.3.6", + "alloy-transport-http 0.3.6", "alloy-transport-ipc", "alloy-transport-ws", ] @@ -96,14 +97,30 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "629b62e38d471cc15fea534eb7283d2f8a4e8bdb1811bcc5d66dda6cfce6fae1" dependencies = [ - "alloy-eips", + "alloy-eips 0.3.6", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.3.6", "c-kzg", "serde", ] +[[package]] +name = "alloy-consensus" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "705687d5bfd019fee57cf9e206b27b30a9a9617535d5590a02b171e813208f8e" +dependencies = [ + "alloy-eips 0.4.2", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 0.4.2", + "auto_impl", + "c-kzg", + "derive_more", + "serde", +] + [[package]] name = "alloy-contract" version = "0.3.6" @@ -112,14 +129,34 @@ checksum = "0eefe64fd344cffa9cf9e3435ec4e93e6e9c3481bc37269af988bf497faf4a6a" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", - "alloy-network", - "alloy-network-primitives", + "alloy-network 0.3.6", + "alloy-network-primitives 0.3.6", "alloy-primitives", - "alloy-provider", + "alloy-provider 0.3.6", "alloy-pubsub", - "alloy-rpc-types-eth", + "alloy-rpc-types-eth 0.3.6", + "alloy-sol-types", + "alloy-transport 0.3.6", + "futures", + "futures-util", + "thiserror", +] + +[[package]] +name = "alloy-contract" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "917f7d12cf3971dc8c11c9972f732b35ccb9aaaf5f28f2f87e9e6523bee3a8ad" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-network 0.4.2", + "alloy-network-primitives 0.4.2", + "alloy-primitives", + "alloy-provider 0.4.2", + "alloy-rpc-types-eth 0.4.2", "alloy-sol-types", - "alloy-transport", + "alloy-transport 0.4.2", "futures", "futures-util", "thiserror", @@ -139,9 +176,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "0.8.3" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4004925bff5ba0a11739ae84dbb6601a981ea692f3bd45b626935ee90a6b8471" +checksum = "f95d76a38cae906fd394a5afb0736aaceee5432efe76addfd71048e623e208af" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -167,9 +204,9 @@ dependencies = [ [[package]] name = "alloy-eip7702" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d319bb544ca6caeab58c39cea8921c55d924d4f68f2c60f24f914673f9a74a" +checksum = "ea59dc42102bc9a1905dc57901edc6dd48b9f38115df86c7d252acba70d71d04" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -187,7 +224,25 @@ dependencies = [ "alloy-eip7702", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.3.6", + "c-kzg", + "derive_more", + "once_cell", + "serde", + "sha2", +] + +[[package]] +name = "alloy-eips" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ffb906284a1e1f63c4607da2068c8197458a352d0b3e9796e67353d72a9be85" +dependencies = [ + "alloy-eip2930", + "alloy-eip7702", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 0.4.2", "c-kzg", "derive_more", "once_cell", @@ -202,15 +257,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a7a18afb0b318616b6b2b0e2e7ac5529d32a966c673b48091c9919e284e6aca" dependencies = [ "alloy-primitives", - "alloy-serde", + "alloy-serde 0.3.6", "serde", ] [[package]] name = "alloy-json-abi" -version = "0.8.3" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9996daf962fd0a90d3c93b388033228865953b92de7bb1959b891d78750a4091" +checksum = "03c66eec1acdd96b39b995b8f5ee5239bc0c871d62c527ae1ac9fd1d7fecd455" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -232,20 +287,55 @@ dependencies = [ "tracing", ] +[[package]] +name = "alloy-json-rpc" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fa8a1a3c4cbd221f2b8e3693aeb328fca79a757fe556ed08e47bbbc2a70db7" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "serde", + "serde_json", + "thiserror", + "tracing", +] + [[package]] name = "alloy-network" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb3705ce7d8602132bcf5ac7a1dd293a42adc2f183abf5907c30ac535ceca049" dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-json-rpc", - "alloy-network-primitives", + "alloy-consensus 0.3.6", + "alloy-eips 0.3.6", + "alloy-json-rpc 0.3.6", + "alloy-network-primitives 0.3.6", + "alloy-primitives", + "alloy-rpc-types-eth 0.3.6", + "alloy-serde 0.3.6", + "alloy-signer 0.3.6", + "alloy-sol-types", + "async-trait", + "auto_impl", + "futures-utils-wasm", + "thiserror", +] + +[[package]] +name = "alloy-network" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85fa23a6a9d612b52e402c995f2d582c25165ec03ac6edf64c861a76bc5b87cd" +dependencies = [ + "alloy-consensus 0.4.2", + "alloy-eips 0.4.2", + "alloy-json-rpc 0.4.2", + "alloy-network-primitives 0.4.2", "alloy-primitives", - "alloy-rpc-types-eth", - "alloy-serde", - "alloy-signer", + "alloy-rpc-types-eth 0.4.2", + "alloy-serde 0.4.2", + "alloy-signer 0.4.2", "alloy-sol-types", "async-trait", "auto_impl", @@ -259,31 +349,67 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94ad40869867ed2d9cd3842b1e800889e5b49e6b92da346e93862b4a741bedf3" dependencies = [ - "alloy-eips", + "alloy-eips 0.3.6", + "alloy-primitives", + "alloy-serde 0.3.6", + "serde", +] + +[[package]] +name = "alloy-network-primitives" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801492711d4392b2ccf5fc0bc69e299fa1aab15167d74dcaa9aab96a54f684bd" +dependencies = [ + "alloy-consensus 0.4.2", + "alloy-eips 0.4.2", "alloy-primitives", - "alloy-serde", + "alloy-serde 0.4.2", "serde", ] +[[package]] +name = "alloy-node-bindings" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5988a227293f949525f0a1b3e1ef728d2ef24afa96bad2b7788c6c9617fa3eec" +dependencies = [ + "alloy-genesis", + "alloy-primitives", + "k256", + "rand", + "serde_json", + "tempfile", + "thiserror", + "tracing", + "url", +] + [[package]] name = "alloy-primitives" -version = "0.8.3" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "411aff151f2a73124ee473708e82ed51b2535f68928b6a1caa8bc1246ae6f7cd" +checksum = "8ecb848c43f6b06ae3de2e4a67496cbbabd78ae87db0f1248934f15d76192c6a" dependencies = [ "alloy-rlp", "bytes", "cfg-if", "const-hex", "derive_more", + "foldhash", + "hashbrown 0.15.0", "hex-literal", + "indexmap", "itoa", "k256", "keccak-asm", + "paste", "proptest", "rand", "ruint", + "rustc-hash 2.0.0", "serde", + "sha3", "tiny-keccak", ] @@ -294,17 +420,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "927f708dd457ed63420400ee5f06945df9632d5d101851952056840426a10dc5" dependencies = [ "alloy-chains", - "alloy-consensus", - "alloy-eips", - "alloy-json-rpc", - "alloy-network", - "alloy-network-primitives", + "alloy-consensus 0.3.6", + "alloy-eips 0.3.6", + "alloy-json-rpc 0.3.6", + "alloy-network 0.3.6", + "alloy-network-primitives 0.3.6", + "alloy-node-bindings", "alloy-primitives", "alloy-pubsub", - "alloy-rpc-client", - "alloy-rpc-types-eth", - "alloy-transport", - "alloy-transport-http", + "alloy-rpc-client 0.3.6", + "alloy-rpc-types-anvil", + "alloy-rpc-types-eth 0.3.6", + "alloy-signer-local", + "alloy-transport 0.3.6", + "alloy-transport-http 0.3.6", "alloy-transport-ipc", "alloy-transport-ws", "async-stream", @@ -324,15 +453,46 @@ dependencies = [ "url", ] +[[package]] +name = "alloy-provider" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcfaa4ffec0af04e3555686b8aacbcdf7d13638133a0672749209069750f78a6" +dependencies = [ + "alloy-chains", + "alloy-consensus 0.4.2", + "alloy-eips 0.4.2", + "alloy-json-rpc 0.4.2", + "alloy-network 0.4.2", + "alloy-network-primitives 0.4.2", + "alloy-primitives", + "alloy-rpc-client 0.4.2", + "alloy-rpc-types-eth 0.4.2", + "alloy-transport 0.4.2", + "async-stream", + "async-trait", + "auto_impl", + "dashmap", + "futures", + "futures-utils-wasm", + "lru", + "pin-project", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + [[package]] name = "alloy-pubsub" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d05f63677e210d758cd5d6d1ce10f20c980c3560ccfbe79ba1997791862a04f" dependencies = [ - "alloy-json-rpc", + "alloy-json-rpc 0.3.6", "alloy-primitives", - "alloy-transport", + "alloy-transport 0.3.6", "bimap", "futures", "serde", @@ -371,11 +531,11 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d82952dca71173813d4e5733e2c986d8b04aea9e0f3b0a576664c232ad050a5" dependencies = [ - "alloy-json-rpc", + "alloy-json-rpc 0.3.6", "alloy-primitives", "alloy-pubsub", - "alloy-transport", - "alloy-transport-http", + "alloy-transport 0.3.6", + "alloy-transport-http 0.3.6", "alloy-transport-ipc", "alloy-transport-ws", "futures", @@ -390,6 +550,26 @@ dependencies = [ "url", ] +[[package]] +name = "alloy-rpc-client" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "370143ed581aace6e663342d21d209c6b2e34ee6142f7d6675adb518deeaf0dc" +dependencies = [ + "alloy-json-rpc 0.4.2", + "alloy-primitives", + "alloy-transport 0.4.2", + "alloy-transport-http 0.4.2", + "futures", + "pin-project", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower 0.5.1", + "tracing", +] + [[package]] name = "alloy-rpc-types" version = "0.3.6" @@ -397,8 +577,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64333d639f2a0cf73491813c629a405744e16343a4bc5640931be707c345ecc5" dependencies = [ "alloy-rpc-types-engine", - "alloy-rpc-types-eth", - "alloy-serde", + "alloy-rpc-types-eth 0.3.6", + "alloy-serde 0.3.6", + "serde", +] + +[[package]] +name = "alloy-rpc-types-anvil" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25cb45ad7c0930dd62eecf164d2afe4c3d2dd2c82af85680ad1f118e1e5cb83" +dependencies = [ + "alloy-primitives", + "alloy-serde 0.3.6", "serde", ] @@ -408,8 +599,8 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1464c4dd646e1bdfde86ae65ce5ba168dbb29180b478011fe87117ae46b1629b" dependencies = [ - "alloy-consensus", - "alloy-eips", + "alloy-consensus 0.3.6", + "alloy-eips 0.3.6", "alloy-primitives", "alloy-rlp", "derive_more", @@ -421,16 +612,35 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83aa984386deda02482660aa31cb8ca1e63d533f1c31a52d7d181ac5ec68e9b8" dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-network-primitives", + "alloy-consensus 0.3.6", + "alloy-eips 0.3.6", + "alloy-network-primitives 0.3.6", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.3.6", "alloy-sol-types", "cfg-if", "derive_more", - "hashbrown", + "hashbrown 0.14.5", + "itertools 0.13.0", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-rpc-types-eth" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413f4aa3ccf2c3e4234a047c5fa4727916d7daf25a89f9b765df0ba09784fd87" +dependencies = [ + "alloy-consensus 0.4.2", + "alloy-eips 0.4.2", + "alloy-network-primitives 0.4.2", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 0.4.2", + "alloy-sol-types", + "derive_more", "itertools 0.13.0", "serde", "serde_json", @@ -447,6 +657,17 @@ dependencies = [ "serde_json", ] +[[package]] +name = "alloy-serde" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dff0ab1cdd43ca001e324dc27ee0e8606bd2161d6623c63e0e0b8c4dfc13600" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", +] + [[package]] name = "alloy-signer" version = "0.3.6" @@ -461,16 +682,30 @@ dependencies = [ "thiserror", ] +[[package]] +name = "alloy-signer" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd4e0ad79c81a27ca659be5d176ca12399141659fef2bcbfdc848da478f4504" +dependencies = [ + "alloy-primitives", + "async-trait", + "auto_impl", + "elliptic-curve", + "k256", + "thiserror", +] + [[package]] name = "alloy-signer-local" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fabe917ab1778e760b4701628d1cae8e028ee9d52ac6307de4e1e9286ab6b5f" dependencies = [ - "alloy-consensus", - "alloy-network", + "alloy-consensus 0.3.6", + "alloy-network 0.3.6", "alloy-primitives", - "alloy-signer", + "alloy-signer 0.3.6", "async-trait", "elliptic-curve", "eth-keystore", @@ -481,9 +716,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.8.3" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0458ccb02a564228fcd76efb8eb5a520521a8347becde37b402afec9a1b83859" +checksum = "661c516eb1fa3294cc7f2fb8955b3b609d639c282ac81a4eedb14d3046db503a" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -495,9 +730,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "0.8.3" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc65475025fc1e84bf86fc840f04f63fcccdcf3cf12053c99918e4054dfbc69" +checksum = "ecbabb8fc3d75a0c2cea5215be22e7a267e3efde835b0f2a8922f5e3f5d47683" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -514,9 +749,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "0.8.3" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed10f0715a0b69fde3236ff3b9ae5f6f7c97db5a387747100070d3016b9266b" +checksum = "16517f2af03064485150d89746b8ffdcdbc9b6eeb3d536fb66efd7c2846fbc75" dependencies = [ "alloy-json-abi", "const-hex", @@ -531,9 +766,9 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "0.8.3" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3edae8ea1de519ccba896b6834dec874230f72fe695ff3c9c118e90ec7cff783" +checksum = "c07ebb0c1674ff8cbb08378d7c2e0e27919d2a2dae07ad3bca26174deda8d389" dependencies = [ "serde", "winnow", @@ -541,9 +776,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "0.8.3" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1eb88e4da0a1b697ed6a9f811fdba223cf4d5c21410804fd1707836af73a462b" +checksum = "8e448d879903624863f608c552d10efb0e0905ddbee98b0049412799911eb062" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -558,7 +793,26 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33616b2edf7454302a1d48084db185e52c309f73f6c10be99b0fe39354b3f1e9" dependencies = [ - "alloy-json-rpc", + "alloy-json-rpc 0.3.6", + "base64 0.22.1", + "futures-util", + "futures-utils-wasm", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower 0.5.1", + "tracing", + "url", +] + +[[package]] +name = "alloy-transport" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ac3e97dad3d31770db0fc89bd6a63b789fbae78963086733f960cf32c483904" +dependencies = [ + "alloy-json-rpc 0.4.2", "base64 0.22.1", "futures-util", "futures-utils-wasm", @@ -577,8 +831,8 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a944f5310c690b62bbb3e7e5ce34527cbd36b2d18532a797af123271ce595a49" dependencies = [ - "alloy-json-rpc", - "alloy-transport", + "alloy-json-rpc 0.3.6", + "alloy-transport 0.3.6", "reqwest", "serde_json", "tower 0.5.1", @@ -586,15 +840,25 @@ dependencies = [ "url", ] +[[package]] +name = "alloy-transport-http" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b367dcccada5b28987c2296717ee04b9a5637aacd78eacb1726ef211678b5212" +dependencies = [ + "alloy-transport 0.4.2", + "url", +] + [[package]] name = "alloy-transport-ipc" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09fd8491249f74d16ec979b1f5672377b12ebb818e6056478ffa386954dbd350" dependencies = [ - "alloy-json-rpc", + "alloy-json-rpc 0.3.6", "alloy-pubsub", - "alloy-transport", + "alloy-transport 0.3.6", "bytes", "futures", "interprocess", @@ -612,7 +876,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9704761f6297fe482276bee7f77a93cb42bd541c2bd6c1c560b6f3a9ece672e" dependencies = [ "alloy-pubsub", - "alloy-transport", + "alloy-transport 0.3.6", "futures", "http 1.1.0", "rustls", @@ -944,7 +1208,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "shlex", "syn 2.0.77", ] @@ -1074,10 +1338,14 @@ name = "challenger" version = "0.1.0" dependencies = [ "alloy", + "alloy-contract 0.4.2", + "alloy-rlp", + "alloy-signer 0.4.2", "chrono", "clap", "env_logger", "eyre", + "futures-util", "log", "metrics-exporter-prometheus", "metrics-process", @@ -1274,7 +1542,7 @@ checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" dependencies = [ "cfg-if", "crossbeam-utils", - "hashbrown", + "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core", @@ -1525,6 +1793,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + [[package]] name = "foreign-types" version = "0.3.2" @@ -1572,9 +1846,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -1582,9 +1856,9 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" @@ -1599,15 +1873,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -1616,21 +1890,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -1744,6 +2018,16 @@ dependencies = [ "serde", ] +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +dependencies = [ + "foldhash", + "serde", +] + [[package]] name = "headers" version = "0.3.9" @@ -2025,7 +2309,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.14.5", + "serde", ] [[package]] @@ -2211,7 +2496,7 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" dependencies = [ - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -2283,7 +2568,7 @@ checksum = "8b07a5eb561b8cbc16be2d216faf7757f9baf3bfb94dbb0fae3df8387a5bb47f" dependencies = [ "crossbeam-epoch", "crossbeam-utils", - "hashbrown", + "hashbrown 0.14.5", "metrics", "num_cpus", "quanta", @@ -2789,6 +3074,7 @@ dependencies = [ "libc", "rand_chacha", "rand_core", + "serde", ] [[package]] @@ -3018,6 +3304,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + [[package]] name = "rustc-hex" version = "2.1.0" @@ -3465,9 +3757,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.8.3" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b95156f8b577cb59dc0b1df15c6f29a10afc5f8a7ac9786b0b5c68c19149278" +checksum = "20e7b52ad118b2153644eea95c6fc740b6c1555b2344fdab763fc9de4075f665" dependencies = [ "paste", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index f51eee8..3df6539 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ eyre = "0.6.8" tokio = { version = "1", features = ["full"] } tokio-util = "0.7.12" -alloy = { version = "0.3.6", features = ["full", "signer-keystore"] } +alloy = { version = "0.3.6", features = ["full", "signer-keystore", "node-bindings"] } warp = "0.3" tower = { version = "0.5.1", features = ["retry"] } @@ -45,3 +45,7 @@ metrics-exporter-prometheus = { workspace = true } clap = { version = "4.3.21", features = ["derive", "env"] } rpassword = "7.2.0" chrono = "0.4.38" +alloy-contract = "0.4.2" +alloy-rlp = "0.3.8" +alloy-signer = "0.4.2" +futures-util = "0.3.31" diff --git a/crates/scribe/src/event_handler.rs b/crates/scribe/src/event_handler.rs index 0080e62..cebd9c6 100644 --- a/crates/scribe/src/event_handler.rs +++ b/crates/scribe/src/event_handler.rs @@ -186,9 +186,15 @@ impl EventHandler { timestamp } }; + + // let latest_block_number = self.provider.get_block_number().await?; + // let latest_block = self.provider.get_block(latest_block_number.into(), BlockTransactionsKind::Hashes).await.unwrap(); + // let current_timestamp = latest_block.unwrap().header.timestamp; let current_timestamp = chrono::Utc::now().timestamp() as u64; log::debug!("[{:?}] OpPoked, event_timestamp: {:?}, current_timestamp: {:?}", self.scribe_address, event_timestamp, current_timestamp); + println!("OpPoked, event_timestamp: {:?}, current_timestamp: {:?}", + event_timestamp, current_timestamp); if current_timestamp - event_timestamp > self.challenge_period.unwrap() { log::debug!( @@ -220,12 +226,10 @@ impl EventHandler { if self.challenge_period.is_some() { return Ok(()); } - let period = ScribeOptimisticProviderInstance::new( self.scribe_address, self.provider.clone() ).get_challenge_period().await?; - self.challenge_period = Some(period as u64); Ok(()) } @@ -380,3 +384,10 @@ impl ChallengeHandler { Ok(()) } } + +// need to make a mock provider to test this +// provider needs to be able to respond to block time +// provider needs to be able to challenge request +// need to be able to make provider fail challenge request + +// test diff --git a/crates/scribe/src/events_listener.rs b/crates/scribe/src/events_listener.rs index 52705aa..23bce45 100644 --- a/crates/scribe/src/events_listener.rs +++ b/crates/scribe/src/events_listener.rs @@ -44,7 +44,7 @@ use crate::{ metrics, }; -const POLL_INTERVAL_SEC: u64 = 30; +// const POLL_INTERVAL_SEC: u64 = 30; const MAX_ADDRESS_PER_REQUEST: usize = 50; /// The provider type used to interact with the Ethereum network. @@ -74,6 +74,7 @@ pub struct Poller { provider: Arc, last_processes_block: Option, tx: Sender, + poll_interval_seconds: u64, } impl Poller { @@ -82,6 +83,7 @@ impl Poller { cancelation_token: CancellationToken, provider: Arc, tx: Sender, + poll_interval_seconds: u64, ) -> Self { Self { addresses, @@ -89,6 +91,7 @@ impl Poller { provider, tx, last_processes_block: None, + poll_interval_seconds, } } @@ -118,11 +121,11 @@ impl Poller { // Poll for new events in block range `self.last_processes_block..latest_block` async fn poll(&mut self) -> Result<()> { log::trace!("Polling for new events"); - + println!("Polling for new events"); // Get latest block number let latest_block = self.provider.get_block_number().await.unwrap(); if None == self.last_processes_block { - self.last_processes_block = Some(latest_block - 100); + self.last_processes_block = Some(latest_block); } // TODO remove this line if latest_block <= self.last_processes_block.unwrap_or(0) { @@ -133,7 +136,7 @@ impl Poller { ); return Ok(()); } - + println!("Checking blocks from {:?} to {:?}", self.last_processes_block, latest_block); // Split addresses into chunks of MAX_ADDRESS_PER_REQUEST to optimize amount of requests for chunk in self.addresses.chunks(MAX_ADDRESS_PER_REQUEST) { let logs = self @@ -147,11 +150,12 @@ impl Poller { match logs { Ok(logs) => { log::debug!("[{:?}] Received {} logs", chunk, logs.len()); - + println!("Received {} logs", logs.len()); for log in logs { match EventWithMetadata::from_log(log) { Ok(event) => { log::debug!("[{:?}] Event received: {:?}", chunk, &event); + println!("Event received: {:?}", &event); // Send event to the channel self.tx.send(event).await?; } @@ -188,8 +192,9 @@ impl Poller { log::info!("Challenger cancelled"); return Ok(()); } - _ = tokio::time::sleep(Duration::from_secs(POLL_INTERVAL_SEC)) => { + _ = tokio::time::sleep(Duration::from_secs(self.poll_interval_seconds)) => { log::info!("Executing tick for events listener..."); + println!("Executing tick for events listener..."); self.poll().await?; } } diff --git a/src/main.rs b/src/main.rs index 3dc38ac..e1ae6dc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -211,6 +211,7 @@ async fn main() -> Result<()> { cancel_token.clone(), provider.clone(), tx.clone(), + 30, ); // Create event distributor @@ -303,6 +304,7 @@ mod tests { raw_password: None, password_file: None, rpc_url: "http://localhost:8545".to_string(), + flashbot_rpc_url: "http://localhost:8545".to_string(), }; let wallet = cli.wallet().unwrap().unwrap(); @@ -323,6 +325,7 @@ mod tests { raw_password: None, password_file: None, rpc_url: "http://localhost:8545".to_string(), + flashbot_rpc_url: "http://localhost:8545".to_string(), }; let wallet = cli.wallet().unwrap().unwrap(); @@ -350,6 +353,7 @@ mod tests { raw_password: None, password_file: Some(keystore_password_file), rpc_url: "http://localhost:8545".to_string(), + flashbot_rpc_url: "http://localhost:8545".to_string(), }; let wallet = cli.wallet().unwrap().unwrap(); @@ -374,6 +378,7 @@ mod tests { raw_password: Some("keystorepassword".to_string()), password_file: None, rpc_url: "http://localhost:8545".to_string(), + flashbot_rpc_url: "http://localhost:8545".to_string(), }; let wallet = cli.wallet().unwrap().unwrap(); @@ -383,4 +388,256 @@ mod tests { address!("ec554aeafe75601aaab43bd4621a22284db566c2") ); } + + + +} + + + +#[cfg(test)] +mod integration_tests { + use std::sync::Arc; + + use alloy::hex; + use alloy::network::EthereumWallet; + use alloy::providers::ext::AnvilApi; + use alloy::providers::fillers::ChainIdFiller; + use alloy::providers::{ Provider, ProviderBuilder }; + use alloy::rpc::client::ClientBuilder; + use alloy::signers::local::PrivateKeySigner; + use alloy::signers::SignerSync; + use alloy::transports::layers::RetryBackoffLayer; + use alloy::{ primitives::U256, sol }; + use alloy::node_bindings::Anvil; + use alloy::primitives::FixedBytes; + use scribe::contract::EventWithMetadata; + use scribe::event_handler; + use scribe::events_listener::Poller; + use tokio::task::JoinSet; + use tokio_util::sync::CancellationToken; + + sol!( + #[allow(missing_docs)] + #[sol(rpc)] + #[derive(Debug)] + ScribeOptimisitic, + "tests/fixtures/bytecode/ScribeOptimistic.json" + ); + + // type ConstructorArgs = sol!(uint256, address); + + #[tokio::test] + async fn challenge() { + + // ------------------------------------------------------------------------------------------------------------ + // create anvil instance + // create provider + // create scribe instance + // set bar + // create lift validator args + // lift validator + + let anvil: alloy::node_bindings::AnvilInstance = Anvil::new().port(8545 as u16).chain_id(31337).block_time_f64(0.1).try_spawn().expect("Failed to spawn anvil"); + // random private key + let private_key = "d4cf162c2e26ff75095922ea108d516ff07bdd732f050e64ced632980f11320b"; + let signer = EthereumWallet::new(private_key.parse::().unwrap()); + + let anvil_provider = Arc::new( + ProviderBuilder::new() + .with_recommended_fillers() + .wallet(signer.clone()) + .filler(ChainIdFiller::new(Some(31337))) + .on_http(anvil.endpoint_url()) + ); + + // set initial balance for deployer + anvil_provider.anvil_set_balance( + signer.default_signer().address(), + U256::from_str_radix( + "1000000000000000000000000000000000000000", 10 + ).unwrap() + ).await.expect("Unable to set balance"); + + // configure anvil settings + anvil_provider.anvil_set_auto_mine(true).await.expect("Failed to set auto mine"); + anvil_provider.anvil_mine(Some(U256::from(3)), Some(U256::from(3))).await.expect("Failed to mine"); + anvil_provider.anvil_impersonate_account(signer.default_signer().address().clone()).await.expect("Failed to impersonate account"); + + let initial_authed = signer.default_signer().address(); + let wat: [u8; 32] = hex!(" + 0123456789abcdef0123456789abcdef + 0123456789abcdef0123456789abcdef + "); + + // set to a low current time for now, this avoids having stale poke error later + anvil_provider.anvil_set_time(1000).await.expect("Failed to set time"); + + // deploy scribe instance + let scribe_optimisitic = ScribeOptimisitic + ::deploy(anvil_provider.clone(), initial_authed.clone(), FixedBytes(wat).clone()); + let scribe_optimisitic = scribe_optimisitic.await.unwrap(); + + let receipt = scribe_optimisitic.setBar(1); + let receipt = receipt.send().await.expect("Failed to set bar"); + receipt.watch().await.expect("Failed to set bar"); + + // lift validator + let pub_key = LibSecp256k1::Point { + x: U256::from_str_radix( + "95726579611468854699048782904089382286224374897874075137780214269565012360365", 10).unwrap(), + y: U256::from_str_radix( + "95517337328947037046967076057450300670379811052080651187456799621439597083272",10).unwrap(), + }; + let ecdsa_data = IScribe::ECDSAData { + v: 0x1b, + r: FixedBytes(hex!("0ced9fd231ad454eaac301d6e15a56b6aaa839a55d664757e3ace927e95948ec")), + s: FixedBytes(hex!("21b2813ad85945f320d7728fbfc9b83cbbb564135e67c16db16d5f4e74392119")), + }; + let receipt = scribe_optimisitic.lift_0(pub_key, ecdsa_data); + let receipt = receipt.send().await.expect("Failed to lift validator"); + receipt.watch().await.expect("Failed to lift validator"); + + // set challenge period + let receipt = scribe_optimisitic.setOpChallengePeriod(300); + let receipt = receipt.send().await.expect("Failed to lift validator"); + receipt.watch().await.expect("Failed to set opChallenge"); + + // deposit some balance for challenge testing + anvil_provider.anvil_set_balance( + scribe_optimisitic.address().clone(), + U256::from_str_radix( + "1000000000000000000000000000000000000000", 10 + ).unwrap() + ).await.expect("Unable to set balance"); + let current_timestamp = chrono::Utc::now().timestamp() as u64 - 100; + anvil_provider.anvil_set_time(current_timestamp).await.expect("Failed to set time"); + + + // ------------------------------------------------------------------------------------------------------------ + // create clients + // create poller + // create handler + + let client = ClientBuilder::default() + .layer(RetryBackoffLayer::new(15, 200, 300)) + .http(anvil.endpoint_url()); + + let provider = Arc::new( + ProviderBuilder::new() + .with_recommended_fillers() + .filler(ChainIdFiller::new(Some(31337))) + .wallet(signer.clone()) + .on_client(client), + ); + + let flashbot_provider = provider.clone(); + + let mut set = JoinSet::new(); + let cancel_token = CancellationToken::new(); + + let (tx, rx) = tokio::sync::mpsc::channel::(100); + + let addresses = vec![scribe_optimisitic.address().clone()]; + + // Create events listener + let mut poller = Poller::new( + addresses.clone(), + cancel_token.clone(), + provider.clone(), + tx.clone(), + 1, + ); + + // Create event distributor + let mut event_distributor = event_handler::EventDistributor::new( + addresses.clone(), + cancel_token.clone(), + provider.clone(), + flashbot_provider.clone(), + rx, + ); + + // Run events listener process + set.spawn(async move { + log::info!("Starting events listener"); + if let Err(err) = poller.start().await { + log::error!("Poller error: {:?}", err); + } + }); + + // Run event distributor process + set.spawn(async move { + log::info!("Starting log handler"); + if let Err(err) = event_distributor.start().await { + log::error!("Log Handler error: {:?}", err); + } + }); + + // Sleep for 1 second to allow event listener to set the current block number after first poll + tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + + // Increase current block count to move away from poller intialisation block + anvil_provider.anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await.expect("Failed to mine"); + + // ------------------------------------------------------------------------------------------------------------ + // create a bad poke + // wait for certain time period + // check that the challenge was made + + // Construct Poke and Schnorr data and ECDSA Sig + let poke_data: IScribe::PokeData = IScribe::PokeData { + val: 10, + age: (current_timestamp - 1) as u32, + }; + let schnorr_data = IScribe::SchnorrData { + signature: FixedBytes(hex!("0000000000000000000000000000000000000000000000000000000000000000")), + commitment: alloy::primitives::Address::ZERO.clone(), + feedIds: hex!("00").into() + }; + let op_poke_message = scribe_optimisitic.constructOpPokeMessage(poke_data.clone(), schnorr_data.clone()).call().await.expect("Failed to read current age"); + let op_poke_message = op_poke_message._0; + let ecdsa_signer = "d4cf162c2e26ff75095922ea108d516ff07bdd732f050e64ced632980f11320b" + .parse::().expect("Failed to parse private key"); + let signature = ecdsa_signer.sign_hash_sync(&op_poke_message).expect("Failed to sign message"); + let ecdsa_data = IScribe::ECDSAData { + v: 0x1b + (signature.v().to_u64() as u8), + r: FixedBytes(signature.r().to_be_bytes()), + s: FixedBytes(signature.s().to_be_bytes()), + }; + + // Assert that teh current contract balance is not 0 + let balance = anvil_provider.get_balance(scribe_optimisitic.address().clone()).await.expect("Failed to get balance"); + assert_ne!(balance, U256::from(0)); + + // Make the invalid poke + let receipt = scribe_optimisitic.opPoke(poke_data.clone(), schnorr_data.clone(), ecdsa_data); + let receipt = receipt.send().await.expect("Failed to send op poke"); + receipt.watch().await.expect("Failed to watch op poke"); + anvil_provider.anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await.expect("Failed to mine"); + + // Poll the contract for up to 5 seconds to ensure that the challenge was successfull by querying balance + for _ in 1..10 { + let balance = anvil_provider.get_balance(scribe_optimisitic.address().clone()).await.expect("Failed to get balance"); + println!("Balance {:?} ", balance); + if balance == U256::from(0) { + return; + } + anvil_provider.anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await.expect("Failed to mine"); + tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; + } + + } + + // test multiple scribes + + // test dont challenge if outside challenge period + + // test dont challenge if receive op challenged + + // test dont challenge if received new op poke + + // test flashbotnused first + + // test fallback to normal rpc } diff --git a/tests/fixtures/bytecode/ScribeOptimistic.json b/tests/fixtures/bytecode/ScribeOptimistic.json new file mode 100644 index 0000000..8fa1208 --- /dev/null +++ b/tests/fixtures/bytecode/ScribeOptimistic.json @@ -0,0 +1,3815 @@ +{ + "abi": [ + { + "type": "constructor", + "inputs": [ + { + "name": "initialAuthed", + "type": "address", + "internalType": "address" + }, + { + "name": "wat_", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "stateMutability": "payable" + }, + { + "type": "receive", + "stateMutability": "payable" + }, + { + "type": "function", + "name": "authed", + "inputs": [ + { + "name": "who", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "authed", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address[]", + "internalType": "address[]" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "bar", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint8", + "internalType": "uint8" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "bud", + "inputs": [ + { + "name": "who", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "challengeReward", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "constructOpPokeMessage", + "inputs": [ + { + "name": "pokeData", + "type": "tuple", + "internalType": "struct IScribe.PokeData", + "components": [ + { + "name": "val", + "type": "uint128", + "internalType": "uint128" + }, + { + "name": "age", + "type": "uint32", + "internalType": "uint32" + } + ] + }, + { + "name": "schnorrData", + "type": "tuple", + "internalType": "struct IScribe.SchnorrData", + "components": [ + { + "name": "signature", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "commitment", + "type": "address", + "internalType": "address" + }, + { + "name": "feedIds", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "outputs": [ + { + "name": "", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "constructPokeMessage", + "inputs": [ + { + "name": "pokeData", + "type": "tuple", + "internalType": "struct IScribe.PokeData", + "components": [ + { + "name": "val", + "type": "uint128", + "internalType": "uint128" + }, + { + "name": "age", + "type": "uint32", + "internalType": "uint32" + } + ] + } + ], + "outputs": [ + { + "name": "", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "decimals", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint8", + "internalType": "uint8" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "deny", + "inputs": [ + { + "name": "who", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "diss", + "inputs": [ + { + "name": "who", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "drop", + "inputs": [ + { + "name": "feedIds", + "type": "uint8[]", + "internalType": "uint8[]" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "drop", + "inputs": [ + { + "name": "feedId", + "type": "uint8", + "internalType": "uint8" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "feedRegistrationMessage", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "feeds", + "inputs": [ + { + "name": "who", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "feeds", + "inputs": [ + { + "name": "feedId", + "type": "uint8", + "internalType": "uint8" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + }, + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "feeds", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address[]", + "internalType": "address[]" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "isAcceptableSchnorrSignatureNow", + "inputs": [ + { + "name": "message", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "schnorrData", + "type": "tuple", + "internalType": "struct IScribe.SchnorrData", + "components": [ + { + "name": "signature", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "commitment", + "type": "address", + "internalType": "address" + }, + { + "name": "feedIds", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "kiss", + "inputs": [ + { + "name": "who", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "latestAnswer", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "int256", + "internalType": "int256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "latestRoundData", + "inputs": [], + "outputs": [ + { + "name": "roundId", + "type": "uint80", + "internalType": "uint80" + }, + { + "name": "answer", + "type": "int256", + "internalType": "int256" + }, + { + "name": "startedAt", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "updatedAt", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "answeredInRound", + "type": "uint80", + "internalType": "uint80" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "lift", + "inputs": [ + { + "name": "pubKey", + "type": "tuple", + "internalType": "struct LibSecp256k1.Point", + "components": [ + { + "name": "x", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "y", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "ecdsaData", + "type": "tuple", + "internalType": "struct IScribe.ECDSAData", + "components": [ + { + "name": "v", + "type": "uint8", + "internalType": "uint8" + }, + { + "name": "r", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "s", + "type": "bytes32", + "internalType": "bytes32" + } + ] + } + ], + "outputs": [ + { + "name": "", + "type": "uint8", + "internalType": "uint8" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "lift", + "inputs": [ + { + "name": "pubKeys", + "type": "tuple[]", + "internalType": "struct LibSecp256k1.Point[]", + "components": [ + { + "name": "x", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "y", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "ecdsaDatas", + "type": "tuple[]", + "internalType": "struct IScribe.ECDSAData[]", + "components": [ + { + "name": "v", + "type": "uint8", + "internalType": "uint8" + }, + { + "name": "r", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "s", + "type": "bytes32", + "internalType": "bytes32" + } + ] + } + ], + "outputs": [ + { + "name": "", + "type": "uint8[]", + "internalType": "uint8[]" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "maxChallengeReward", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "opChallenge", + "inputs": [ + { + "name": "schnorrData", + "type": "tuple", + "internalType": "struct IScribe.SchnorrData", + "components": [ + { + "name": "signature", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "commitment", + "type": "address", + "internalType": "address" + }, + { + "name": "feedIds", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "opChallengePeriod", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint16", + "internalType": "uint16" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "opFeedId", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint8", + "internalType": "uint8" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "opPoke", + "inputs": [ + { + "name": "pokeData", + "type": "tuple", + "internalType": "struct IScribe.PokeData", + "components": [ + { + "name": "val", + "type": "uint128", + "internalType": "uint128" + }, + { + "name": "age", + "type": "uint32", + "internalType": "uint32" + } + ] + }, + { + "name": "schnorrData", + "type": "tuple", + "internalType": "struct IScribe.SchnorrData", + "components": [ + { + "name": "signature", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "commitment", + "type": "address", + "internalType": "address" + }, + { + "name": "feedIds", + "type": "bytes", + "internalType": "bytes" + } + ] + }, + { + "name": "ecdsaData", + "type": "tuple", + "internalType": "struct IScribe.ECDSAData", + "components": [ + { + "name": "v", + "type": "uint8", + "internalType": "uint8" + }, + { + "name": "r", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "s", + "type": "bytes32", + "internalType": "bytes32" + } + ] + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "opPoke_optimized_397084999", + "inputs": [ + { + "name": "pokeData", + "type": "tuple", + "internalType": "struct IScribe.PokeData", + "components": [ + { + "name": "val", + "type": "uint128", + "internalType": "uint128" + }, + { + "name": "age", + "type": "uint32", + "internalType": "uint32" + } + ] + }, + { + "name": "schnorrData", + "type": "tuple", + "internalType": "struct IScribe.SchnorrData", + "components": [ + { + "name": "signature", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "commitment", + "type": "address", + "internalType": "address" + }, + { + "name": "feedIds", + "type": "bytes", + "internalType": "bytes" + } + ] + }, + { + "name": "ecdsaData", + "type": "tuple", + "internalType": "struct IScribe.ECDSAData", + "components": [ + { + "name": "v", + "type": "uint8", + "internalType": "uint8" + }, + { + "name": "r", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "s", + "type": "bytes32", + "internalType": "bytes32" + } + ] + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "peek", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "peep", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "poke", + "inputs": [ + { + "name": "pokeData", + "type": "tuple", + "internalType": "struct IScribe.PokeData", + "components": [ + { + "name": "val", + "type": "uint128", + "internalType": "uint128" + }, + { + "name": "age", + "type": "uint32", + "internalType": "uint32" + } + ] + }, + { + "name": "schnorrData", + "type": "tuple", + "internalType": "struct IScribe.SchnorrData", + "components": [ + { + "name": "signature", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "commitment", + "type": "address", + "internalType": "address" + }, + { + "name": "feedIds", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "poke_optimized_7136211", + "inputs": [ + { + "name": "pokeData", + "type": "tuple", + "internalType": "struct IScribe.PokeData", + "components": [ + { + "name": "val", + "type": "uint128", + "internalType": "uint128" + }, + { + "name": "age", + "type": "uint32", + "internalType": "uint32" + } + ] + }, + { + "name": "schnorrData", + "type": "tuple", + "internalType": "struct IScribe.SchnorrData", + "components": [ + { + "name": "signature", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "commitment", + "type": "address", + "internalType": "address" + }, + { + "name": "feedIds", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "read", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "readWithAge", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "rely", + "inputs": [ + { + "name": "who", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setBar", + "inputs": [ + { + "name": "bar_", + "type": "uint8", + "internalType": "uint8" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setMaxChallengeReward", + "inputs": [ + { + "name": "maxChallengeReward_", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setOpChallengePeriod", + "inputs": [ + { + "name": "opChallengePeriod_", + "type": "uint16", + "internalType": "uint16" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "tolled", + "inputs": [ + { + "name": "who", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "tolled", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address[]", + "internalType": "address[]" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "tryRead", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + }, + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "tryReadWithAge", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + }, + { + "name": "", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "wards", + "inputs": [ + { + "name": "who", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "wat", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "stateMutability": "view" + }, + { + "type": "event", + "name": "AuthGranted", + "inputs": [ + { + "name": "caller", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "who", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "AuthRenounced", + "inputs": [ + { + "name": "caller", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "who", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "BarUpdated", + "inputs": [ + { + "name": "caller", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "oldBar", + "type": "uint8", + "indexed": false, + "internalType": "uint8" + }, + { + "name": "newBar", + "type": "uint8", + "indexed": false, + "internalType": "uint8" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "FeedDropped", + "inputs": [ + { + "name": "caller", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "feed", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "FeedLifted", + "inputs": [ + { + "name": "caller", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "feed", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "MaxChallengeRewardUpdated", + "inputs": [ + { + "name": "caller", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "oldMaxChallengeReward", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "newMaxChallengeReward", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OpChallengePeriodUpdated", + "inputs": [ + { + "name": "caller", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "oldOpChallengePeriod", + "type": "uint16", + "indexed": false, + "internalType": "uint16" + }, + { + "name": "newOpChallengePeriod", + "type": "uint16", + "indexed": false, + "internalType": "uint16" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OpChallengeRewardPaid", + "inputs": [ + { + "name": "challenger", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "schnorrData", + "type": "tuple", + "indexed": false, + "internalType": "struct IScribe.SchnorrData", + "components": [ + { + "name": "signature", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "commitment", + "type": "address", + "internalType": "address" + }, + { + "name": "feedIds", + "type": "bytes", + "internalType": "bytes" + } + ] + }, + { + "name": "reward", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OpPokeChallengedSuccessfully", + "inputs": [ + { + "name": "caller", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "schnorrData", + "type": "tuple", + "indexed": false, + "internalType": "struct IScribe.SchnorrData", + "components": [ + { + "name": "signature", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "commitment", + "type": "address", + "internalType": "address" + }, + { + "name": "feedIds", + "type": "bytes", + "internalType": "bytes" + } + ] + }, + { + "name": "schnorrErr", + "type": "bytes", + "indexed": false, + "internalType": "bytes" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OpPokeChallengedUnsuccessfully", + "inputs": [ + { + "name": "caller", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "schnorrData", + "type": "tuple", + "indexed": false, + "internalType": "struct IScribe.SchnorrData", + "components": [ + { + "name": "signature", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "commitment", + "type": "address", + "internalType": "address" + }, + { + "name": "feedIds", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OpPokeDataDropped", + "inputs": [ + { + "name": "caller", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "pokeData", + "type": "tuple", + "indexed": false, + "internalType": "struct IScribe.PokeData", + "components": [ + { + "name": "val", + "type": "uint128", + "internalType": "uint128" + }, + { + "name": "age", + "type": "uint32", + "internalType": "uint32" + } + ] + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OpPoked", + "inputs": [ + { + "name": "caller", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "opFeed", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "schnorrData", + "type": "tuple", + "indexed": false, + "internalType": "struct IScribe.SchnorrData", + "components": [ + { + "name": "signature", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "commitment", + "type": "address", + "internalType": "address" + }, + { + "name": "feedIds", + "type": "bytes", + "internalType": "bytes" + } + ] + }, + { + "name": "pokeData", + "type": "tuple", + "indexed": false, + "internalType": "struct IScribe.PokeData", + "components": [ + { + "name": "val", + "type": "uint128", + "internalType": "uint128" + }, + { + "name": "age", + "type": "uint32", + "internalType": "uint32" + } + ] + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Poked", + "inputs": [ + { + "name": "caller", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "val", + "type": "uint128", + "indexed": false, + "internalType": "uint128" + }, + { + "name": "age", + "type": "uint32", + "indexed": false, + "internalType": "uint32" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "TollGranted", + "inputs": [ + { + "name": "caller", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "who", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "TollRenounced", + "inputs": [ + { + "name": "caller", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "who", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "BarNotReached", + "inputs": [ + { + "name": "numberSigners", + "type": "uint8", + "internalType": "uint8" + }, + { + "name": "bar", + "type": "uint8", + "internalType": "uint8" + } + ] + }, + { + "type": "error", + "name": "DoubleSigningAttempted", + "inputs": [ + { + "name": "feedId", + "type": "uint8", + "internalType": "uint8" + } + ] + }, + { + "type": "error", + "name": "FutureMessage", + "inputs": [ + { + "name": "givenAge", + "type": "uint32", + "internalType": "uint32" + }, + { + "name": "currentTimestamp", + "type": "uint32", + "internalType": "uint32" + } + ] + }, + { + "type": "error", + "name": "InChallengePeriod", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidFeedId", + "inputs": [ + { + "name": "feedId", + "type": "uint8", + "internalType": "uint8" + } + ] + }, + { + "type": "error", + "name": "NoOpPokeToChallenge", + "inputs": [] + }, + { + "type": "error", + "name": "NotAuthorized", + "inputs": [ + { + "name": "caller", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "NotTolled", + "inputs": [ + { + "name": "caller", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "SchnorrDataMismatch", + "inputs": [ + { + "name": "gotHash", + "type": "uint160", + "internalType": "uint160" + }, + { + "name": "wantHash", + "type": "uint160", + "internalType": "uint160" + } + ] + }, + { + "type": "error", + "name": "SchnorrSignatureInvalid", + "inputs": [] + }, + { + "type": "error", + "name": "SignerNotFeed", + "inputs": [ + { + "name": "signer", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "StaleMessage", + "inputs": [ + { + "name": "givenAge", + "type": "uint32", + "internalType": "uint32" + }, + { + "name": "currentAge", + "type": "uint32", + "internalType": "uint32" + } + ] + } + ], + "bytecode": { + "object": "0x620037a560a0601f38839003908101601f19168201906001600160401b03821183831017620002465780839160409586948552833981010312620002415780516001600160a01b0381169081900362000241576020809201516000828152808452600185822055600154680100000000000000008110156200022d5760018101806001558110156200021957600182528482200180546001600160a01b03191684179055845192817fe31c10b0adbedd0c6e5d024286c6eeead7761e65a67608dcf0b67604c0da7e2f8186a3811562000216575060805261020590815460ff81169160028303620001d7575b505050620000f86200025c565b805461ffff8160081c16906104b09182810362000191575b505050506200011e6200025c565b6102078054600019926001820162000157575b845161340890816200039d8239608051818181611714015281816125d90152612ec90152f35b839085519283528201527ffccefdc521d919e1c7e09025203c5542ec6e99eb409c750d4fc386cdb7feee37843392a2553880808062000131565b6204b000928651918252858201527f06eaf85dccf95ffb2040720d23d296ebf45c6b193a5f373a553300fa640a1118863392a262ffff0019161790553880808062000110565b600292815282858201527f95623b9931156d6d5cb43881a13f223ae416fb199e5edf776efb38766f38cbea863392a260ff1916178155388080620000eb565b80fd5b634e487b7160e01b82526032600452602482fd5b634e487b7160e01b82526041600452602482fd5b600080fd5b634e487b7160e01b600052604160045260246000fd5b303b156200039a57604080519081016001600160401b03811182821017620002465760405261020680546001600160801b03808216845260809190911c63ffffffff908116602085018181526102055460081c61ffff16909101949282861162000384576000958380421691161180158062000370575b62000345575b62000306575b505050919055506004805463ffffffff60801b19164260801b63ffffffff60801b16179055565b6040519351168352511660208201527f20246a67cf0cfc89415e0ea0b3293fcc5138acc3c3b2111012dd06eb742b899460403392a238808080620002df565b818551166004549063ffffffff60801b855160801b169160018060a01b0319161717600455620002d9565b50838351168460045460801c1610620002d3565b634e487b7160e01b600052601160045260246000fd5b56fe608080604052600436101561001d575b50361561001b57600080fd5b005b60003560e01c908115611ab757508060821461189b5780630760861b14611a045780630e5a6c70146115ef5780630fce3415146119ba57806310b07b7114611969578063224242ca146118a95780632f529d731461189b5780632fba4aa914611818578063313ce567146117fc578063393e5ede1461179c5780633bee58f9146117525780633ea0c15e146117375780634ca29923146116fc5780634fce7a2a146116b557806350d25bcd1461166b57806357de26a41461161a57806359e02dd7146115ef578063646edb68146115c957806365c4ce7a146115a857806365fae35e146115725780636712af9e14611068578063789d819114610fc65780638928a1f814610c145780638b0b044c14610bfb5780639954b0dc14610b465780639c52a7f114610b105780639dadc88614610a4d5780639fd001f614610a2e578063ab06ee16146109c3578063acf40b6f14610968578063b259da5c1461088f578063b9ee3fc11461086a578063bf353dbb14610823578063bfe5861f14610602578063c83c6334146105cb578063ceed3ef214610544578063d0a5882a14610521578063d63605b8146103ea578063dac42ad81461039d578063f29c29c41461037c578063fe663495146102b0578063feaf968c1461022e5763febb0f7e14610207575b3861000f565b3461022957600060031936011261022957602060ff6102055416604051908152f35b600080fd5b34610229576000600319360112610229573360005260026020526040600020541561029e5760a061025d612f5b565b63ffffffff60206fffffffffffffffffffffffffffffffff835116920151166040519160018352602083015260006040830152606082015260016080820152f35b63d957b595600052336020526024601cfd5b34610229576020600319360112610229576102c96120d9565b3360005260006020526040600020541561036a5760ff811680156102295761020591825460ff8116838103610301575b61001b613091565b6040805160ff92831681529390911660208401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009233917f95623b9931156d6d5cb43881a13f223ae416fb199e5edf776efb38766f38cbea91a21617905580808080806102f9565b634a0bfec1600052336020526024601cfd5b346102295760206003193601126102295761001b6103986120f7565b61244c565b3461022957600319604081360112610229576024359067ffffffffffffffff8211610229576060908236030112610229576103df602091600401600435612708565b506040519015158152f35b3461022957600060031936011261022957604051612020810181811067ffffffffffffffff8211176104f25760405261010090818152602061200036828401376000816040516104398161207e565b828152015260009060005b848110610460578284526040518061045c868261211a565b0390f35b8460ff821610156104c3576001906104806101fe82841b166005016126c9565b80518482015117610493575b5001610444565b9373ffffffffffffffffffffffffffffffffffffffff604084939620166104ba8288612438565b5201928661048c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b3461022957600060031936011261022957602061053c6121b1565b604051908152f35b34610229576000600319360112610229573360005260026020526040600020541561029e576060610573612f5b565b80516fffffffffffffffffffffffffffffffff9081169182156105b8579063ffffffff60208193015116906001935b6040519415158552166020840152166040820152f35b506000915063ffffffff908290816105a2565b34610229576020600319360112610229576105e46120d9565b3360005260006020526040600020541561036a5761001b9033613001565b346102295760406003193601126102295767ffffffffffffffff600435818111610229573660238201121561022957806004013561063f81612199565b9061064d60405192836120b6565b808252602092838301906024809360061b82010190368211610229578301915b8183106107f3575050508035938411610229573660238501121561022957836004013561069981612199565b946106a760405196876120b6565b81865284860183606080940283010191368311610229578401905b8282106107b357505050503360005260008352604060002054156107a45750805190835182036102295761070e6106f883612199565b9261070660405194856120b6565b808452612199565b92601f19818401940136853760005b825181101561075e578061074861073660019386612438565b51610741838a612438565b5190612b38565b60ff6107548388612438565b911690520161071d565b508284604051928392818401908285525180915260408401929160005b82811061078a57505050500390f35b835160ff168552869550938101939281019260010161077b565b634a0bfec1600052338352601cfd5b8382360312610229578684916040516107cb8161209a565b6107d4856120e9565b81528285013583820152604085013560408201528152019101906106c2565b6040833603126102295785604091825161080c8161207e565b85358152828601358382015281520192019161066d565b346102295760206003193601126102295773ffffffffffffffffffffffffffffffffffffffff6108516120f7565b1660005260006020526020604060002054604051908152f35b3461022957600060031936011261022957602060ff6102055460181c16604051908152f35b34610229576020806003193601126102295760043567ffffffffffffffff81116102295736602382011215610229578060040135906108cd82612199565b916108db60405193846120b6565b80835260248484019160051b8301019136831161022957602401905b828210610951578385336000526000815260406000205415610940575060005b815181101561001b578061093a60ff61093260019486612438565b511633613001565b01610917565b634a0bfec16000523390526024601cfd5b84809161095d846120e9565b8152019101906108f7565b34610229576040600319360112610229576040516109858161207e565b6004356fffffffffffffffffffffffffffffffff8116810361022957815260243563ffffffff81168103610229578161053c91602080940152612585565b34610229576020600319360112610229576004353360005260006020526040600020541561036a5761020780548281036109f957005b6040519081528260208201527ffccefdc521d919e1c7e09025203c5542ec6e99eb409c750d4fc386cdb7feee3760403392a255005b3461022957600060031936011261022957602061020754604051908152f35b346102295760206003193601126102295760043561ffff80821691828103610229573360005260006020526040600020541561036a57821561022957610205928354928360081c1691818303610aa55761001b613091565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ff9262ffff009260405191825260208201527f06eaf85dccf95ffb2040720d23d296ebf45c6b193a5f373a553300fa640a111860403392a260081b16911617905580808080806102f9565b3461022957602060031936011261022957610b296120f7565b3360005260006020526040600020541561036a5761001b9061234f565b34610229576000600319360112610229576003610b6381546123ab565b9060009160005b8151811015610bea5780610b80610bbb9261224c565b9073ffffffffffffffffffffffffffffffffffffffff9182915490871b1c166000526002602052600160406000205414610bc0575b506123dc565b610b6a565b610bc98261224c565b905490861b1c16610be3610bdc876123dc565b9685612438565b5285610bb5565b8382526040518061045c848261211a565b3461022957602061053c610c0e36611ee8565b90612e1c565b3461022957602060031981813601126102295767ffffffffffffffff906004358281116102295760608160040192823603011261022957610c53612ce5565b84810163ffffffff928382511695610c77610205549761ffff8960081c1690612d1b565b8580421691161115610f9c57610c9c6044610c94602485016126e7565b930187612678565b9290610cb36040519485928c8401948b3586612d33565b0392610cc7601f19948581018352826120b6565b51902073ffffffffffffffffffffffffffffffffffffffff90811690888a1c16808203610f65575050610d2f86610d2a6fffffffffffffffffffffffffffffffff8097511660405190610d198261207e565b8152888b60c01c168c820152612585565b612708565b91909788600014610e365750505050829051168260045460801c1610610d95575b50507f3f2cf79ad39c64280638c6fc485670c10ed4dad0a35ecd2857255ee7a130ba2060405184815280610d88339487830190612d74565b0390a25b60405190158152f35b610e2e600092610de2610206938454166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff000000000000000000000000000000006004541617600455565b825460801c167fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff73ffffffff000000000000000000000000000000006004549260801b16911617600455565b558380610d50565b90935060ff919550610e50929796945060181c1630613001565b610e586131eb565b906000808060405185335af1903d15610f5f573d9081116104f25760405190610e888989601f84011601836120b6565b81526000883d92013e5b610f19575b5084610eae60405192604084526040840190612d74565b948286038284015283519384875260005b858110610f06575050858391600084877f2984f6d7ff4df266745fc0283d83a02c5125069524c8eeb85887de137e8f890c98999a010152601f3397011601030190a2610d8c565b8181018401518882018501528301610ebf565b7f7e4d8c9da9421f78d986605928e77f0920f2c995d1cbba819c8915d2b209aa4b60405160408152610f4e6040820185612d74565b92888201528033930390a285610e97565b50610e92565b60449250604051917f73ceee3000000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b60046040517fcff75876000000000000000000000000000000000000000000000000000000008152fd5b3461022957602060031936011261022957610fdf6120d9565b6101008110156104c357610ff89060011b6005016126c9565b805160208201511761103057506040600073ffffffffffffffffffffffffffffffffffffffff60005b83519215158352166020820152f35b73ffffffffffffffffffffffffffffffffffffffff60408092201673ffffffffffffffffffffffffffffffffffffffff600191611021565b346102295761107636611e7e565b9160ff6110866040840184612678565b9050116115345763ffffffff9161109b612ce5565b83602082015116946110b9610205549661ffff8860081c1690612d1b565b854216958691161161150a5763ffffffff60208301511663ffffffff60045460801c168082116000146115025750945b6110f560208601612cb7565b63ffffffff808816911611156114b6578063ffffffff61111760208801612cb7565b161161146b57506111288385612e1c565b9060ff8135168135036102295760408051928352813560ff16602080850191909152808301358483015291013560608301526000808052909160809060015afa1561145f576000519373ffffffffffffffffffffffffffffffffffffffff948581169561010060ff8360981c1610156104c357869060406111b26101fe8560971c166005016126c9565b20160361142e577fb9dc937c5e394d0c8f76e0e324500b88251b4c909ddc56232df10e2ea42b3c639495967fffffffffffffffff000000000000000000000000000000000000000000ffffff63ff00000077ffffffffffffffffffffffffffffffffffffffff000000008761125161125f61122f602084016126e7565b61123c6040850185612678565b60409491945194859360208501973588612d33565b03601f1981018352826120b6565b51902060201b169360801c16911617176102055563ffffffff806020840151169116811461138a575b505063ffffffff61137c60206fffffffffffffffffffffffffffffffff95866112b082612cc8565b16610206908154907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff000000000000000000000000000000004260801b16921617179055611304828201612cb7565b7fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff7bffffffff000000000000000000000000000000000000000000000000610205549260c01b169116176102055561136760405195606087526060870190612d74565b966113718261216b565b168286015201612188565b1660408201528033930390a3005b6113e16fffffffffffffffffffffffffffffffff6114279351166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff000000000000000000000000000000006004541617600455565b7fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff73ffffffff000000000000000000000000000000006004549260801b16911617600455565b8480611288565b602486604051907f56d7d2e80000000000000000000000000000000000000000000000000000000082526004820152fd5b6040513d6000823e3d90fd5b61147760208601612cb7565b6040517feea80f5200000000000000000000000000000000000000000000000000000000815263ffffffff918216600482015291166024820152604490fd5b856114c360208701612cb7565b6040517f76f4b87800000000000000000000000000000000000000000000000000000000815263ffffffff918216600482015291166024820152604490fd5b9050946110e9565b60046040517f8855b9e8000000000000000000000000000000000000000000000000000000008152fd5b604460ff6102055416604051907fce818a2400000000000000000000000000000000000000000000000000000000825260ff60048301526024820152fd5b346102295760206003193601126102295761158b6120f7565b3360005260006020526040600020541561036a5761001b90612283565b346102295760206003193601126102295761001b6115c46120f7565b612508565b3461022957600060031936011261022957602061ffff6102055460081c16604051908152f35b3461022957600060031936011261022957611608612f25565b60408051928352901515602083015290f35b34610229576000600319360112610229573360005260026020526040600020541561029e576fffffffffffffffffffffffffffffffff611658612f5b565b5116801561022957602090604051908152f35b34610229576000600319360112610229573360005260026020526040600020541561029e5760206fffffffffffffffffffffffffffffffff6116ab612f5b565b5116604051908152f35b346102295760206003193601126102295773ffffffffffffffffffffffffffffffffffffffff6116e36120f7565b1660005260026020526020604060002054604051908152f35b346102295760006003193601126102295760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461022957600060031936011261022957602061053c6131eb565b346102295760206003193601126102295773ffffffffffffffffffffffffffffffffffffffff6117806120f7565b1660005260026020526020600160406000205414604051908152f35b34610229576000600319360112610229573360005260026020526040600020541561029e576117c9612f5b565b6fffffffffffffffffffffffffffffffff81511680156102295763ffffffff602060409301511682519182526020820152f35b3461022957600060031936011261022957602060405160128152f35b34610229576020600319360112610229576118316120f7565b61010060ff8260981c1610156104c357806118576101fe60209360971c166005016126c9565b8051838201511715159182611873575b50506040519015158152f35b909150604073ffffffffffffffffffffffffffffffffffffffff809216922016148280611867565b506118a4611f24565b610201565b346102295760006003193601126102295760016118c681546123ab565b60009182815b6118e2575b8383526040518061045c858261211a565b825181101561196457611934816118f98493612215565b73ffffffffffffffffffffffffffffffffffffffff809254600392831b1c166000526000602052846040600020541461193a575b50506123dc565b906118cc565b61194383612215565b9054911b1c1661195c611955886123dc565b9787612438565b52868061192d565b6118d1565b34610229576000600319360112610229573360005260026020526040600020541561029e5760406fffffffffffffffffffffffffffffffff6119a9612f5b565b511681519080151582526020820152f35b346102295760206003193601126102295773ffffffffffffffffffffffffffffffffffffffff6119e86120f7565b1660005260006020526020600160406000205414604051908152f35b3461022957600319360160a081126102295760401361022957604051611a298161207e565b6004358152602435602082015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126102295760405190611a6d8261209a565b60443560ff81168103610229578252606435602083015260843560408301523360005260006020526040600020541561036a57602091611aac91612b38565b60ff60405191168152f35b611ac036611e7e565b91604082019360ff611ad28685612678565b905011611e435750611ae2612ce5565b63ffffffff8060208301511661020595611b0687549261ffff8460081c1690612d1b565b834216938491161161150a5763ffffffff92836020860151168460045460801c16808211600014611e3c57505b611b3f60208801612cb7565b858216958691161115611e3057508063ffffffff611b5f60208901612cb7565b1611611e245750611b708686612e1c565b9060ff8135168135036102295760408051928352813560ff16602080850191909152808301358483015291013560608301526000808052909160809060015afa1561145f576000519073ffffffffffffffffffffffffffffffffffffffff978883169861010060ff8560981c1610156104c35789906040611bfa6101fe8760971c166005016126c9565b201603611df3579361137c936020938897937fffffffffffffffff000000000000000000000000000000000000000000ffffff63ff00000077ffffffffffffffffffffffffffffffffffffffff000000008b611251611ca57fb9dc937c5e394d0c8f76e0e324500b88251b4c909ddc56232df10e2ea42b3c639f9d611c9063ffffffff9f8e611c8991016126e7565b9185612678565b604094919451948f9386948501973588612d33565b519020891b169360801c16911617178955858483015116908114611d95575b50506fffffffffffffffffffffffffffffffff9687611ce283612cc8565b16610206908154907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff000000000000000000000000000000004260801b16921617179055611d36838301612cb7565b7fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff7bffffffff00000000000000000000000000000000000000000000000083549260c01b16911617905561136760405195606087526060870190612d74565b6113e16fffffffffffffffffffffffffffffffff611dec9351166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff000000000000000000000000000000006004541617600455565b8880611cc4565b602489604051907f56d7d2e80000000000000000000000000000000000000000000000000000000082526004820152fd5b61147760208701612cb7565b6114c360208801612cb7565b9050611b33565b60449060ff61020554167fce818a2400000000000000000000000000000000000000000000000000000000825260ff60048301526024820152fd5b6003199182820160c0811261022957604013610229576004926044359067ffffffffffffffff8211610229576060908285030112610229577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c606091850193011261022957606490565b906003198083016060811261022957604013610229576004926044359167ffffffffffffffff8311610229578260609203011261022957820190565b503461022957611f3336611ee8565b9063ffffffff90816020611f45612f5b565b0151169260208201938084611f5987612cb7565b161115612075575082611f6b85612cb7565b814216918291161161206c575060408236031261022957611fb390610d2a604051611f958161207e565b611f9e8561216b565b8152611fa987612188565b6020820152612585565b9015612064575061202e6120286fffffffffffffffffffffffffffffffff9283611fdc82612cc8565b16600454907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff000000000000000000000000000000004260801b1692161717600455612cc8565b93612cb7565b90604051931683521660208201527f7045db5134e0c7ca4bff0c0e096616ef4ebb36b719cd00c5f0ba4f4475ecceee60403392a2005b602081519101fd5b61147785612cb7565b6114c385612cb7565b6040810190811067ffffffffffffffff8211176104f257604052565b6060810190811067ffffffffffffffff8211176104f257604052565b90601f601f19910116810190811067ffffffffffffffff8211176104f257604052565b6004359060ff8216820361022957565b359060ff8216820361022957565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361022957565b6020908160408183019282815285518094520193019160005b828110612141575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101612133565b35906fffffffffffffffffffffffffffffffff8216820361022957565b359063ffffffff8216820361022957565b67ffffffffffffffff81116104f25760051b60200190565b60405160208101907f19457468657265756d205369676e6564204d6573736167653a0a33320000000082527ff3acba882491058ea715223a1463b7d7e8610fbbb588100fb1e69a89099384a0603c820152603c815261220f8161209a565b51902090565b6001548110156104c35760016000527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60190600090565b6003548110156104c35760036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0190600090565b73ffffffffffffffffffffffffffffffffffffffff8091169060009082825281602052600160408320541461234a57600160408320556001546801000000000000000081101561231d576122de816001869301600155612215565b909283549160031b90811b9283911b169119161790557fe31c10b0adbedd0c6e5d024286c6eeead7761e65a67608dcf0b67604c0da7e2f3391604051a3565b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b505050565b73ffffffffffffffffffffffffffffffffffffffff166000818152806020526040812054156123a7578060408120557f58466e5837b54e559819c9ba8a5d7c77c97c985d1aabf4bdc5f41069fa5d65a03391604051a3565b5050565b906123b582612199565b6123c260405191826120b6565b828152601f196123d28294612199565b0190602036910137565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146124095760010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80518210156104c35760209160051b010190565b6000338152806020526040812054156124f75773ffffffffffffffffffffffffffffffffffffffff809216918282526002602052600160408320541461234a57600160408320556003546801000000000000000081101561231d576124b881600186930160035561224c565b909283549160031b90811b9283911b169119161790557f75d30ca40c7bcd48e685894b82b864808b9cb566090efc53444a2e61742f18a33391604051a3565b634a0bfec19052336020526024601cfd5b600090338252816020526040822054156125745773ffffffffffffffffffffffffffffffffffffffff169081815260026020526040812054156123a7578060408120557fdadd1471db1ea2f303654fb1bdcc010e5a664214ab41934c0f752aabca88a4913391604051a3565b634a0bfec18252336020526024601cfd5b602081519101517fffffffff00000000000000000000000000000000000000000000000000000000604051917fffffffffffffffffffffffffffffffff0000000000000000000000000000000060208401947f0000000000000000000000000000000000000000000000000000000000000000865260801b16604084015260e01b166050820152603481526126198161209a565b51902060405160208101917f19457468657265756d205369676e6564204d6573736167653a0a3332000000008352603c820152603c815261220f8161209a565b604051906126668261209a565b60006040838281528260208201520152565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610229570180359067ffffffffffffffff82116102295760200191813603831361022957565b906040516126d68161207e565b602060018294805484520154910152565b3573ffffffffffffffffffffffffffffffffffffffff811681036102295790565b90600060206040516127198161207e565b8281520152612726612659565b506127346040820182612678565b93905060ff6102055416808503612ae957506127536040830183612678565b156104c35735916101008360f81c10156104c35761277a6101fe8460f71c166005016126c9565b92835160208501511715612ad75760019060f81c1b90612798612659565b5060208451940151604051946127ad8661209a565b855260208501526001604085015260015b60ff8116928784101561295b576127d86040840184612678565b85919510156104c3576101008186013560f81c10156104c3576128076101fe8287013560f71c166005016126c9565b9485516020870151171561293e5760018282013560f81c1b83166128ed5791600160ff94928194013560f81c1b179487805160208201516020828460408101517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f809881808099818099818099818a81808280099781898d510990820390089d8e936040848087800981808d8180856004099b8c9a089382039082030891800908910152099b09950151816002988d82039409900908840996828080888709810384820308818a800908809a5209900982039482039008900908602088015201166127be565b96509750505050509150604051927f1280090c000000000000000000000000000000000000000000000000000000006020850152602491013560f81c8184015282526129388261209a565b60009190565b9598509550505050506129569250013560f81c612c75565b600091565b50509294505091600060206040516129728161207e565b8281520152604051906129848261207e565b600082526020820160008152604086015195600096600190807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f915b612a9a57505050867ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f809260208284612a199b9c0991838382510989520151920990099052612a11602084016126e7565b9235916131fc565b15612a4c57604051906020820182811067ffffffffffffffff8211176104f2578060009160405281845236903760019190565b604051907fbd2a556b000000000000000000000000000000000000000000000000000000006020830152600482526040820182811067ffffffffffffffff8211176104f25760405260009190565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8183949b930492818c85098203900892909182029003806129c0565b9250509250612956915060f81c612c75565b6040517fce818a2400000000000000000000000000000000000000000000000000000000602082015260ff958616602482015294166044808601919091528452915061293890506064836120b6565b6040918282209273ffffffffffffffffffffffffffffffffffffffff91828516926020612b96612b666121b1565b84518386015195870151875192835260ff9091166020830152604082019590955260608101949094526080840190565b836000948592838052039060015afa15612c6957808251168403612c655760ff8660981c1695610100871015612c385760971c6101fe16906005820190612bdc826126c9565b8051602082015117612c235750506020866006927f2c44ddc69b8b0966ef9e8edb873b850fb2eff06ca6e2bc37c9adf3551ec39e549697985190550151910155339251a390565b92509392955050201603612c35575090565b80fd5b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526032600452fd5b5080fd5b509051903d90823e3d90fd5b60ff604051917fc523c75e00000000000000000000000000000000000000000000000000000000602084015216602482015260248152612cb48161209a565b90565b3563ffffffff811681036102295790565b356fffffffffffffffffffffffffffffffff811681036102295790565b60405190612cf28261207e565b610206546fffffffffffffffffffffffffffffffff8116835260801c63ffffffff166020830152565b91909163ffffffff8080941691160191821161240957565b91927fffffffffffffffffffffffffffffffffffffffff000000000000000000000000859460349693855260601b1660208401528483013701016000815290565b80358252602081013573ffffffffffffffffffffffffffffffffffffffff811680910361022957602083015260408101357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1823603018112156102295701906020823592019167ffffffffffffffff811161022957803603831361022957601f81601f1992608095606060408701528160608701528686013760008582860101520116010190565b9061261960887fffffffff0000000000000000000000000000000000000000000000000000000092612e596020612e5287612cc8565b9601612cb7565b907fffffffffffffffffffffffffffffffffffffffff000000000000000000000000612e87602083016126e7565b91612e956040820182612678565b9384939192604051998a977fffffffffffffffffffffffffffffffff0000000000000000000000000000000060208a019d8e7f0000000000000000000000000000000000000000000000000000000000000000905260801b1660408a015260e01b16605088015235605487015260601b1660748501528484013781016000838201520360688101845201826120b6565b3360005260026020526040600020541561029e576fffffffffffffffffffffffffffffffff612f52612f5b565b51169081151590565b60006020604051612f6b8161207e565b8281520152604051612f7c8161207e565b600454906fffffffffffffffffffffffffffffffff82168152602081019163ffffffff809160801c168352612faf612ce5565b926020840191612fcd8184511661ffff6102055460081c1690612d1b565b81804216911611159283612fef575b505050600014612fea575090565b905090565b81929350511691511610388080612fdc565b906101008110156104c35760011b806005019061301d826126c9565b908151602083015117613039575b50505050613037613091565b565b60066000918260409555015573ffffffffffffffffffffffffffffffffffffffff918291201691167f9ec05f17908406877637da36ab29c002620cb544d0049837bfa095e62726455c6000604051a33880808061302b565b303b15613037576130a0612ce5565b6020810163ffffffff906130c28282511661ffff6102055460081c1690612d1b565b828042169116118015806131d8575b613178575b61312a575b5050506000610206556004547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff73ffffffff000000000000000000000000000000004260801b16911617600455565b6fffffffffffffffffffffffffffffffff6040519351168352511660208201527f20246a67cf0cfc89415e0ea0b3293fcc5138acc3c3b2111012dd06eb742b899460403392a23880806130db565b6fffffffffffffffffffffffffffffffff845116600454907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff00000000000000000000000000000000855160801b16921617176004556130d6565b50828251168360045460801c16106130d1565b610207544781811115612fea575090565b919091811580156133b4575b6133ab57602081019283518251947ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f80600781808a80098a0908908380090361339f577ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641419283861015613392577f0100000000000000000000000000000000000000000000000000000000000000604051936020850198895260f81b16604084015260418301527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008760601b16606183015260558252608082019382851067ffffffffffffffff8611176104f257601b6000968580600160ff82613363987fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff809b60209f8e6040528c519020069851978894859251160198098303961693099003875260ff166020870152604086015260608501526080840190565b84805203019060015afa1561145f5773ffffffffffffffffffffffffffffffffffffffff806000511691161490565b5050505050505050600090565b50505050505050600090565b50505050600090565b5073ffffffffffffffffffffffffffffffffffffffff84161561320856fea2646970667358221220c21b536128e7c89e0161e33fd6be525b7ae687f6dcc23404560dd0a23c9103db64736f6c63430008100033", + "sourceMap": "562:18101:29:-:0;;;;;;;;;;-1:-1:-1;;562:18101:29;;;;-1:-1:-1;;;;;562:18101:29;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;562:18101:29;;;;;;;;;;;;;-1:-1:-1;562:18101:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;562:18101:29;;;;;;;;;2980:38:1;562:18101:29;;2980:38:1;1728:9:28;;562:18101:29;;1779:10:28;;;13277:3;562:18101:29;;;;;;13277:11:28;1841:1;13277:11;;13273:104;;-1:-1:-1;14825:4:29;;;;;:::i;:::-;562:18101;;;;;;;1761:10;;14323:39;;;;14319:232;;-1:-1:-1;14319:232:29;;;;;;:::i;:::-;18244:18;562:18101;;-1:-1:-1;;1860:14:29;18244:41;;;18240:239;;-1:-1:-1;562:18101:29;;;;;;;;1779:10:28;562:18101:29;;;;;;;;;;;;;;;;18240:239;562:18101;;;;;;;;;;18306:108;18349:10;;18306:108;;562:18101;18240:239;;;;;;14319:232;562:18101;;;;;;;;;;;14383:105;14425:10;;14383:105;;-1:-1:-1;;562:18101:29;;;;14319:232;;;;;;13273:104:28;1841:1;562:18101:29;;;;;;;;13309:33:28;13320:10;;13309:33;;-1:-1:-1;;562:18101:29;;;;13273:104:28;;;;;562:18101:29;;;;-1:-1:-1;;;562:18101:29;;;;;;;;;-1:-1:-1;;;562:18101:29;;;;;;;;;-1:-1:-1;562:18101:29;;;;;;-1:-1:-1;562:18101:29;;;;;-1:-1:-1;562:18101:29;15625:2083;15727:4;15719:25;:30;15715:43;;562:18101;;;;;;-1:-1:-1;;;;;562:18101:29;;;;;;;;;;15839:11;562:18101;;-1:-1:-1;;;;;562:18101:29;;;;;;;;;;;;;;;;;;;;16178:17;562:18101;;;;;;;;;;;;;;;-1:-1:-1;16206:15:29;;;;562:18101;;;16161:61;;;16796:53;;;15625:2083;16792:106;;15625:2083;17020:97;;15625:2083;-1:-1:-1;;;562:18101:29;;;-1:-1:-1;17662:9:29;562:18101;;-1:-1:-1;;;;562:18101:29;16206:15;562:18101;;-1:-1:-1;;;562:18101:29;;;;15625:2083::o;17020:97::-;562:18101;;;;;;;;;;;;;17065:41;562:18101;17083:10;17065:41;;17020:97;;;;;;16792:106;562:18101;;;;16865:22;562:18101;;;;;;;;;;;;;;;;;;;;16865:22;562:18101;16792:106;;16796:53;562:18101;;;;;;16836:9;562:18101;;;;-1:-1:-1;16796:53:29;;562:18101;;;;-1:-1:-1;562:18101:29;;;;;-1:-1:-1;562:18101:29;15715:43;15751:7::o", + "linkReferences": {} + }, + "deployedBytecode": { + "object": "0x608080604052600436101561001d575b50361561001b57600080fd5b005b60003560e01c908115611ab757508060821461189b5780630760861b14611a045780630e5a6c70146115ef5780630fce3415146119ba57806310b07b7114611969578063224242ca146118a95780632f529d731461189b5780632fba4aa914611818578063313ce567146117fc578063393e5ede1461179c5780633bee58f9146117525780633ea0c15e146117375780634ca29923146116fc5780634fce7a2a146116b557806350d25bcd1461166b57806357de26a41461161a57806359e02dd7146115ef578063646edb68146115c957806365c4ce7a146115a857806365fae35e146115725780636712af9e14611068578063789d819114610fc65780638928a1f814610c145780638b0b044c14610bfb5780639954b0dc14610b465780639c52a7f114610b105780639dadc88614610a4d5780639fd001f614610a2e578063ab06ee16146109c3578063acf40b6f14610968578063b259da5c1461088f578063b9ee3fc11461086a578063bf353dbb14610823578063bfe5861f14610602578063c83c6334146105cb578063ceed3ef214610544578063d0a5882a14610521578063d63605b8146103ea578063dac42ad81461039d578063f29c29c41461037c578063fe663495146102b0578063feaf968c1461022e5763febb0f7e14610207575b3861000f565b3461022957600060031936011261022957602060ff6102055416604051908152f35b600080fd5b34610229576000600319360112610229573360005260026020526040600020541561029e5760a061025d612f5b565b63ffffffff60206fffffffffffffffffffffffffffffffff835116920151166040519160018352602083015260006040830152606082015260016080820152f35b63d957b595600052336020526024601cfd5b34610229576020600319360112610229576102c96120d9565b3360005260006020526040600020541561036a5760ff811680156102295761020591825460ff8116838103610301575b61001b613091565b6040805160ff92831681529390911660208401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009233917f95623b9931156d6d5cb43881a13f223ae416fb199e5edf776efb38766f38cbea91a21617905580808080806102f9565b634a0bfec1600052336020526024601cfd5b346102295760206003193601126102295761001b6103986120f7565b61244c565b3461022957600319604081360112610229576024359067ffffffffffffffff8211610229576060908236030112610229576103df602091600401600435612708565b506040519015158152f35b3461022957600060031936011261022957604051612020810181811067ffffffffffffffff8211176104f25760405261010090818152602061200036828401376000816040516104398161207e565b828152015260009060005b848110610460578284526040518061045c868261211a565b0390f35b8460ff821610156104c3576001906104806101fe82841b166005016126c9565b80518482015117610493575b5001610444565b9373ffffffffffffffffffffffffffffffffffffffff604084939620166104ba8288612438565b5201928661048c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b3461022957600060031936011261022957602061053c6121b1565b604051908152f35b34610229576000600319360112610229573360005260026020526040600020541561029e576060610573612f5b565b80516fffffffffffffffffffffffffffffffff9081169182156105b8579063ffffffff60208193015116906001935b6040519415158552166020840152166040820152f35b506000915063ffffffff908290816105a2565b34610229576020600319360112610229576105e46120d9565b3360005260006020526040600020541561036a5761001b9033613001565b346102295760406003193601126102295767ffffffffffffffff600435818111610229573660238201121561022957806004013561063f81612199565b9061064d60405192836120b6565b808252602092838301906024809360061b82010190368211610229578301915b8183106107f3575050508035938411610229573660238501121561022957836004013561069981612199565b946106a760405196876120b6565b81865284860183606080940283010191368311610229578401905b8282106107b357505050503360005260008352604060002054156107a45750805190835182036102295761070e6106f883612199565b9261070660405194856120b6565b808452612199565b92601f19818401940136853760005b825181101561075e578061074861073660019386612438565b51610741838a612438565b5190612b38565b60ff6107548388612438565b911690520161071d565b508284604051928392818401908285525180915260408401929160005b82811061078a57505050500390f35b835160ff168552869550938101939281019260010161077b565b634a0bfec1600052338352601cfd5b8382360312610229578684916040516107cb8161209a565b6107d4856120e9565b81528285013583820152604085013560408201528152019101906106c2565b6040833603126102295785604091825161080c8161207e565b85358152828601358382015281520192019161066d565b346102295760206003193601126102295773ffffffffffffffffffffffffffffffffffffffff6108516120f7565b1660005260006020526020604060002054604051908152f35b3461022957600060031936011261022957602060ff6102055460181c16604051908152f35b34610229576020806003193601126102295760043567ffffffffffffffff81116102295736602382011215610229578060040135906108cd82612199565b916108db60405193846120b6565b80835260248484019160051b8301019136831161022957602401905b828210610951578385336000526000815260406000205415610940575060005b815181101561001b578061093a60ff61093260019486612438565b511633613001565b01610917565b634a0bfec16000523390526024601cfd5b84809161095d846120e9565b8152019101906108f7565b34610229576040600319360112610229576040516109858161207e565b6004356fffffffffffffffffffffffffffffffff8116810361022957815260243563ffffffff81168103610229578161053c91602080940152612585565b34610229576020600319360112610229576004353360005260006020526040600020541561036a5761020780548281036109f957005b6040519081528260208201527ffccefdc521d919e1c7e09025203c5542ec6e99eb409c750d4fc386cdb7feee3760403392a255005b3461022957600060031936011261022957602061020754604051908152f35b346102295760206003193601126102295760043561ffff80821691828103610229573360005260006020526040600020541561036a57821561022957610205928354928360081c1691818303610aa55761001b613091565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ff9262ffff009260405191825260208201527f06eaf85dccf95ffb2040720d23d296ebf45c6b193a5f373a553300fa640a111860403392a260081b16911617905580808080806102f9565b3461022957602060031936011261022957610b296120f7565b3360005260006020526040600020541561036a5761001b9061234f565b34610229576000600319360112610229576003610b6381546123ab565b9060009160005b8151811015610bea5780610b80610bbb9261224c565b9073ffffffffffffffffffffffffffffffffffffffff9182915490871b1c166000526002602052600160406000205414610bc0575b506123dc565b610b6a565b610bc98261224c565b905490861b1c16610be3610bdc876123dc565b9685612438565b5285610bb5565b8382526040518061045c848261211a565b3461022957602061053c610c0e36611ee8565b90612e1c565b3461022957602060031981813601126102295767ffffffffffffffff906004358281116102295760608160040192823603011261022957610c53612ce5565b84810163ffffffff928382511695610c77610205549761ffff8960081c1690612d1b565b8580421691161115610f9c57610c9c6044610c94602485016126e7565b930187612678565b9290610cb36040519485928c8401948b3586612d33565b0392610cc7601f19948581018352826120b6565b51902073ffffffffffffffffffffffffffffffffffffffff90811690888a1c16808203610f65575050610d2f86610d2a6fffffffffffffffffffffffffffffffff8097511660405190610d198261207e565b8152888b60c01c168c820152612585565b612708565b91909788600014610e365750505050829051168260045460801c1610610d95575b50507f3f2cf79ad39c64280638c6fc485670c10ed4dad0a35ecd2857255ee7a130ba2060405184815280610d88339487830190612d74565b0390a25b60405190158152f35b610e2e600092610de2610206938454166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff000000000000000000000000000000006004541617600455565b825460801c167fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff73ffffffff000000000000000000000000000000006004549260801b16911617600455565b558380610d50565b90935060ff919550610e50929796945060181c1630613001565b610e586131eb565b906000808060405185335af1903d15610f5f573d9081116104f25760405190610e888989601f84011601836120b6565b81526000883d92013e5b610f19575b5084610eae60405192604084526040840190612d74565b948286038284015283519384875260005b858110610f06575050858391600084877f2984f6d7ff4df266745fc0283d83a02c5125069524c8eeb85887de137e8f890c98999a010152601f3397011601030190a2610d8c565b8181018401518882018501528301610ebf565b7f7e4d8c9da9421f78d986605928e77f0920f2c995d1cbba819c8915d2b209aa4b60405160408152610f4e6040820185612d74565b92888201528033930390a285610e97565b50610e92565b60449250604051917f73ceee3000000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b60046040517fcff75876000000000000000000000000000000000000000000000000000000008152fd5b3461022957602060031936011261022957610fdf6120d9565b6101008110156104c357610ff89060011b6005016126c9565b805160208201511761103057506040600073ffffffffffffffffffffffffffffffffffffffff60005b83519215158352166020820152f35b73ffffffffffffffffffffffffffffffffffffffff60408092201673ffffffffffffffffffffffffffffffffffffffff600191611021565b346102295761107636611e7e565b9160ff6110866040840184612678565b9050116115345763ffffffff9161109b612ce5565b83602082015116946110b9610205549661ffff8860081c1690612d1b565b854216958691161161150a5763ffffffff60208301511663ffffffff60045460801c168082116000146115025750945b6110f560208601612cb7565b63ffffffff808816911611156114b6578063ffffffff61111760208801612cb7565b161161146b57506111288385612e1c565b9060ff8135168135036102295760408051928352813560ff16602080850191909152808301358483015291013560608301526000808052909160809060015afa1561145f576000519373ffffffffffffffffffffffffffffffffffffffff948581169561010060ff8360981c1610156104c357869060406111b26101fe8560971c166005016126c9565b20160361142e577fb9dc937c5e394d0c8f76e0e324500b88251b4c909ddc56232df10e2ea42b3c639495967fffffffffffffffff000000000000000000000000000000000000000000ffffff63ff00000077ffffffffffffffffffffffffffffffffffffffff000000008761125161125f61122f602084016126e7565b61123c6040850185612678565b60409491945194859360208501973588612d33565b03601f1981018352826120b6565b51902060201b169360801c16911617176102055563ffffffff806020840151169116811461138a575b505063ffffffff61137c60206fffffffffffffffffffffffffffffffff95866112b082612cc8565b16610206908154907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff000000000000000000000000000000004260801b16921617179055611304828201612cb7565b7fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff7bffffffff000000000000000000000000000000000000000000000000610205549260c01b169116176102055561136760405195606087526060870190612d74565b966113718261216b565b168286015201612188565b1660408201528033930390a3005b6113e16fffffffffffffffffffffffffffffffff6114279351166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff000000000000000000000000000000006004541617600455565b7fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff73ffffffff000000000000000000000000000000006004549260801b16911617600455565b8480611288565b602486604051907f56d7d2e80000000000000000000000000000000000000000000000000000000082526004820152fd5b6040513d6000823e3d90fd5b61147760208601612cb7565b6040517feea80f5200000000000000000000000000000000000000000000000000000000815263ffffffff918216600482015291166024820152604490fd5b856114c360208701612cb7565b6040517f76f4b87800000000000000000000000000000000000000000000000000000000815263ffffffff918216600482015291166024820152604490fd5b9050946110e9565b60046040517f8855b9e8000000000000000000000000000000000000000000000000000000008152fd5b604460ff6102055416604051907fce818a2400000000000000000000000000000000000000000000000000000000825260ff60048301526024820152fd5b346102295760206003193601126102295761158b6120f7565b3360005260006020526040600020541561036a5761001b90612283565b346102295760206003193601126102295761001b6115c46120f7565b612508565b3461022957600060031936011261022957602061ffff6102055460081c16604051908152f35b3461022957600060031936011261022957611608612f25565b60408051928352901515602083015290f35b34610229576000600319360112610229573360005260026020526040600020541561029e576fffffffffffffffffffffffffffffffff611658612f5b565b5116801561022957602090604051908152f35b34610229576000600319360112610229573360005260026020526040600020541561029e5760206fffffffffffffffffffffffffffffffff6116ab612f5b565b5116604051908152f35b346102295760206003193601126102295773ffffffffffffffffffffffffffffffffffffffff6116e36120f7565b1660005260026020526020604060002054604051908152f35b346102295760006003193601126102295760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461022957600060031936011261022957602061053c6131eb565b346102295760206003193601126102295773ffffffffffffffffffffffffffffffffffffffff6117806120f7565b1660005260026020526020600160406000205414604051908152f35b34610229576000600319360112610229573360005260026020526040600020541561029e576117c9612f5b565b6fffffffffffffffffffffffffffffffff81511680156102295763ffffffff602060409301511682519182526020820152f35b3461022957600060031936011261022957602060405160128152f35b34610229576020600319360112610229576118316120f7565b61010060ff8260981c1610156104c357806118576101fe60209360971c166005016126c9565b8051838201511715159182611873575b50506040519015158152f35b909150604073ffffffffffffffffffffffffffffffffffffffff809216922016148280611867565b506118a4611f24565b610201565b346102295760006003193601126102295760016118c681546123ab565b60009182815b6118e2575b8383526040518061045c858261211a565b825181101561196457611934816118f98493612215565b73ffffffffffffffffffffffffffffffffffffffff809254600392831b1c166000526000602052846040600020541461193a575b50506123dc565b906118cc565b61194383612215565b9054911b1c1661195c611955886123dc565b9787612438565b52868061192d565b6118d1565b34610229576000600319360112610229573360005260026020526040600020541561029e5760406fffffffffffffffffffffffffffffffff6119a9612f5b565b511681519080151582526020820152f35b346102295760206003193601126102295773ffffffffffffffffffffffffffffffffffffffff6119e86120f7565b1660005260006020526020600160406000205414604051908152f35b3461022957600319360160a081126102295760401361022957604051611a298161207e565b6004358152602435602082015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126102295760405190611a6d8261209a565b60443560ff81168103610229578252606435602083015260843560408301523360005260006020526040600020541561036a57602091611aac91612b38565b60ff60405191168152f35b611ac036611e7e565b91604082019360ff611ad28685612678565b905011611e435750611ae2612ce5565b63ffffffff8060208301511661020595611b0687549261ffff8460081c1690612d1b565b834216938491161161150a5763ffffffff92836020860151168460045460801c16808211600014611e3c57505b611b3f60208801612cb7565b858216958691161115611e3057508063ffffffff611b5f60208901612cb7565b1611611e245750611b708686612e1c565b9060ff8135168135036102295760408051928352813560ff16602080850191909152808301358483015291013560608301526000808052909160809060015afa1561145f576000519073ffffffffffffffffffffffffffffffffffffffff978883169861010060ff8560981c1610156104c35789906040611bfa6101fe8760971c166005016126c9565b201603611df3579361137c936020938897937fffffffffffffffff000000000000000000000000000000000000000000ffffff63ff00000077ffffffffffffffffffffffffffffffffffffffff000000008b611251611ca57fb9dc937c5e394d0c8f76e0e324500b88251b4c909ddc56232df10e2ea42b3c639f9d611c9063ffffffff9f8e611c8991016126e7565b9185612678565b604094919451948f9386948501973588612d33565b519020891b169360801c16911617178955858483015116908114611d95575b50506fffffffffffffffffffffffffffffffff9687611ce283612cc8565b16610206908154907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff000000000000000000000000000000004260801b16921617179055611d36838301612cb7565b7fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff7bffffffff00000000000000000000000000000000000000000000000083549260c01b16911617905561136760405195606087526060870190612d74565b6113e16fffffffffffffffffffffffffffffffff611dec9351166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff000000000000000000000000000000006004541617600455565b8880611cc4565b602489604051907f56d7d2e80000000000000000000000000000000000000000000000000000000082526004820152fd5b61147760208701612cb7565b6114c360208801612cb7565b9050611b33565b60449060ff61020554167fce818a2400000000000000000000000000000000000000000000000000000000825260ff60048301526024820152fd5b6003199182820160c0811261022957604013610229576004926044359067ffffffffffffffff8211610229576060908285030112610229577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c606091850193011261022957606490565b906003198083016060811261022957604013610229576004926044359167ffffffffffffffff8311610229578260609203011261022957820190565b503461022957611f3336611ee8565b9063ffffffff90816020611f45612f5b565b0151169260208201938084611f5987612cb7565b161115612075575082611f6b85612cb7565b814216918291161161206c575060408236031261022957611fb390610d2a604051611f958161207e565b611f9e8561216b565b8152611fa987612188565b6020820152612585565b9015612064575061202e6120286fffffffffffffffffffffffffffffffff9283611fdc82612cc8565b16600454907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff000000000000000000000000000000004260801b1692161717600455612cc8565b93612cb7565b90604051931683521660208201527f7045db5134e0c7ca4bff0c0e096616ef4ebb36b719cd00c5f0ba4f4475ecceee60403392a2005b602081519101fd5b61147785612cb7565b6114c385612cb7565b6040810190811067ffffffffffffffff8211176104f257604052565b6060810190811067ffffffffffffffff8211176104f257604052565b90601f601f19910116810190811067ffffffffffffffff8211176104f257604052565b6004359060ff8216820361022957565b359060ff8216820361022957565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361022957565b6020908160408183019282815285518094520193019160005b828110612141575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101612133565b35906fffffffffffffffffffffffffffffffff8216820361022957565b359063ffffffff8216820361022957565b67ffffffffffffffff81116104f25760051b60200190565b60405160208101907f19457468657265756d205369676e6564204d6573736167653a0a33320000000082527ff3acba882491058ea715223a1463b7d7e8610fbbb588100fb1e69a89099384a0603c820152603c815261220f8161209a565b51902090565b6001548110156104c35760016000527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60190600090565b6003548110156104c35760036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0190600090565b73ffffffffffffffffffffffffffffffffffffffff8091169060009082825281602052600160408320541461234a57600160408320556001546801000000000000000081101561231d576122de816001869301600155612215565b909283549160031b90811b9283911b169119161790557fe31c10b0adbedd0c6e5d024286c6eeead7761e65a67608dcf0b67604c0da7e2f3391604051a3565b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b505050565b73ffffffffffffffffffffffffffffffffffffffff166000818152806020526040812054156123a7578060408120557f58466e5837b54e559819c9ba8a5d7c77c97c985d1aabf4bdc5f41069fa5d65a03391604051a3565b5050565b906123b582612199565b6123c260405191826120b6565b828152601f196123d28294612199565b0190602036910137565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146124095760010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80518210156104c35760209160051b010190565b6000338152806020526040812054156124f75773ffffffffffffffffffffffffffffffffffffffff809216918282526002602052600160408320541461234a57600160408320556003546801000000000000000081101561231d576124b881600186930160035561224c565b909283549160031b90811b9283911b169119161790557f75d30ca40c7bcd48e685894b82b864808b9cb566090efc53444a2e61742f18a33391604051a3565b634a0bfec19052336020526024601cfd5b600090338252816020526040822054156125745773ffffffffffffffffffffffffffffffffffffffff169081815260026020526040812054156123a7578060408120557fdadd1471db1ea2f303654fb1bdcc010e5a664214ab41934c0f752aabca88a4913391604051a3565b634a0bfec18252336020526024601cfd5b602081519101517fffffffff00000000000000000000000000000000000000000000000000000000604051917fffffffffffffffffffffffffffffffff0000000000000000000000000000000060208401947f0000000000000000000000000000000000000000000000000000000000000000865260801b16604084015260e01b166050820152603481526126198161209a565b51902060405160208101917f19457468657265756d205369676e6564204d6573736167653a0a3332000000008352603c820152603c815261220f8161209a565b604051906126668261209a565b60006040838281528260208201520152565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610229570180359067ffffffffffffffff82116102295760200191813603831361022957565b906040516126d68161207e565b602060018294805484520154910152565b3573ffffffffffffffffffffffffffffffffffffffff811681036102295790565b90600060206040516127198161207e565b8281520152612726612659565b506127346040820182612678565b93905060ff6102055416808503612ae957506127536040830183612678565b156104c35735916101008360f81c10156104c35761277a6101fe8460f71c166005016126c9565b92835160208501511715612ad75760019060f81c1b90612798612659565b5060208451940151604051946127ad8661209a565b855260208501526001604085015260015b60ff8116928784101561295b576127d86040840184612678565b85919510156104c3576101008186013560f81c10156104c3576128076101fe8287013560f71c166005016126c9565b9485516020870151171561293e5760018282013560f81c1b83166128ed5791600160ff94928194013560f81c1b179487805160208201516020828460408101517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f809881808099818099818099818a81808280099781898d510990820390089d8e936040848087800981808d8180856004099b8c9a089382039082030891800908910152099b09950151816002988d82039409900908840996828080888709810384820308818a800908809a5209900982039482039008900908602088015201166127be565b96509750505050509150604051927f1280090c000000000000000000000000000000000000000000000000000000006020850152602491013560f81c8184015282526129388261209a565b60009190565b9598509550505050506129569250013560f81c612c75565b600091565b50509294505091600060206040516129728161207e565b8281520152604051906129848261207e565b600082526020820160008152604086015195600096600190807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f915b612a9a57505050867ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f809260208284612a199b9c0991838382510989520151920990099052612a11602084016126e7565b9235916131fc565b15612a4c57604051906020820182811067ffffffffffffffff8211176104f2578060009160405281845236903760019190565b604051907fbd2a556b000000000000000000000000000000000000000000000000000000006020830152600482526040820182811067ffffffffffffffff8211176104f25760405260009190565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8183949b930492818c85098203900892909182029003806129c0565b9250509250612956915060f81c612c75565b6040517fce818a2400000000000000000000000000000000000000000000000000000000602082015260ff958616602482015294166044808601919091528452915061293890506064836120b6565b6040918282209273ffffffffffffffffffffffffffffffffffffffff91828516926020612b96612b666121b1565b84518386015195870151875192835260ff9091166020830152604082019590955260608101949094526080840190565b836000948592838052039060015afa15612c6957808251168403612c655760ff8660981c1695610100871015612c385760971c6101fe16906005820190612bdc826126c9565b8051602082015117612c235750506020866006927f2c44ddc69b8b0966ef9e8edb873b850fb2eff06ca6e2bc37c9adf3551ec39e549697985190550151910155339251a390565b92509392955050201603612c35575090565b80fd5b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526032600452fd5b5080fd5b509051903d90823e3d90fd5b60ff604051917fc523c75e00000000000000000000000000000000000000000000000000000000602084015216602482015260248152612cb48161209a565b90565b3563ffffffff811681036102295790565b356fffffffffffffffffffffffffffffffff811681036102295790565b60405190612cf28261207e565b610206546fffffffffffffffffffffffffffffffff8116835260801c63ffffffff166020830152565b91909163ffffffff8080941691160191821161240957565b91927fffffffffffffffffffffffffffffffffffffffff000000000000000000000000859460349693855260601b1660208401528483013701016000815290565b80358252602081013573ffffffffffffffffffffffffffffffffffffffff811680910361022957602083015260408101357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1823603018112156102295701906020823592019167ffffffffffffffff811161022957803603831361022957601f81601f1992608095606060408701528160608701528686013760008582860101520116010190565b9061261960887fffffffff0000000000000000000000000000000000000000000000000000000092612e596020612e5287612cc8565b9601612cb7565b907fffffffffffffffffffffffffffffffffffffffff000000000000000000000000612e87602083016126e7565b91612e956040820182612678565b9384939192604051998a977fffffffffffffffffffffffffffffffff0000000000000000000000000000000060208a019d8e7f0000000000000000000000000000000000000000000000000000000000000000905260801b1660408a015260e01b16605088015235605487015260601b1660748501528484013781016000838201520360688101845201826120b6565b3360005260026020526040600020541561029e576fffffffffffffffffffffffffffffffff612f52612f5b565b51169081151590565b60006020604051612f6b8161207e565b8281520152604051612f7c8161207e565b600454906fffffffffffffffffffffffffffffffff82168152602081019163ffffffff809160801c168352612faf612ce5565b926020840191612fcd8184511661ffff6102055460081c1690612d1b565b81804216911611159283612fef575b505050600014612fea575090565b905090565b81929350511691511610388080612fdc565b906101008110156104c35760011b806005019061301d826126c9565b908151602083015117613039575b50505050613037613091565b565b60066000918260409555015573ffffffffffffffffffffffffffffffffffffffff918291201691167f9ec05f17908406877637da36ab29c002620cb544d0049837bfa095e62726455c6000604051a33880808061302b565b303b15613037576130a0612ce5565b6020810163ffffffff906130c28282511661ffff6102055460081c1690612d1b565b828042169116118015806131d8575b613178575b61312a575b5050506000610206556004547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff73ffffffff000000000000000000000000000000004260801b16911617600455565b6fffffffffffffffffffffffffffffffff6040519351168352511660208201527f20246a67cf0cfc89415e0ea0b3293fcc5138acc3c3b2111012dd06eb742b899460403392a23880806130db565b6fffffffffffffffffffffffffffffffff845116600454907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff00000000000000000000000000000000855160801b16921617176004556130d6565b50828251168360045460801c16106130d1565b610207544781811115612fea575090565b919091811580156133b4575b6133ab57602081019283518251947ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f80600781808a80098a0908908380090361339f577ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641419283861015613392577f0100000000000000000000000000000000000000000000000000000000000000604051936020850198895260f81b16604084015260418301527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008760601b16606183015260558252608082019382851067ffffffffffffffff8611176104f257601b6000968580600160ff82613363987fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff809b60209f8e6040528c519020069851978894859251160198098303961693099003875260ff166020870152604086015260608501526080840190565b84805203019060015afa1561145f5773ffffffffffffffffffffffffffffffffffffffff806000511691161490565b5050505050505050600090565b50505050505050600090565b50505050600090565b5073ffffffffffffffffffffffffffffffffffffffff84161561320856fea2646970667358221220c21b536128e7c89e0161e33fd6be525b7ae687f6dcc23404560dd0a23c9103db64736f6c63430008100033", + "sourceMap": "562:18101:29:-:0;;;;;;;;;;-1:-1:-1;562:18101:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2040:595:4;562:18101:29;2040:595:4;;;;562:18101:29;2040:595:4;;;562:18101:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;562:18101:29;;;;;;;1564:16:28;562:18101:29;;;;;;;;;;;;;;;;;-1:-1:-1;;562:18101:29;;;;;2040:595:4;562:18101:29;2040:595:4;;;;562:18101:29;;2040:595:4;;;;;562:18101:29;12861:18;;:::i;:::-;562:18101;2040:595:4;562:18101:29;;;;13043:12;;562:18101;;;;;12900:1;562:18101;;2040:595:4;562:18101:29;;;;;;;;;;;;12900:1;562:18101;;;;;2040:595:4;;562:18101:29;2040:595:4;;;;;;;562:18101:29;;;;;-1:-1:-1;;562:18101:29;;;;;;;:::i;:::-;2120:602:1;562:18101:29;2120:602:1;562:18101:29;;2120:602:1;562:18101:29;;2120:602:1;;;;;562:18101:29;;;13252:9:28;;562:18101:29;;13277:3:28;562:18101:29;;;;;;13277:11:28;;;13273:104;;562:18101:29;14825:4;;:::i;13273:104:28:-;562:18101:29;;;;;;;;;;;;;;;;;;;2120:602:1;;13309:33:28;;;562:18101:29;;;;13273:104:28;;;;;;;2120:602:1;;562:18101:29;2120:602:1;;562:18101:29;2120:602:1;;;;562:18101:29;;;;;-1:-1:-1;;562:18101:29;;;;;;;;:::i;:::-;;:::i;:::-;;;;-1:-1:-1;;562:18101:29;;;;;;;;;;;;;;;;;;;;;;;;4072:45:28;562:18101:29;;;;;;4072:45:28;:::i;:::-;562:18101:29;;;;;;;;;;;;;;-1:-1:-1;;562:18101:29;;;;;;;;;;;;;;;;;;;;;10130:3:28;562:18101:29;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;10232:6:28;562:18101:29;10240:7:28;;;;;;10608:68;;;562:18101:29;;;;10608:68:28;562:18101:29;;:::i;:::-;;;;10232:6:28;562:18101:29;;;;;;;;;;;;;;;;10273:8:28;562:18101:29;;:::i;:::-;;;3056:6:31;;;562:18101:29;3047:15:31;10306:207:28;;10232:6;562:18101:29;;10232:6:28;;10306:207;2524:97:31;;562:18101:29;2524:97:31;;;;;10395:18:28;;;;:::i;:::-;562:18101:29;;10306:207:28;;;;562:18101:29;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;562:18101:29;;;;;;857:203:28;;:::i;:::-;562:18101:29;;;;;;;;;;;-1:-1:-1;;562:18101:29;;;;;2040:595:4;562:18101:29;2040:595:4;;;;562:18101:29;;2040:595:4;;;;;562:18101:29;11683:18;;:::i;:::-;562:18101;;;;;;;11718:17;;562:18101;;11771:12;562:18101;2040:595:4;11771:12:29;;;562:18101;;11718:94;11751:4;11718:94;;562:18101;;;;;;;;2040:595:4;562:18101:29;;;;;;;;;11718:94;-1:-1:-1;562:18101:29;;-1:-1:-1;562:18101:29;;;;;11718:94;;562:18101;;;;;-1:-1:-1;;562:18101:29;;;;;;;:::i;:::-;2120:602:1;562:18101:29;2120:602:1;562:18101:29;;2120:602:1;562:18101:29;;2120:602:1;;;;;12518:6:28;2120:602:1;;12518:6:28;:::i;562:18101:29:-;;;;;-1:-1:-1;;562:18101:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2120:602:1;;;;;562:18101:29;2120:602:1;562:18101:29;2120:602:1;;562:18101:29;;2120:602:1;;;;;562:18101:29;;;;;;11153:35:28;;562:18101:29;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;:::i;:::-;;-1:-1:-1;;562:18101:29;;;;;;;;;11267:6:28;562:18101:29;;11275:18:28;;;;;11329:10;11323:32;11329:10;562:18101:29;11329:10:28;;;:::i;:::-;;11341:13;;;;:::i;:::-;;11323:32;;:::i;:::-;562:18101:29;11310:45:28;;;;:::i;:::-;562:18101:29;;;;;11267:6:28;;11275:18;;;;562:18101:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;562:18101:29;;;;;;;;;;;;2120:602:1;;562:18101:29;2120:602:1;;;;;;562:18101:29;;;;;;;;;;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;562:18101:29;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;562:18101:29;;;;;;;901:21;562:18101;;;;;;;;;;;;;;;;-1:-1:-1;;562:18101:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2120:602:1;;;562:18101:29;2120:602:1;562:18101:29;2120:602:1;;562:18101:29;;2120:602:1;;;;;12633:6:28;562:18101:29;12633:6:28;562:18101:29;;12641:18:28;;;;;12694:10;;562:18101:29;12694:10:28;562:18101:29;12694:10:28;;;:::i;:::-;562:18101:29;;2120:602:1;12694:10:28;:::i;:::-;562:18101:29;12633:6:28;;2120:602:1;;562:18101:29;2120:602:1;;;;562:18101:29;2120:602:1;;562:18101:29;;;;;;;:::i;:::-;;;;;;;;;;;;;;-1:-1:-1;;562:18101:29;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;-1:-1:-1;;562:18101:29;;;;;;;2120:602:1;562:18101:29;2120:602:1;562:18101:29;;2120:602:1;562:18101:29;;2120:602:1;;;;;18244:18:29;562:18101;;18244:41;;;18240:239;;562:18101;18240:239;562:18101;;;;;;;;;;18306:108;562:18101;2120:602:1;18306:108:29;;562:18101;18240:239;562:18101;;;;;-1:-1:-1;;562:18101:29;;;;;;1482:30;562:18101;;;;;;;;;;;;-1:-1:-1;;562:18101:29;;;;;;;;;;;;;;;;;2120:602:1;562:18101:29;2120:602:1;562:18101:29;;2120:602:1;562:18101:29;;2120:602:1;;;;;14284:23:29;;562:18101;;14323:17;562:18101;;;;;;;;14323:39;;;;14319:232;;;;:::i;:::-;562:18101;;;;;;;;;;;;;14383:105;562:18101;2120:602:1;14383:105:29;;562:18101;;;;;;;;14319:232;;;;;;;562:18101;;;;;-1:-1:-1;;562:18101:29;;;;;;;:::i;:::-;2120:602:1;562:18101:29;2120:602:1;562:18101:29;;2120:602:1;562:18101:29;;2120:602:1;;;;;2731:1;;;:::i;562:18101:29:-;;;;;-1:-1:-1;;562:18101:29;;;;;3837:12:4;3823:34;562:18101:29;;3823:34:4;:::i;:::-;3926:8;562:18101:29;3949:6:4;562:18101:29;3978:3:4;562:18101:29;;3957:19:4;;;;;4056:15;;3978:3;4056:15;;:::i;:::-;562:18101:29;;;;;;;;;;;;;4050:5:4;562:18101:29;;4076:1:4;562:18101:29;;;;4050:27:4;4046:99;;3978:3;;;:::i;:::-;3949:6;;4046:99;4115:15;;;:::i;:::-;562:18101:29;;;;;;;4097:33:4;4106:5;;;:::i;:::-;4097:33;;;:::i;:::-;562:18101:29;4046:99:4;;;3957:19;4245:70;;;562:18101:29;;;;3957:19:4;562:18101:29;;:::i;:::-;;;;;9683:46;562:18101;;;:::i;:::-;9683:46;;:::i;562:18101::-;;;;;-1:-1:-1;;562:18101:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;7130:14;;;562:18101;;;;;;;7130:34;7147:17;562:18101;;;;;;;7130:34;;:::i;:::-;7174:15;;;562:18101;;;7130:60;7260:24;7256:83;;7640:19;;7592:22;;;;;:::i;:::-;7640:19;;;;:::i;:::-;562:18101;;7503:178;562:18101;;7503:178;;;;;;562:18101;;;7503:178;;:::i;:::-;;;;-1:-1:-1;;7503:178:29;;;;;;;;:::i;:::-;562:18101;7472:227;;562:18101;;;;;;;;;7814:41;;;7810:139;;562:18101;;8073:184;562:18101;8110:112;562:18101;;;;;;;;;;;:::i;:::-;;;;;;;;8148:60;;;562:18101;8110:112;:::i;:::-;8073:184;:::i;:::-;8268:1125;;;;;;;;562:18101;;;;;;;;;;;;;;-1:-1:-1;8651:114:29;;8268:1125;562:18101;;8784:55;562:18101;;;;;8815:10;562:18101;8815:10;562:18101;;;;;;:::i;:::-;8784:55;;;8268:1125;562:18101;;9464:3;;562:18101;;;8651:114;562:18101;;7009:11;562:18101;7009:11;562:18101;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8651:114;;;;8268:1125;562:18101;;;;;;;9056:8;562:18101;;;;;;;;9049:4;9056:8;:::i;:::-;9139:17;;:::i;:::-;562:18101;;;;;;9191:10;;18609:26;;562:18101;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;9170:135;;562:18101;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;9324:58;562:18101;;;;;;;9191:10;562:18101;;;;9324:58;;;;8268:1125;;562:18101;;;;;;;;;;;;;;;;;9170:135;9236:54;562:18101;;;;;;;;;;;:::i;:::-;;;;;;9191:10;;9236:54;;;;9170:135;;;562:18101;;;;7810:139;7640:19;562:18101;;;;7878:60;;;;562:18101;7878:60;;562:18101;7592:22;562:18101;;;7878:60;7256:83;562:18101;;;7307:21;;;;562:18101;;;;;-1:-1:-1;;562:18101:29;;;;;;;:::i;:::-;;;;;;;;;;;9852:8:28;562:18101:29;;:::i;:::-;;;;3056:6:31;;562:18101:29;3047:15:31;9886:95:28;;;562:18101:29;;;;9886:95:28;562:18101:29;;;;;;;;;;;;;9886:95:28;2524:97:31;562:18101:29;2524:97:31;;;;562:18101:29;;9886:95:28;;;562:18101:29;;;;;;;:::i;:::-;4183:19;562:18101;4183:19;562:18101;4183:19;;;;:::i;:::-;:44;;;4179:117;;562:18101;;;;:::i;:::-;4487:14;;;;562:18101;;;4487:34;4504:17;562:18101;;;;;;;4487:34;;:::i;:::-;4532:15;;562:18101;;;;;4487:61;4633:77;;562:18101;4487:14;;;562:18101;;;;;;;;4776:30;;;:63;:30;;;:63;;;4891:12;4487:14;4891:12;;;:::i;:::-;562:18101;;;;;;4891:19;;4887:88;;4891:12;562:18101;5035:12;4487:14;4891:12;;5035;:::i;:::-;562:18101;5035:38;5031:128;;5242:46;;;;;:::i;:::-;562:18101;;;;;;;;;;;;;;;;;;;;4487:14;562:18101;;;;;;;5327:11;;;562:18101;;;;;5352:11;;562:18101;;;;;-1:-1:-1;5219:154:29;;;4487:14;;562:18101;;;5219:154;;;;;562:18101;5219:154;562:18101;;;;;;;;;;5468:3;562:18101;;;;;;;;;;;;;;;5588:8;562:18101;;:::i;:::-;2524:97:31;;5588:38:29;5584:97;;6728:50;6005:22;;;562:18101;;;6005:22;5916:178;;6005:22;4487:14;6005:22;;;:::i;:::-;6053:19;562:18101;4183:19;;6053;;:::i;:::-;562:18101;;;;;5916:178;;;4487:14;5916:178;;562:18101;;5916:178;;:::i;:::-;;-1:-1:-1;;5916:178:29;;;;;;:::i;:::-;562:18101;5885:227;;4487:14;562:18101;;;;;;;;;;4504:17;562:18101;;4487:14;;;;562:18101;;;;6364:21;;6360:74;;4776:63;562:18101;;;;4487:14;562:18101;6527:12;;;;;:::i;:::-;562:18101;4377:11;562:18101;;;4532:15;562:18101;;4532:15;562:18101;;;;;;;;;6700:12;4891;;;6700;:::i;:::-;562:18101;;4504:17;562:18101;;;;;;;;4504:17;562:18101;;;;;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;4891:12;562:18101;:::i;:::-;;;;;;6736:10;;6728:50;;;;562:18101;6360:74;562:18101;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6360:74;;;;5584:97;562:18101;;;;5649:21;;;;562:18101;5649:21;;562:18101;5649:21;5219:154;562:18101;;;;;;;;;5031:128;5110:12;4487:14;4891:12;;5110;:::i;:::-;562:18101;;5096:52;;;562:18101;;;;;5096:52;;562:18101;;;;;;;;;5096:52;4887:88;4891:12;4946;4487:14;4891:12;;4946;:::i;:::-;562:18101;;4933:31;;;562:18101;;;;;4933:31;;562:18101;;;;;;;;;5096:52;4776:63;;;;;;4633:77;562:18101;;;4680:19;;;;4179:117;562:18101;;4281:3;562:18101;;;;4250:35;;;;562:18101;;4250:35;;562:18101;;;;;4250:35;562:18101;;;;;-1:-1:-1;;562:18101:29;;;;;;;:::i;:::-;2120:602:1;562:18101:29;2120:602:1;562:18101:29;;2120:602:1;562:18101:29;;2120:602:1;;;;;2731:1;;;:::i;562:18101:29:-;;;;;-1:-1:-1;;562:18101:29;;;;;;;;:::i;:::-;;:::i;:::-;;;;;-1:-1:-1;;562:18101:29;;;;;;;825:31;562:18101;;;;;;;;;;;;;;;-1:-1:-1;;562:18101:29;;;;;12049:4;;:::i;:::-;562:18101;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;562:18101:29;;;;;2040:595:4;562:18101:29;2040:595:4;;;;562:18101:29;;2040:595:4;;;;;562:18101:29;10680:18;;:::i;:::-;562:18101;;10720:8;;562:18101;;2040:595:4;562:18101:29;;;;;;;;;;;;-1:-1:-1;;562:18101:29;;;;;2040:595:4;562:18101:29;2040:595:4;;;;562:18101:29;;2040:595:4;;;;;;562:18101:29;13342:18;;:::i;:::-;562:18101;;;;;;;;;;;;;-1:-1:-1;;562:18101:29;;;;;;;;:::i;:::-;;;;4449:5:4;562:18101:29;;;;;;;;;;;;;;;;;;-1:-1:-1;;562:18101:29;;;;;;;;1098:28:28;562:18101:29;;;;;;;;-1:-1:-1;;562:18101:29;;;;;;;;:::i;:::-;;;;;-1:-1:-1;;562:18101:29;;;;;;;;:::i;:::-;;;;3369:5:4;562:18101:29;;;3383:1:4;562:18101:29;;;;3369:15:4;562:18101:29;;;;;;;;;;;-1:-1:-1;;562:18101:29;;;;;2040:595:4;562:18101:29;2040:595:4;;;;562:18101:29;;2040:595:4;;;;;11311:18:29;;:::i;:::-;562:18101;;;;11347:17;;562:18101;;;2040:595:4;562:18101:29;11397:12;;562:18101;;;;;;;2040:595:4;562:18101:29;;;;;;;;;-1:-1:-1;;562:18101:29;;;;;;;;820:2:28;562:18101:29;;;;;;;;-1:-1:-1;;562:18101:29;;;;;;;:::i;:::-;;;;9565:3:28;562:18101:29;;;;;;;;;;;;;;9615:8:28;562:18101:29;;:::i;:::-;;;3056:6:31;;;562:18101:29;3047:15:31;3046:22;9649:21:28;:50;;;;562:18101:29;;;;;;;;;;;9649:50:28;562:18101:29;;;;;;;;2524:97:31;;;9674:25:28;9649:50;;;;562:18101:29;;;;:::i;:::-;;;;;;;;-1:-1:-1;;562:18101:29;;;;;4014:13:1;4000:35;562:18101:29;;4000:35:1;:::i;:::-;562:18101:29;;;4128:6:1;4014:13;;;4123:217;4431:71;;;562:18101:29;;;;4431:71:1;562:18101:29;;:::i;4158:3:1:-;562:18101:29;;4136:20:1;;;;;4158:3;4238:16;;;;;:::i;:::-;562:18101:29;;;;;;;;;;;;;;;;;;;;4231:29:1;4227:103;;4158:3;;;;:::i;:::-;4128:6;;;4227:103;4299:16;;;:::i;:::-;562:18101:29;;;;;;4280:35:1;4290:5;;;:::i;:::-;4280:35;;;:::i;:::-;562:18101:29;4227:103:1;;;;4136:20;;;562:18101:29;;;;;-1:-1:-1;;562:18101:29;;;;;2040:595:4;562:18101:29;2040:595:4;;;;562:18101:29;;2040:595:4;;;;;562:18101:29;;10993:18;;:::i;:::-;562:18101;;;;11033:8;;;;562:18101;;2040:595:4;562:18101:29;;;;;;;;;-1:-1:-1;;562:18101:29;;;;;;;;:::i;:::-;;;;;;;;3554:1:1;562:18101:29;;;;3539:16:1;562:18101:29;;;;;;;;;;-1:-1:-1;;562:18101:29;;;;;;;;-1:-1:-1;562:18101:29;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;2120:602:1;562:18101:29;2120:602:1;562:18101:29;;2120:602:1;562:18101:29;;2120:602:1;;;;;562:18101:29;10927:24:28;;;;:::i;:::-;562:18101:29;;;;;;;;;;;;:::i;:::-;4183:19;562:18101;4183:19;;;562:18101;4183:19;;;;:::i;:::-;:44;;;4179:117;;562:18101;;;:::i;:::-;;4487:14;;;;562:18101;;4504:17;562:18101;4487:34;562:18101;;;;;;;;4487:34;;:::i;:::-;4532:15;;562:18101;;;;;4487:61;4633:77;;562:18101;4487:14;;;;;562:18101;;;;;;;;4776:30;;;:63;:30;;;:63;;4891:12;4487:14;4891:12;;;:::i;:::-;562:18101;;;;;;;4891:19;;4887:88;;4891:12;;562:18101;5035:12;4487:14;4891:12;;5035;:::i;:::-;562:18101;5035:38;5031:128;;5242:46;;;;;:::i;:::-;562:18101;;;;;;;;;;;;;;;;;;;;4487:14;562:18101;;;;;;;5327:11;;;562:18101;;;;;5352:11;;562:18101;;;;;-1:-1:-1;5219:154:29;;;4487:14;;562:18101;;;5219:154;;;;;562:18101;5219:154;562:18101;;;;;;;;;;5468:3;562:18101;;;;;;;;;;;;;;;5588:8;562:18101;;:::i;:::-;2524:97:31;;5588:38:29;5584:97;;6005:22;562:18101;6005:22;4487:14;6005:22;;;;562:18101;;;6005:22;5916:178;;6728:50;6005:22;;6053:19;562:18101;6005:22;;;;;;:::i;:::-;6053:19;;;:::i;:::-;562:18101;;;;;5916:178;;;;;;;562:18101;;5916:178;;:::i;:::-;562:18101;5885:227;;562:18101;;;;;;;;;;;;;4487:14;;;;562:18101;;6364:21;;;6360:74;;4776:63;562:18101;;;6527:12;;;;;:::i;:::-;562:18101;4377:11;562:18101;;;4532:15;562:18101;;4532:15;562:18101;;;;;;;;;6700:12;4891;;;6700;:::i;:::-;562:18101;;;;;;;;;;;;;;;;;;;;;;;;;:::i;6360:74::-;562:18101;;;;;;;;;;;;;;;;;6360:74;;;;5584:97;562:18101;;;;5649:21;;;;562:18101;5649:21;;562:18101;5649:21;5031:128;5110:12;4487:14;4891:12;;5110;:::i;4887:88::-;4946:12;4487:14;4891:12;;4946;:::i;4776:63::-;;;;;4179:117;562:18101;;;4281:3;562:18101;;4250:35;;;562:18101;;4250:35;;562:18101;;;;;4250:35;562:18101;-1:-1:-1;;562:18101:29;;;;;;;;;;-1:-1:-1;562:18101:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;-1:-1:-1;;562:18101:29;;;;;;;;;-1:-1:-1;562:18101:29;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;:::i;:::-;;;2144:18;;:22;:18;;:::i;:::-;:22;562:18101;;2218:12;2144:22;2218:12;;;;;;;;:::i;:::-;562:18101;2218:19;;2214:88;;2362:12;;;;;:::i;:::-;2384:15;;562:18101;;;;;2362:38;2358:128;;562:18101;;;;;;;;2661:102;562:18101;2698:30;562:18101;;;;;:::i;:::-;;;;:::i;:::-;;;;;;:::i;:::-;2144:22;562:18101;;;2698:30;:::i;2661:102::-;2777:3;;2773:46;;562:18101;3031:12;3017;562:18101;2922:12;;;;;:::i;:::-;562:18101;;;2384:15;562:18101;;2384:15;562:18101;;;;;;;;;3017:12;:::i;:::-;3031;;:::i;:::-;562:18101;;;;;;;;2144:22;562:18101;;;2999:45;562:18101;3005:10;2999:45;;562:18101;2773:46;2144:22;13515:145:28;;;;;2358:128:29;2437:12;;;:::i;2214:88::-;2273:12;;;:::i;562:18101::-;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;562:18101:29;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;;;;;;;;:::o;857:203:28:-;562:18101:29;;926:128:28;;;562:18101:29;;;;1004:40:28;562:18101:29;;;;;926:128:28;;;;;:::i;:::-;562:18101:29;907:153:28;;857:203;:::o;562:18101:29:-;4014:13:1;562:18101:29;;;;;;4014:13:1;-1:-1:-1;562:18101:29;;;;-1:-1:-1;562:18101:29;:::o;:::-;3837:12:4;562:18101:29;;;;;;3837:12:4;-1:-1:-1;562:18101:29;;;;-1:-1:-1;562:18101:29;:::o;3057:188:1:-;562:18101:29;;;;3112:6:1;;562:18101:29;;;;;;;3127:1:1;562:18101:29;;;;3112:16:1;3108:29;;3127:1;562:18101:29;;;;3127:1:1;562:18101:29;;;;;;;;;3127:1:1;562:18101:29;;;3127:1:1;562:18101:29;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;3210:28:1;3222:10;562:18101:29;;;3210:28:1;3057:188::o;562:18101:29:-;;;;;;;;;;3108:29:1;3130:7;;;:::o;3277:157::-;562:18101:29;;3332:6:1;562:18101:29;;;;;;;;;;3332:16:1;3328:29;;562:18101:29;;;;;3397:30:1;3411:10;562:18101:29;;;3397:30:1;3277:157::o;3328:29::-;3350:7;;:::o;562:18101:29:-;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;-1:-1:-1;;562:18101:29;;;;:::i;:::-;;;;;;;;:::o;:::-;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;:::o;2858:202:4:-;2120:602:1;;;;;;;;;;;;;;562:18101:29;;;;;;;;2930:5:4;2120:602:1;562:18101:29;2944:1:4;2120:602:1;562:18101:29;;;2930:15:4;2926:28;;2944:1;2120:602:1;562:18101:29;;;2988:12:4;562:18101:29;;;;;;;;;2944:1:4;562:18101:29;;;2988:12:4;562:18101:29;;:::i;:::-;;;;;;2988:12:4;562:18101:29;;;;;;;;;;;;;;;3025:28:4;2120:602:1;562:18101:29;2120:602:1;562:18101:29;3025:28:4;2858:202::o;2120:602:1:-;;;;;;;;;;3092:172:4;2120:602:1;;;;;;;;;;;;;;;562:18101:29;;;;;;3164:5:4;2120:602:1;562:18101:29;2120:602:1;562:18101:29;;;3164:15:4;3160:28;;562:18101:29;2120:602:1;562:18101:29;;;3227:30:4;2120:602:1;562:18101:29;2120:602:1;562:18101:29;3227:30:4;3092:172::o;2120:602:1:-;;;;;;;;;;3475:331:28;3761:12;562:18101:29;;3761:12:28;;562:18101:29;;;;3725:49:28;562:18101:29;3761:12:28;3725:49;;3742:3;;562:18101:29;;;;;;;;;;;;;;;;3725:49:28;;;;;;:::i;:::-;562:18101:29;3715:60:28;;562:18101:29;;3761:12:28;3629:160;;562:18101:29;;;;;;;;;3629:160:28;;;;;:::i;562:18101:29:-;;;;;;;:::i;:::-;-1:-1:-1;562:18101:29;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;4245:2706:28:-;;-1:-1:-1;562:18101:29;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;5068:19:28;562:18101:29;5068:19:28;;;;:::i;:::-;562:18101:29;;;;5123:3:28;562:18101:29;;5108:18:28;;;5104:109;;5068:19;5308;562:18101:29;5068:19:28;;5308;;:::i;:::-;562:18101:29;;;5308:22:28;562:18101:29;;;;;;;;;;;;;;;5354:8:28;562:18101:29;;:::i;:::-;;;;;3056:6:31;;562:18101:29;3047:15:31;3046:22;5417:98:28;;562:18101:29;;;;;;;;:::i;:::-;;;;;3056:6:31;;562:18101:29;;;;;;;:::i;:::-;;;;2849:43:31;;562:18101:29;;;2849:43:31;;562:18101:29;;5725:11:28;562:18101:29;;;5738:15:28;;;;;;;5823:19;562:18101:29;5068:19:28;;5823;;:::i;:::-;562:18101:29;;;;;;;;;;;5823:22:28;562:18101:29;;;;;;;;;;;5823:22:28;562:18101:29;;;5354:8:28;562:18101:29;;:::i;:::-;;;;;3056:6:31;;562:18101:29;3047:15:31;3046:22;5944:106:28;;562:18101:29;;;;5823:22:28;562:18101:29;;;6117:21:28;;6113:117;;562:18101:29;;;;;;;;5823:22:28;562:18101:29;;;6279:20:28;562:18101:29;;;;;2849:43:31;;562:18101:29;;2849:43:31;;562:18101:29;2849:43:31;;562:18101:29;996:66:31;7319:18;;;;;;;;;;;;;;;;;;;562:18101:29;;;;;7782:21:31;562:18101:29;;;7775:42:31;;7928:16;;;562:18101:29;7928:16:31;;;;;8003:18;;;;;;8010:1;8003:18;8681:17;;;;562:18101:29;;;;;;8828:22:31;8674:48;;;8815:40;2849:43;;562:18101:29;8923:17:31;8997:16;;3056:6;;562:18101:29;9579:1:31;;562:18101:29;;;;9617:20:31;;9605:37;;9598:58;9555:135;;10391:16;;;;;;;562:18101:29;;;;;10443:22:31;10312:16;;;;10431:39;562:18101:29;;;11264:17:31;11254:32;;562:18101:29;;;;;11191:26:31;;11181:41;;11310:23;562:18101:29;2849:43:31;;562:18101:29;;;5725:11:28;;6113:117;562:18101:29;;;;;;;;;;;;14301:93:28;14337:39;562:18101:29;14301:93:28;;;;562:18101:29;;5823:22:28;562:18101:29;;14301:93:28;;;562:18101:29;14301:93:28;;;;;:::i;:::-;-1:-1:-1;6163:52:28;;:::o;5944:106::-;562:18101:29;;;;;;;;;6007:27:28;562:18101:29;;;5823:22:28;562:18101:29;;6007:27:28;:::i;:::-;-1:-1:-1;;5992:43:28:o;5738:15::-;;;;;;;;-1:-1:-1;562:18101:29;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;-1:-1:-1;562:18101:29;;;;;-1:-1:-1;562:18101:29;;;2849:43:31;;562:18101:29;11972:6:31;-1:-1:-1;12004:13:31;562:18101:29;12027:11:31;;996:66;12049:1389;;;;4654:22;;;;996:66;4654:22;;562:18101:29;4654:22:31;;6653:114:28;4654:22:31;;;562:18101:29;;;;;4829:26:31;562:18101:29;;2849:43:31;562:18101:29;5023:24:31;;5008:44;;562:18101:29;;6735:22:28;562:18101:29;6735:22:28;;;:::i;:::-;562:18101:29;;6653:114:28;;:::i;:::-;6781:3;6777:81;;562:18101:29;;;;;;;;;;;;;;;;-1:-1:-1;562:18101:29;;;;;;;;;;6917:27:28;4245:2706;:::o;6777:81::-;562:18101:29;;14530:64:28;14553:40;562:18101:29;14530:64:28;;;8010:1:31;14530:64:28;;562:18101:29;;;;;;;;;;;;;;-1:-1:-1;6800:47:28;;:::o;12049:1389:31:-;996:66;12049:1389;;;;;;;;;;;;;;;;;;;;;;;;;5417:98:28;562:18101:29;;;;;5476:27:28;562:18101:29;;;;5476:27:28;:::i;5104:109::-;562:18101:29;;13861:30:28;562:18101:29;13838:65:28;;;562:18101:29;;;;13838:65:28;;;562:18101:29;;;;;;;;;;;13838:65:28;;562:18101:29;-1:-1:-1;13838:65:28;;-1:-1:-1;562:18101:29;;13838:65:28;:::i;11478:936::-;2524:97:31;;;;;;;;;;;11775:23:28;11837:11;11752:131;11775:23;;:::i;:::-;562:18101:29;;11837:11:28;;;562:18101:29;11862:11:28;;;562:18101:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;11752:131:28;-1:-1:-1;;11752:131:28;;;;;;;;;;;;;;;;;562:18101:29;11901:17:28;;562:18101:29;;;;11974:3:28;562:18101:29;;;;;;;;;;;;;;12025:8:28;562:18101:29;;;;;;:::i;:::-;;;11837:11:28;3056:6:31;;562:18101:29;3047:15:31;11837:11:28;;562:18101:29;;11837:11:28;562:18101:29;;;12137:28:28;562:18101:29;;;;;;;;;;;12148:10:28;562:18101:29;;12137:28:28;11478:936;:::o;12051:333::-;2524:97:31;;;;;;;;;12345:27:28;562:18101:29;;12051:333:28;11478:936;:::o;562:18101:29:-;;;;;;;;;;;;;;;;;11752:131:28;562:18101:29;;;;;;;;;;;13916:245:28;562:18101:29;;;14092:62:28;14115:30;14092:62;;;;562:18101:29;14092:62:28;;;562:18101:29;14092:62:28;;;;;;:::i;:::-;13916:245;:::o;562:18101:29:-;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;;;;:::i;:::-;4377:11;562:18101;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;562:18101:29;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;562:18101:29;;;;;;;;;;;;;;;;;-1:-1:-1;562:18101:29;;;;;;;;;;;:::o;9742:638::-;;10048:283;562:18101;;9742:638;10157:12;;10119;;;:::i;:::-;10157;;;:::i;:::-;10242:22;562:18101;10242:22;10157:12;10242:22;;;:::i;:::-;10290:19;;;;;;;:::i;:::-;562:18101;;;;;10290:19;562:18101;10048:283;;;562:18101;10157:12;10048:283;;10090:3;;;562:18101;;;;;10290:19;562:18101;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;562:18101:29;;;;10048:283;;;;;;;;;:::i;2014:638:4:-;2040:595;562:18101:29;2040:595:4;;;;;562:18101:29;2040:595:4;;;;;562:18101:29;12080:18;;:::i;:::-;562:18101;;12125:8;;;;2014:638:4;:::o;13402:582:29:-;-1:-1:-1;562:18101:29;;;;;;:::i;:::-;;;;;;;;;;;:::i;:::-;13552:9;562:18101;;;;;;;;;;;;;;;;;;;;;:::i;:::-;13713:14;562:18101;13713:14;;562:18101;13713:34;562:18101;;;;;13730:17;562:18101;;;;13713:34;;:::i;:::-;13758:15;;;562:18101;;;13713:61;;13836:52;;;;13402:582;13832:146;;;;;;;13904:17;;:::o;13832:146::-;13952:15;;;:::o;13836:52::-;562:18101;;;;;;;;;-1:-1:-1;13836:52:29;;;;;14594:146;;562:18101;;;;;;;;;12910:8:28;562:18101:29;;;;;:::i;:::-;;;;3056:6:31;;;562:18101:29;3047:15:31;12936:134:28;;14594:146:29;14695:6;;;;;;:::i;:::-;14594:146::o;12936:134:28:-;562:18101:29;-1:-1:-1;562:18101:29;;2524:97:31;562:18101:29;;;;2524:97:31;;;;;;562:18101:29;;13020:39:28;-1:-1:-1;2524:97:31;562:18101:29;13020:39:28;12936:134;;;;;;15625:2083:29;15727:4;15719:25;:30;15715:43;;562:18101;;:::i;:::-;16161:14;;;562:18101;;16161:34;562:18101;;;;;16178:17;562:18101;;;;16161:34;;:::i;:::-;16206:15;;;562:18101;;;16161:61;;;16796:53;;;15625:2083;16792:106;;15625:2083;17020:97;;15625:2083;562:18101;;;15748:1;15839:11;562:18101;17662:9;562:18101;;;16206:15;562:18101;;;;;;17662:9;562:18101;15625:2083::o;17020:97::-;562:18101;;;;;;;;;;16161:14;562:18101;;;17065:41;562:18101;17083:10;17065:41;;17020:97;;;;;16792:106;562:18101;;;;16865:22;562:18101;;;;;;;;;;;;;16865:22;562:18101;16792:106;;16796:53;562:18101;;;;;;16836:9;562:18101;;;;-1:-1:-1;16796:53:29;;17797:183;17924:18;562:18101;17876:21;17914:28;;;;;;:59;17797:183;:::o;537:3004:30:-;;;;793:14;;:42;;;;537:3004;789:85;;3388:6:31;;;562:18101:29;;;;;996:66:31;;3532:26;954:1;3532:26;;;;;3517:46;;3510:62;3381:26;;;;3590:13;1129:62:30;;1185:66:31;1619:35:30;;;;;1615:78;;562:18101:29;;;1836:110:30;3388:6:31;1836:110:30;;562:18101:29;;;;;;;;;;;;;;;;;;;;;;;1836:110:30;;;562:18101:29;;;;;;;;;;;;;2723:2:30;806:1;562:18101:29;;;3944:1:31;562:18101:29;;3297:61:30;562:18101:29;3297:61:30;562:18101:29;3388:6:31;562:18101:29;;;;;;1809:151:30;;562:18101:29;;;;;;;;;3935:10:31;562:18101:29;3148:45:30;;562:18101:29;;;;2440:51:30;;562:18101:29;;;;;;;;;;;;;;;;;;;;;;;3297:61:30;;;;;;;3944:1:31;3297:61:30;;;;;562:18101:29;3297:61:30;806:1;3297:61;562:18101:29;;;3511:23:30;537:3004;:::o;1615:78::-;1670:12;;;;;;;;806:1;1670:12;:::o;1129:62::-;1168:12;;;;;;;806:1;1168:12;:::o;789:85::-;851:12;;;;806:1;851:12;:::o;793:42::-;562:18101:29;;;;811:24:30;793:42;", + "linkReferences": {}, + "immutableReferences": { + "43936": [ + { + "start": 5908, + "length": 32 + }, + { + "start": 9689, + "length": 32 + }, + { + "start": 11977, + "length": 32 + } + ] + } + }, + "methodIdentifiers": { + "authed()": "224242ca", + "authed(address)": "0fce3415", + "bar()": "febb0f7e", + "bud(address)": "4fce7a2a", + "challengeReward()": "3ea0c15e", + "constructOpPokeMessage((uint128,uint32),(bytes32,address,bytes))": "8b0b044c", + "constructPokeMessage((uint128,uint32))": "acf40b6f", + "decimals()": "313ce567", + "deny(address)": "9c52a7f1", + "diss(address)": "65c4ce7a", + "drop(uint8)": "c83c6334", + "drop(uint8[])": "b259da5c", + "feedRegistrationMessage()": "d0a5882a", + "feeds()": "d63605b8", + "feeds(address)": "2fba4aa9", + "feeds(uint8)": "789d8191", + "isAcceptableSchnorrSignatureNow(bytes32,(bytes32,address,bytes))": "dac42ad8", + "kiss(address)": "f29c29c4", + "latestAnswer()": "50d25bcd", + "latestRoundData()": "feaf968c", + "lift((uint256,uint256),(uint8,bytes32,bytes32))": "0760861b", + "lift((uint256,uint256)[],(uint8,bytes32,bytes32)[])": "bfe5861f", + "maxChallengeReward()": "9fd001f6", + "opChallenge((bytes32,address,bytes))": "8928a1f8", + "opChallengePeriod()": "646edb68", + "opFeedId()": "b9ee3fc1", + "opPoke((uint128,uint32),(bytes32,address,bytes),(uint8,bytes32,bytes32))": "6712af9e", + "opPoke_optimized_397084999((uint128,uint32),(bytes32,address,bytes),(uint8,bytes32,bytes32))": "00000000", + "peek()": "59e02dd7", + "peep()": "0e5a6c70", + "poke((uint128,uint32),(bytes32,address,bytes))": "2f529d73", + "poke_optimized_7136211((uint128,uint32),(bytes32,address,bytes))": "00000082", + "read()": "57de26a4", + "readWithAge()": "393e5ede", + "rely(address)": "65fae35e", + "setBar(uint8)": "fe663495", + "setMaxChallengeReward(uint256)": "ab06ee16", + "setOpChallengePeriod(uint16)": "9dadc886", + "tolled()": "9954b0dc", + "tolled(address)": "3bee58f9", + "tryRead()": "10b07b71", + "tryReadWithAge()": "ceed3ef2", + "wards(address)": "bf353dbb", + "wat()": "4ca29923" + }, + "rawMetadata": "{\"compiler\":{\"version\":\"0.8.16+commit.07a7930e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"initialAuthed\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"wat_\",\"type\":\"bytes32\"}],\"stateMutability\":\"payable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"numberSigners\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"bar\",\"type\":\"uint8\"}],\"name\":\"BarNotReached\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"feedId\",\"type\":\"uint8\"}],\"name\":\"DoubleSigningAttempted\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"givenAge\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"currentTimestamp\",\"type\":\"uint32\"}],\"name\":\"FutureMessage\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InChallengePeriod\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"feedId\",\"type\":\"uint8\"}],\"name\":\"InvalidFeedId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoOpPokeToChallenge\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"NotAuthorized\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"NotTolled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint160\",\"name\":\"gotHash\",\"type\":\"uint160\"},{\"internalType\":\"uint160\",\"name\":\"wantHash\",\"type\":\"uint160\"}],\"name\":\"SchnorrDataMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SchnorrSignatureInvalid\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"SignerNotFeed\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"givenAge\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"currentAge\",\"type\":\"uint32\"}],\"name\":\"StaleMessage\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"who\",\"type\":\"address\"}],\"name\":\"AuthGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"who\",\"type\":\"address\"}],\"name\":\"AuthRenounced\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"oldBar\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"newBar\",\"type\":\"uint8\"}],\"name\":\"BarUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"feed\",\"type\":\"address\"}],\"name\":\"FeedDropped\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"feed\",\"type\":\"address\"}],\"name\":\"FeedLifted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldMaxChallengeReward\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newMaxChallengeReward\",\"type\":\"uint256\"}],\"name\":\"MaxChallengeRewardUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"oldOpChallengePeriod\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"newOpChallengePeriod\",\"type\":\"uint16\"}],\"name\":\"OpChallengePeriodUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"challenger\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"signature\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"commitment\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"feedIds\",\"type\":\"bytes\"}],\"indexed\":false,\"internalType\":\"struct IScribe.SchnorrData\",\"name\":\"schnorrData\",\"type\":\"tuple\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"reward\",\"type\":\"uint256\"}],\"name\":\"OpChallengeRewardPaid\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"signature\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"commitment\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"feedIds\",\"type\":\"bytes\"}],\"indexed\":false,\"internalType\":\"struct IScribe.SchnorrData\",\"name\":\"schnorrData\",\"type\":\"tuple\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"schnorrErr\",\"type\":\"bytes\"}],\"name\":\"OpPokeChallengedSuccessfully\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"signature\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"commitment\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"feedIds\",\"type\":\"bytes\"}],\"indexed\":false,\"internalType\":\"struct IScribe.SchnorrData\",\"name\":\"schnorrData\",\"type\":\"tuple\"}],\"name\":\"OpPokeChallengedUnsuccessfully\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint128\",\"name\":\"val\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"age\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"struct IScribe.PokeData\",\"name\":\"pokeData\",\"type\":\"tuple\"}],\"name\":\"OpPokeDataDropped\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"opFeed\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"signature\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"commitment\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"feedIds\",\"type\":\"bytes\"}],\"indexed\":false,\"internalType\":\"struct IScribe.SchnorrData\",\"name\":\"schnorrData\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint128\",\"name\":\"val\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"age\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"struct IScribe.PokeData\",\"name\":\"pokeData\",\"type\":\"tuple\"}],\"name\":\"OpPoked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"val\",\"type\":\"uint128\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"age\",\"type\":\"uint32\"}],\"name\":\"Poked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"who\",\"type\":\"address\"}],\"name\":\"TollGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"who\",\"type\":\"address\"}],\"name\":\"TollRenounced\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"who\",\"type\":\"address\"}],\"name\":\"authed\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"authed\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"bar\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"who\",\"type\":\"address\"}],\"name\":\"bud\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"challengeReward\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"val\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"age\",\"type\":\"uint32\"}],\"internalType\":\"struct IScribe.PokeData\",\"name\":\"pokeData\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"signature\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"commitment\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"feedIds\",\"type\":\"bytes\"}],\"internalType\":\"struct IScribe.SchnorrData\",\"name\":\"schnorrData\",\"type\":\"tuple\"}],\"name\":\"constructOpPokeMessage\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"val\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"age\",\"type\":\"uint32\"}],\"internalType\":\"struct IScribe.PokeData\",\"name\":\"pokeData\",\"type\":\"tuple\"}],\"name\":\"constructPokeMessage\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"who\",\"type\":\"address\"}],\"name\":\"deny\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"who\",\"type\":\"address\"}],\"name\":\"diss\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8[]\",\"name\":\"feedIds\",\"type\":\"uint8[]\"}],\"name\":\"drop\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"feedId\",\"type\":\"uint8\"}],\"name\":\"drop\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"feedRegistrationMessage\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"who\",\"type\":\"address\"}],\"name\":\"feeds\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"feedId\",\"type\":\"uint8\"}],\"name\":\"feeds\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"feeds\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"message\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"signature\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"commitment\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"feedIds\",\"type\":\"bytes\"}],\"internalType\":\"struct IScribe.SchnorrData\",\"name\":\"schnorrData\",\"type\":\"tuple\"}],\"name\":\"isAcceptableSchnorrSignatureNow\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"who\",\"type\":\"address\"}],\"name\":\"kiss\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestAnswer\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestRoundData\",\"outputs\":[{\"internalType\":\"uint80\",\"name\":\"roundId\",\"type\":\"uint80\"},{\"internalType\":\"int256\",\"name\":\"answer\",\"type\":\"int256\"},{\"internalType\":\"uint256\",\"name\":\"startedAt\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"updatedAt\",\"type\":\"uint256\"},{\"internalType\":\"uint80\",\"name\":\"answeredInRound\",\"type\":\"uint80\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"y\",\"type\":\"uint256\"}],\"internalType\":\"struct LibSecp256k1.Point\",\"name\":\"pubKey\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"internalType\":\"struct IScribe.ECDSAData\",\"name\":\"ecdsaData\",\"type\":\"tuple\"}],\"name\":\"lift\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"y\",\"type\":\"uint256\"}],\"internalType\":\"struct LibSecp256k1.Point[]\",\"name\":\"pubKeys\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"internalType\":\"struct IScribe.ECDSAData[]\",\"name\":\"ecdsaDatas\",\"type\":\"tuple[]\"}],\"name\":\"lift\",\"outputs\":[{\"internalType\":\"uint8[]\",\"name\":\"\",\"type\":\"uint8[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"maxChallengeReward\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"signature\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"commitment\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"feedIds\",\"type\":\"bytes\"}],\"internalType\":\"struct IScribe.SchnorrData\",\"name\":\"schnorrData\",\"type\":\"tuple\"}],\"name\":\"opChallenge\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"opChallengePeriod\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"opFeedId\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"val\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"age\",\"type\":\"uint32\"}],\"internalType\":\"struct IScribe.PokeData\",\"name\":\"pokeData\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"signature\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"commitment\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"feedIds\",\"type\":\"bytes\"}],\"internalType\":\"struct IScribe.SchnorrData\",\"name\":\"schnorrData\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"internalType\":\"struct IScribe.ECDSAData\",\"name\":\"ecdsaData\",\"type\":\"tuple\"}],\"name\":\"opPoke\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"val\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"age\",\"type\":\"uint32\"}],\"internalType\":\"struct IScribe.PokeData\",\"name\":\"pokeData\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"signature\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"commitment\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"feedIds\",\"type\":\"bytes\"}],\"internalType\":\"struct IScribe.SchnorrData\",\"name\":\"schnorrData\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"internalType\":\"struct IScribe.ECDSAData\",\"name\":\"ecdsaData\",\"type\":\"tuple\"}],\"name\":\"opPoke_optimized_397084999\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"peek\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"peep\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"val\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"age\",\"type\":\"uint32\"}],\"internalType\":\"struct IScribe.PokeData\",\"name\":\"pokeData\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"signature\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"commitment\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"feedIds\",\"type\":\"bytes\"}],\"internalType\":\"struct IScribe.SchnorrData\",\"name\":\"schnorrData\",\"type\":\"tuple\"}],\"name\":\"poke\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"val\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"age\",\"type\":\"uint32\"}],\"internalType\":\"struct IScribe.PokeData\",\"name\":\"pokeData\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"signature\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"commitment\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"feedIds\",\"type\":\"bytes\"}],\"internalType\":\"struct IScribe.SchnorrData\",\"name\":\"schnorrData\",\"type\":\"tuple\"}],\"name\":\"poke_optimized_7136211\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"read\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"readWithAge\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"who\",\"type\":\"address\"}],\"name\":\"rely\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"bar_\",\"type\":\"uint8\"}],\"name\":\"setBar\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"maxChallengeReward_\",\"type\":\"uint256\"}],\"name\":\"setMaxChallengeReward\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"opChallengePeriod_\",\"type\":\"uint16\"}],\"name\":\"setOpChallengePeriod\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"who\",\"type\":\"address\"}],\"name\":\"tolled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"tolled\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"tryRead\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"tryReadWithAge\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"who\",\"type\":\"address\"}],\"name\":\"wards\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"wat\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}],\"devdoc\":{\"author\":\"Chronicle Labs, Inc\",\"custom:security-contact\":\"security@chroniclelabs.org\",\"errors\":{\"BarNotReached(uint8,uint8)\":[{\"params\":{\"bar\":\"The bar security parameter.\",\"numberSigners\":\"The number of signers for given Schnorr signature.\"}}],\"DoubleSigningAttempted(uint8)\":[{\"params\":{\"feedId\":\"The id of the feed attempting to double sign.\"}}],\"FutureMessage(uint32,uint32)\":[{\"params\":{\"currentTimestamp\":\"The current time.\",\"givenAge\":\"The poked value's age.\"}}],\"InvalidFeedId(uint8)\":[{\"params\":{\"feedId\":\"The invalid feed id.\"}}],\"NotAuthorized(address)\":[{\"params\":{\"caller\":\"The caller's address.\"}}],\"NotTolled(address)\":[{\"params\":{\"caller\":\"The caller's address.\"}}],\"SchnorrDataMismatch(uint160,uint160)\":[{\"params\":{\"gotHash\":\"The truncated keccak256 hash of the SchnorrData argument.\",\"wantHash\":\"The truncated expected keccak256 hash of the SchnorrData argument.\"}}],\"SignerNotFeed(address)\":[{\"params\":{\"signer\":\"The ECDSA signature's signer.\"}}],\"StaleMessage(uint32,uint32)\":[{\"params\":{\"currentAge\":\"The oracle's current value's age.\",\"givenAge\":\"The poked value's age.\"}}]},\"kind\":\"dev\",\"methods\":{\"authed()\":{\"custom:invariant\":\"Only contains auth'ed addresses. \\u2200x \\u220a authed(): _wards[x] == 1Contains all auth'ed addresses. \\u2200x \\u220a Address: _wards[x] == 1 \\u2192 x \\u220a authed()\",\"details\":\"May contain duplicates.\",\"returns\":{\"_0\":\"List of addresses granted auth.\"}},\"authed(address)\":{\"params\":{\"who\":\"The address to check.\"},\"returns\":{\"_0\":\"True if `who` is auth'ed, false otherwise.\"}},\"bud(address)\":{\"params\":{\"who\":\"The address to check.\"},\"returns\":{\"_0\":\"1 if `who` is tolled, 0 otherwise.\"}},\"challengeReward()\":{\"returns\":{\"_0\":\"The ETH reward for successfully challenging an opPoke.\"}},\"constructOpPokeMessage((uint128,uint32),(bytes32,address,bytes))\":{\"details\":\"The message is defined as: H(tag \\u2016 H(wat \\u2016 pokeData \\u2016 schnorrData)), where H() is the keccak256 function.\",\"params\":{\"pokeData\":\"The pokeData being optimistically poked.\",\"schnorrData\":\"The schnorrData proving `pokeData`'s integrity.\"},\"returns\":{\"_0\":\"Message to be signed for an opPoke for `pokeData` and `schnorrData`.\"}},\"constructPokeMessage((uint128,uint32))\":{\"details\":\"The message is defined as: H(tag \\u2016 H(wat \\u2016 pokeData)), where H() is the keccak256 function.\",\"params\":{\"pokeData\":\"The pokeData to create the message for.\"},\"returns\":{\"_0\":\"Message for `pokeData`.\"}},\"deny(address)\":{\"details\":\"Only callable by auth'ed address.\",\"params\":{\"who\":\"The address to renounce auth.\"}},\"diss(address)\":{\"details\":\"Only callable by auth'ed address.\",\"params\":{\"who\":\"The address to renounce toll.\"}},\"drop(uint8)\":{\"details\":\"Only callable by auth'ed address.\",\"params\":{\"feedId\":\"The feed id to drop.\"}},\"drop(uint8[])\":{\"details\":\"Only callable by auth'ed address.\",\"params\":{\"feedIds\":\"The feed ids to drop.\"}},\"feeds()\":{\"details\":\"Note that this function has a high gas consumption and is not intended to be called onchain.\",\"returns\":{\"_0\":\"List of feed addresses.\"}},\"feeds(address)\":{\"params\":{\"who\":\"The address to check.\"},\"returns\":{\"_0\":\"True if `who` is feed, false otherwise.\"}},\"feeds(uint8)\":{\"params\":{\"feedId\":\"The feed id to check.\"},\"returns\":{\"_0\":\"True if `feedId` is a feed, false otherwise.\",\"_1\":\"Address of the feed with id `feedId` if `feedId` is a feed, zero-address otherwise.\"}},\"isAcceptableSchnorrSignatureNow(bytes32,(bytes32,address,bytes))\":{\"details\":\"Note that a valid Schnorr signature is only acceptable if the signature was signed by exactly bar many feeds. For more info, see `bar()(uint8)` and `feeds()(address[])`.Note that bar and feeds are configurable, meaning a once acceptable Schnorr signature may become unacceptable in the future.\",\"params\":{\"message\":\"The message expected to be signed via `schnorrData`.\",\"schnorrData\":\"The SchnorrData to verify whether it proves the `message`'s integrity.\"},\"returns\":{\"_0\":\"True if Schnorr signature is acceptable, false otherwise.\"}},\"kiss(address)\":{\"details\":\"Only callable by auth'ed address.\",\"params\":{\"who\":\"The address to grant toll.\"}},\"latestAnswer()\":{\"details\":\"Only callable by toll'ed address.\",\"returns\":{\"_0\":\"answer The oracle's latest value.\"}},\"latestRoundData()\":{\"details\":\"Only callable by toll'ed address.\",\"returns\":{\"answer\":\"The oracle's latest value.\",\"answeredInRound\":\"1.\",\"roundId\":\"1.\",\"startedAt\":\"0.\",\"updatedAt\":\"The timestamp of oracle's latest update.\"}},\"lift((uint256,uint256),(uint8,bytes32,bytes32))\":{\"details\":\"Only callable by auth'ed address.The message expected to be signed by `ecdsaData` is defined via `feedRegistrationMessage()(bytes32)`.\",\"params\":{\"ecdsaData\":\"ECDSA signed message by the feed's public key.\",\"pubKey\":\"The public key of the feed.\"},\"returns\":{\"_0\":\"The id of the newly lifted feed.\"}},\"lift((uint256,uint256)[],(uint8,bytes32,bytes32)[])\":{\"details\":\"Only callable by auth'ed address.The message expected to be signed by `ecdsaDatas` is defined via `feedRegistrationMessage()(bytes32)`.\",\"params\":{\"ecdsaDatas\":\"ECDSA signed message by the feeds' public keys.\",\"pubKeys\":\"The public keys of the feeds.\"},\"returns\":{\"_0\":\"List of feed ids of the newly lifted feeds.\"}},\"opChallenge((bytes32,address,bytes))\":{\"details\":\"If opPoke is determined to be invalid, the caller receives an ETH bounty. The bounty is defined via the `challengeReward()(uint)` function.If opPoke is determined to be invalid, the corresponding feed is dropped.\",\"params\":{\"schnorrData\":\"The SchnorrData initially provided via opPoke.\"},\"returns\":{\"_0\":\"True if opPoke declared invalid, false otherwise.\"}},\"opPoke((uint128,uint32),(bytes32,address,bytes),(uint8,bytes32,bytes32))\":{\"details\":\"Expects `pokeData`'s age to be greater than the timestamp of the last successful poke.Expects `pokeData`'s age to not be greater than the current time.Expects `ecdsaData` to be a signature from a feed.Expects `ecdsaData` to prove the integrity of the `pokeData` and `schnorrData`.If the `schnorrData` is proven to be invalid via the opChallenge function, the `ecdsaData` signing feed will be dropped.\",\"params\":{\"ecdsaData\":\"The ECDSAData proving the integrity of the `pokeData` and `schnorrData`.\",\"pokeData\":\"The PokeData being poked.\",\"schnorrData\":\"The SchnorrData optimistically assumed to be proving the `pokeData`'s integrity.\"}},\"opPoke_optimized_397084999((uint128,uint32),(bytes32,address,bytes),(uint8,bytes32,bytes32))\":{\"details\":\"Optimized function selector: 0x00000000. Note that this function is _not_ defined via the IScribeOptimistic interface and one should _not_ depend on it.\"},\"peek()\":{\"details\":\"Only callable by toll'ed address.\",\"returns\":{\"_0\":\"The oracle's current value if it exists, zero otherwise.\",\"_1\":\"True if value exists, false otherwise.\"}},\"peep()\":{\"details\":\"Only callable by toll'ed address.\",\"returns\":{\"_0\":\"The oracle's current value if it exists, zero otherwise.\",\"_1\":\"True if value exists, false otherwise.\"}},\"poke((uint128,uint32),(bytes32,address,bytes))\":{\"details\":\"Expects `pokeData`'s age to be greater than the timestamp of the last successful poke.Expects `pokeData`'s age to not be greater than the current time.Expects `schnorrData` to prove `pokeData`'s integrity. See `isAcceptableSchnorrSignatureNow(bytes32,SchnorrData)(bool)`.\",\"params\":{\"pokeData\":\"The PokeData being poked.\",\"schnorrData\":\"The SchnorrData proving the `pokeData`'s integrity.\"}},\"poke_optimized_7136211((uint128,uint32),(bytes32,address,bytes))\":{\"details\":\"Optimized function selector: 0x00000082. Note that this function is _not_ defined via the IScribe interface and one should _not_ depend on it.\"},\"read()\":{\"details\":\"Only callable by toll'ed address.\",\"returns\":{\"_0\":\"The oracle's current value.\"}},\"readWithAge()\":{\"details\":\"Only callable by toll'ed address.\",\"returns\":{\"_0\":\"The oracle's current value.\",\"_1\":\"The value's age.\"}},\"rely(address)\":{\"details\":\"Only callable by auth'ed address.\",\"params\":{\"who\":\"The address to grant auth.\"}},\"setBar(uint8)\":{\"details\":\"Only callable by auth'ed address.Reverts if `bar` is zero.\",\"params\":{\"bar\":\"The value to update bar to.\"}},\"setMaxChallengeReward(uint256)\":{\"details\":\"Only callable by auth'ed address.\",\"params\":{\"maxChallengeReward\":\"The value to update maxChallengeReward to.\"}},\"setOpChallengePeriod(uint16)\":{\"details\":\"Only callable by auth'ed address.Reverts if opChallengePeriod is zero.Note that evaluating whether an opPoke is finalized happens via the _current_ opChallengePeriod. This means a finalized opPoke is dropped if opChallengePeriod is decreased to a value less than opPoke's age.\",\"params\":{\"opChallengePeriod\":\"The value to update opChallengePeriod to.\"}},\"tolled()\":{\"custom:invariant\":\"Only contains tolled addresses. \\u2200x \\u220a tolled(): _tolled[x]Contains all tolled addresses. \\u2200x \\u220a Address: _tolled[x] == 1 \\u2192 x \\u220a tolled()\",\"details\":\"May contain duplicates.\",\"returns\":{\"_0\":\"List of addresses tolled.\"}},\"tolled(address)\":{\"params\":{\"who\":\"The address to check.\"},\"returns\":{\"_0\":\"True if `who` is tolled, false otherwise.\"}},\"tryRead()\":{\"details\":\"Only callable by toll'ed address.\",\"returns\":{\"_0\":\"True if value exists, false otherwise.\",\"_1\":\"The oracle's current value if it exists, zero otherwise.\"}},\"tryReadWithAge()\":{\"details\":\"Only callable by toll'ed address.\",\"returns\":{\"_0\":\"True if value exists, false otherwise.\",\"_1\":\"The oracle's current value if it exists, zero otherwise.\",\"_2\":\"The value's age if value exists, zero otherwise.\"}},\"wards(address)\":{\"params\":{\"who\":\"The address to check.\"},\"returns\":{\"_0\":\"1 if `who` is auth'ed, 0 otherwise.\"}}},\"stateVariables\":{\"_opPokeData\":{\"details\":\"opScribe's last opPoke'd value and corresponding age.\"},\"_originalOpPokeDataAge\":{\"details\":\"The age of the pokeData provided in last opPoke. Ensures Schnorr signature can be verified after setting pokeData's age to block.timestamp during opPoke.\"},\"_schnorrDataCommitment\":{\"details\":\"The truncated hash of the schnorrData provided in last opPoke. Binds the opFeed to their schnorrData.\"},\"maxChallengeReward\":{\"return\":\"The maxChallengeReward parameter.\",\"returns\":{\"_0\":\"The maxChallengeReward parameter.\"}},\"opChallengePeriod\":{\"return\":\"The opChallengePeriod security parameter.\",\"returns\":{\"_0\":\"The opChallengePeriod security parameter.\"}},\"opFeedId\":{\"return\":\"Feed id of the feed last opPoke'd.\",\"returns\":{\"_0\":\"Feed id of the feed last opPoke'd.\"}}},\"title\":\"ScribeOptimistic\",\"version\":1},\"userdoc\":{\"errors\":{\"BarNotReached(uint8,uint8)\":[{\"notice\":\"Thrown if Schnorr signature not signed by exactly bar many signers.\"}],\"DoubleSigningAttempted(uint8)\":[{\"notice\":\"Thrown if double signing attempted.\"}],\"FutureMessage(uint32,uint32)\":[{\"notice\":\"Thrown if a poked value's age is greater than the current time.\"}],\"InChallengePeriod()\":[{\"notice\":\"Thrown if attempted to opPoke while a previous opPoke is still in challenge period.\"}],\"InvalidFeedId(uint8)\":[{\"notice\":\"Thrown if given feed id invalid.\"}],\"NoOpPokeToChallenge()\":[{\"notice\":\"Thrown if opChallenge called while no challengeable opPoke exists.\"}],\"NotAuthorized(address)\":[{\"notice\":\"Thrown by protected function if caller not auth'ed.\"}],\"NotTolled(address)\":[{\"notice\":\"Thrown by protected function if caller not tolled.\"}],\"SchnorrDataMismatch(uint160,uint160)\":[{\"notice\":\"Thrown if opChallenge called with SchnorrData not matching opPoke's SchnorrData.\"}],\"SchnorrSignatureInvalid()\":[{\"notice\":\"Thrown if Schnorr signature verification failed.\"}],\"SignerNotFeed(address)\":[{\"notice\":\"Thrown if opPoke called with non-feed ECDSA signature.\"}],\"StaleMessage(uint32,uint32)\":[{\"notice\":\"Thrown if a poked value's age is not greater than the oracle's current value's age.\"}]},\"events\":{\"AuthGranted(address,address)\":{\"notice\":\"Emitted when auth granted to address.\"},\"AuthRenounced(address,address)\":{\"notice\":\"Emitted when auth renounced from address.\"},\"BarUpdated(address,uint8,uint8)\":{\"notice\":\"Emitted when bar updated.\"},\"FeedDropped(address,address)\":{\"notice\":\"Emitted when feed dropped.\"},\"FeedLifted(address,address)\":{\"notice\":\"Emitted when new feed lifted.\"},\"MaxChallengeRewardUpdated(address,uint256,uint256)\":{\"notice\":\"Emitted when maxChallengeReward updated.\"},\"OpChallengePeriodUpdated(address,uint16,uint16)\":{\"notice\":\"Emitted when length of opChallengePeriod updated.\"},\"OpChallengeRewardPaid(address,(bytes32,address,bytes),uint256)\":{\"notice\":\"Emitted when ETH reward paid for successfully challenging an opPoke.\"},\"OpPokeChallengedSuccessfully(address,(bytes32,address,bytes),bytes)\":{\"notice\":\"Emitted when successfully challenged an opPoke.\"},\"OpPokeChallengedUnsuccessfully(address,(bytes32,address,bytes))\":{\"notice\":\"Emitted when unsuccessfully challenged an opPoke.\"},\"OpPokeDataDropped(address,(uint128,uint32))\":{\"notice\":\"Emitted when an opPoke dropped.\"},\"OpPoked(address,address,(bytes32,address,bytes),(uint128,uint32))\":{\"notice\":\"Emitted when oracles was successfully opPoked.\"},\"Poked(address,uint128,uint32)\":{\"notice\":\"Emitted when oracle was successfully poked.\"},\"TollGranted(address,address)\":{\"notice\":\"Emitted when toll granted to address.\"},\"TollRenounced(address,address)\":{\"notice\":\"Emitted when toll renounced from address.\"}},\"kind\":\"user\",\"methods\":{\"authed()\":{\"notice\":\"Returns full list of addresses granted auth.\"},\"authed(address)\":{\"notice\":\"Returns whether address `who` is auth'ed.\"},\"bar()\":{\"notice\":\"Returns the bar security parameter.\"},\"bud(address)\":{\"notice\":\"Returns whether address `who` is tolled.\"},\"challengeReward()\":{\"notice\":\"Returns the ETH rewards being paid for successfully challenging an opPoke.\"},\"constructOpPokeMessage((uint128,uint32),(bytes32,address,bytes))\":{\"notice\":\"Returns the message expected to be signed via ECDSA for calling opPoke.\"},\"constructPokeMessage((uint128,uint32))\":{\"notice\":\"Returns the message expected to be signed via Schnorr for `pokeData`.\"},\"decimals()\":{\"notice\":\"Returns the number of decimals of the oracle's value.\"},\"deny(address)\":{\"notice\":\"Renounces address `who`'s auth.\"},\"diss(address)\":{\"notice\":\"Renounces address `who`'s toll.\"},\"drop(uint8)\":{\"notice\":\"Drops feed with id `feedId`.\"},\"drop(uint8[])\":{\"notice\":\"Drops feeds with ids' `feedIds`.\"},\"feedRegistrationMessage()\":{\"notice\":\"Returns the feed registration message.\"},\"feeds()\":{\"notice\":\"Returns list of feed addresses.\"},\"feeds(address)\":{\"notice\":\"Returns whether address `who` is a feed.\"},\"feeds(uint8)\":{\"notice\":\"Returns whether feed id `feedId` is a feed and, if so, the feed's address.\"},\"isAcceptableSchnorrSignatureNow(bytes32,(bytes32,address,bytes))\":{\"notice\":\"Returns whether the Schnorr signature `schnorrData` is currently acceptable for message `message`.\"},\"kiss(address)\":{\"notice\":\"Grants address `who` toll.\"},\"latestAnswer()\":{\"notice\":\"Returns the oracle's latest value.\"},\"latestRoundData()\":{\"notice\":\"Returns the oracle's latest value.\"},\"lift((uint256,uint256),(uint8,bytes32,bytes32))\":{\"notice\":\"Lifts public key `pubKey` to being a feed.\"},\"lift((uint256,uint256)[],(uint8,bytes32,bytes32)[])\":{\"notice\":\"Lifts public keys `pubKeys` to being feeds.\"},\"maxChallengeReward()\":{\"notice\":\"Returns the maxChallengeRewards parameter.\"},\"opChallenge((bytes32,address,bytes))\":{\"notice\":\"Challenges the current challengeable opPoke.\"},\"opChallengePeriod()\":{\"notice\":\"Returns the opChallengePeriod security parameter.\"},\"opFeedId()\":{\"notice\":\"Returns the feed id of the feed last opPoke'd.\"},\"opPoke((uint128,uint32),(bytes32,address,bytes),(uint8,bytes32,bytes32))\":{\"notice\":\"Optimistically pokes the oracle.\"},\"peek()\":{\"notice\":\"Returns the oracle's current value.\"},\"peep()\":{\"notice\":\"Returns the oracle's current value.\"},\"poke((uint128,uint32),(bytes32,address,bytes))\":{\"notice\":\"Pokes the oracle.\"},\"read()\":{\"notice\":\"Returns the oracle's current value.\"},\"readWithAge()\":{\"notice\":\"Returns the oracle's current value and its age.\"},\"rely(address)\":{\"notice\":\"Grants address `who` auth.\"},\"setBar(uint8)\":{\"notice\":\"Updates the bar security parameters to `bar`.\"},\"setMaxChallengeReward(uint256)\":{\"notice\":\"Updates the maxChallengeReward parameter.\"},\"setOpChallengePeriod(uint16)\":{\"notice\":\"Updates the opChallengePeriod security parameter.\"},\"tolled()\":{\"notice\":\"Returns full list of addresses tolled.\"},\"tolled(address)\":{\"notice\":\"Returns whether address `who` is tolled.\"},\"tryRead()\":{\"notice\":\"Returns the oracle's current value.\"},\"tryReadWithAge()\":{\"notice\":\"Returns the oracle's current value and its age.\"},\"wards(address)\":{\"notice\":\"Returns whether address `who` is auth'ed.\"},\"wat()\":{\"notice\":\"Returns the oracle's identifier.\"}},\"notice\":\"Scribe based optimistic Oracle with onchain fault resolution\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/ScribeOptimistic.sol\":\"ScribeOptimistic\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[\":chronicle-std/=lib/chronicle-std/src/\",\":ds-test/=lib/forge-std/lib/ds-test/src/\",\":forge-std/=lib/forge-std/src/\",\"lib/chronicle-std:ds-test/=lib/chronicle-std/lib/forge-std/lib/ds-test/src/\",\"lib/chronicle-std:forge-std/=lib/chronicle-std/lib/forge-std/src/\",\"lib/chronicle-std:src/=lib/chronicle-std/src/\"],\"viaIR\":true},\"sources\":{\"lib/chronicle-std/src/IChronicle.sol\":{\"keccak256\":\"0x12c7c1ed4f56cc317ef6faa03c3a8a04a5227f473c18f3a9a3c59becaf1fafca\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://92655a68ffa472078a2a8d44205abcbecdff23a918e9b06b4a136f46eaedc418\",\"dweb:/ipfs/QmP3Etq6HaAULg6X3GLjuhggGvzMiqfa3sn5LDXrqFiNjX\"]},\"lib/chronicle-std/src/auth/Auth.sol\":{\"keccak256\":\"0x634eb69385999f28623b15a04424a12be66bc2b848c70f5217557bd1ffd20766\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://32680625804f064660cfe2b94fa9a0bd625b3fad5902cf1b074415760c72c55a\",\"dweb:/ipfs/QmQtCDsrArnqWNs4C41zssxyRMc1UYSW3FL96Gdmxs38CG\"]},\"lib/chronicle-std/src/auth/IAuth.sol\":{\"keccak256\":\"0x67e2920d75dc7c394f899fbb7d07e298f96059e8ae5bb3340fb6a5bf2a35624f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://b0e308256c1e2a29f4fccef062475abac367cb973d77df21fd4903f58cb5a731\",\"dweb:/ipfs/QmTipAgzavbPErRXWSGeb1tmKXB36zQUL4SAWJz5MYZwsP\"]},\"lib/chronicle-std/src/toll/IToll.sol\":{\"keccak256\":\"0x138157bd72dcd67d9d6ac30ab9f43575730b18bc0a537eeb7799508fd2fa4df1\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://2f6aabad886cb76dca534fb50225f19dc02e42f3e254eddde9610af463adfc96\",\"dweb:/ipfs/QmawqX98Pg6YdXWpWGfosrZmtMYp9FFE5LXKhWftRjbonA\"]},\"lib/chronicle-std/src/toll/Toll.sol\":{\"keccak256\":\"0xbca48a68e702db0186c43df67bd74915df76bcc30ebc4f3d22d7449fd0963b41\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://41662b21eeab923c0cd05bcdfcbc87d957b1cbc66e81f9c1db6f9f4541ebc542\",\"dweb:/ipfs/QmQqFDKURMZMYtatLLWPKbSugxNmz2MjxSF9dRaaqq5XJX\"]},\"src/IScribe.sol\":{\"keccak256\":\"0x766d3a5dfc4937f30db89151e68d01b9b26585bbfb399aa3f50b58bdfb9c1d5a\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://81b821f53f4745ec704a4d52cc2bf325f27636b85fb66520dd3988ca1860e8e1\",\"dweb:/ipfs/QmQSBoGib9rbBStwJNenU8PjAzai7Zo3c7uCKMXY41eLpj\"]},\"src/IScribeOptimistic.sol\":{\"keccak256\":\"0xe6d69b8fe380c32811e30615994ecc6cf99b705626b63156021a8e86845bf64f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://d10b5515f8b492d843fdd7f525fc74ecfca4e1051028bfc92c40ee737dc84e99\",\"dweb:/ipfs/QmXqmPHLBRaowi9xppLUtPKZUgHeuW1Jc7iDG4b83jAsgr\"]},\"src/Scribe.sol\":{\"keccak256\":\"0xb0c7b1b6a9e5bde23b4f3b3445a3b4959b3c3f98f38033fc3bb73985716b91f7\",\"license\":\"BUSL-1.1\",\"urls\":[\"bzz-raw://13adde6bcaa51ebd8494e6b36e00846429d93180c824d8c895f5f2a3b57cac1a\",\"dweb:/ipfs/Qmf2BfWM5VJ4JJLCCCfBM2wG4t5PtMRgyUhjnegBD1fXwz\"]},\"src/ScribeOptimistic.sol\":{\"keccak256\":\"0x2673daf5c1381e72760acf5a548ed6a84ea669da22b479e83ed9af5e2f29fd6d\",\"license\":\"BUSL-1.1\",\"urls\":[\"bzz-raw://4fcc2b0fc471827fa7cd7e82ff75708ff9361e3f162a9b66305ad8a8fca7e6e0\",\"dweb:/ipfs/QmW5D4A4MdvmgWCeefc7swCdJRnF4BMecEMbaxSTVgPfUN\"]},\"src/libs/LibSchnorr.sol\":{\"keccak256\":\"0xe0a3cc7cbeef80e86318a0f89dff4b05fb303a83fd43634a6ffd35df088fc838\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://f101a3c215ab131fc2a8c35f4d83ff42e8f39a995243760f762d280d171dc4d8\",\"dweb:/ipfs/QmRgoTZeZmnVbvzrHA6vAYDwDHiRQ4Et9nf4NZZeVau4J1\"]},\"src/libs/LibSecp256k1.sol\":{\"keccak256\":\"0x32c5d7f655e8493823a413e1579fdf05dcbc2f9bcaa0dc3da47e269006311245\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://150478306872245cbc9496f1eddf100ae3da3a628fef7a0ad9008e4296a30ba1\",\"dweb:/ipfs/QmYao4oaCihTkmdaSUj82BEDus9ZsEg7ZrTgGFr7H9DtzD\"]}},\"version\":1}", + "metadata": { + "compiler": { + "version": "0.8.16+commit.07a7930e" + }, + "language": "Solidity", + "output": { + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "initialAuthed", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "wat_", + "type": "bytes32" + } + ], + "stateMutability": "payable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "numberSigners", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "bar", + "type": "uint8" + } + ], + "type": "error", + "name": "BarNotReached" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "feedId", + "type": "uint8" + } + ], + "type": "error", + "name": "DoubleSigningAttempted" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "givenAge", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "currentTimestamp", + "type": "uint32" + } + ], + "type": "error", + "name": "FutureMessage" + }, + { + "inputs": [], + "type": "error", + "name": "InChallengePeriod" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "feedId", + "type": "uint8" + } + ], + "type": "error", + "name": "InvalidFeedId" + }, + { + "inputs": [], + "type": "error", + "name": "NoOpPokeToChallenge" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address" + } + ], + "type": "error", + "name": "NotAuthorized" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address" + } + ], + "type": "error", + "name": "NotTolled" + }, + { + "inputs": [ + { + "internalType": "uint160", + "name": "gotHash", + "type": "uint160" + }, + { + "internalType": "uint160", + "name": "wantHash", + "type": "uint160" + } + ], + "type": "error", + "name": "SchnorrDataMismatch" + }, + { + "inputs": [], + "type": "error", + "name": "SchnorrSignatureInvalid" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + } + ], + "type": "error", + "name": "SignerNotFeed" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "givenAge", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "currentAge", + "type": "uint32" + } + ], + "type": "error", + "name": "StaleMessage" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address", + "indexed": true + }, + { + "internalType": "address", + "name": "who", + "type": "address", + "indexed": true + } + ], + "type": "event", + "name": "AuthGranted", + "anonymous": false + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address", + "indexed": true + }, + { + "internalType": "address", + "name": "who", + "type": "address", + "indexed": true + } + ], + "type": "event", + "name": "AuthRenounced", + "anonymous": false + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address", + "indexed": true + }, + { + "internalType": "uint8", + "name": "oldBar", + "type": "uint8", + "indexed": false + }, + { + "internalType": "uint8", + "name": "newBar", + "type": "uint8", + "indexed": false + } + ], + "type": "event", + "name": "BarUpdated", + "anonymous": false + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address", + "indexed": true + }, + { + "internalType": "address", + "name": "feed", + "type": "address", + "indexed": true + } + ], + "type": "event", + "name": "FeedDropped", + "anonymous": false + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address", + "indexed": true + }, + { + "internalType": "address", + "name": "feed", + "type": "address", + "indexed": true + } + ], + "type": "event", + "name": "FeedLifted", + "anonymous": false + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address", + "indexed": true + }, + { + "internalType": "uint256", + "name": "oldMaxChallengeReward", + "type": "uint256", + "indexed": false + }, + { + "internalType": "uint256", + "name": "newMaxChallengeReward", + "type": "uint256", + "indexed": false + } + ], + "type": "event", + "name": "MaxChallengeRewardUpdated", + "anonymous": false + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address", + "indexed": true + }, + { + "internalType": "uint16", + "name": "oldOpChallengePeriod", + "type": "uint16", + "indexed": false + }, + { + "internalType": "uint16", + "name": "newOpChallengePeriod", + "type": "uint16", + "indexed": false + } + ], + "type": "event", + "name": "OpChallengePeriodUpdated", + "anonymous": false + }, + { + "inputs": [ + { + "internalType": "address", + "name": "challenger", + "type": "address", + "indexed": true + }, + { + "internalType": "struct IScribe.SchnorrData", + "name": "schnorrData", + "type": "tuple", + "components": [ + { + "internalType": "bytes32", + "name": "signature", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "commitment", + "type": "address" + }, + { + "internalType": "bytes", + "name": "feedIds", + "type": "bytes" + } + ], + "indexed": false + }, + { + "internalType": "uint256", + "name": "reward", + "type": "uint256", + "indexed": false + } + ], + "type": "event", + "name": "OpChallengeRewardPaid", + "anonymous": false + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address", + "indexed": true + }, + { + "internalType": "struct IScribe.SchnorrData", + "name": "schnorrData", + "type": "tuple", + "components": [ + { + "internalType": "bytes32", + "name": "signature", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "commitment", + "type": "address" + }, + { + "internalType": "bytes", + "name": "feedIds", + "type": "bytes" + } + ], + "indexed": false + }, + { + "internalType": "bytes", + "name": "schnorrErr", + "type": "bytes", + "indexed": false + } + ], + "type": "event", + "name": "OpPokeChallengedSuccessfully", + "anonymous": false + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address", + "indexed": true + }, + { + "internalType": "struct IScribe.SchnorrData", + "name": "schnorrData", + "type": "tuple", + "components": [ + { + "internalType": "bytes32", + "name": "signature", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "commitment", + "type": "address" + }, + { + "internalType": "bytes", + "name": "feedIds", + "type": "bytes" + } + ], + "indexed": false + } + ], + "type": "event", + "name": "OpPokeChallengedUnsuccessfully", + "anonymous": false + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address", + "indexed": true + }, + { + "internalType": "struct IScribe.PokeData", + "name": "pokeData", + "type": "tuple", + "components": [ + { + "internalType": "uint128", + "name": "val", + "type": "uint128" + }, + { + "internalType": "uint32", + "name": "age", + "type": "uint32" + } + ], + "indexed": false + } + ], + "type": "event", + "name": "OpPokeDataDropped", + "anonymous": false + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address", + "indexed": true + }, + { + "internalType": "address", + "name": "opFeed", + "type": "address", + "indexed": true + }, + { + "internalType": "struct IScribe.SchnorrData", + "name": "schnorrData", + "type": "tuple", + "components": [ + { + "internalType": "bytes32", + "name": "signature", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "commitment", + "type": "address" + }, + { + "internalType": "bytes", + "name": "feedIds", + "type": "bytes" + } + ], + "indexed": false + }, + { + "internalType": "struct IScribe.PokeData", + "name": "pokeData", + "type": "tuple", + "components": [ + { + "internalType": "uint128", + "name": "val", + "type": "uint128" + }, + { + "internalType": "uint32", + "name": "age", + "type": "uint32" + } + ], + "indexed": false + } + ], + "type": "event", + "name": "OpPoked", + "anonymous": false + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address", + "indexed": true + }, + { + "internalType": "uint128", + "name": "val", + "type": "uint128", + "indexed": false + }, + { + "internalType": "uint32", + "name": "age", + "type": "uint32", + "indexed": false + } + ], + "type": "event", + "name": "Poked", + "anonymous": false + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address", + "indexed": true + }, + { + "internalType": "address", + "name": "who", + "type": "address", + "indexed": true + } + ], + "type": "event", + "name": "TollGranted", + "anonymous": false + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address", + "indexed": true + }, + { + "internalType": "address", + "name": "who", + "type": "address", + "indexed": true + } + ], + "type": "event", + "name": "TollRenounced", + "anonymous": false + }, + { + "inputs": [ + { + "internalType": "address", + "name": "who", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function", + "name": "authed", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ] + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "authed", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ] + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "bar", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ] + }, + { + "inputs": [ + { + "internalType": "address", + "name": "who", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function", + "name": "bud", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ] + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "challengeReward", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ] + }, + { + "inputs": [ + { + "internalType": "struct IScribe.PokeData", + "name": "pokeData", + "type": "tuple", + "components": [ + { + "internalType": "uint128", + "name": "val", + "type": "uint128" + }, + { + "internalType": "uint32", + "name": "age", + "type": "uint32" + } + ] + }, + { + "internalType": "struct IScribe.SchnorrData", + "name": "schnorrData", + "type": "tuple", + "components": [ + { + "internalType": "bytes32", + "name": "signature", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "commitment", + "type": "address" + }, + { + "internalType": "bytes", + "name": "feedIds", + "type": "bytes" + } + ] + } + ], + "stateMutability": "view", + "type": "function", + "name": "constructOpPokeMessage", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ] + }, + { + "inputs": [ + { + "internalType": "struct IScribe.PokeData", + "name": "pokeData", + "type": "tuple", + "components": [ + { + "internalType": "uint128", + "name": "val", + "type": "uint128" + }, + { + "internalType": "uint32", + "name": "age", + "type": "uint32" + } + ] + } + ], + "stateMutability": "view", + "type": "function", + "name": "constructPokeMessage", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ] + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ] + }, + { + "inputs": [ + { + "internalType": "address", + "name": "who", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function", + "name": "deny" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "who", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function", + "name": "diss" + }, + { + "inputs": [ + { + "internalType": "uint8[]", + "name": "feedIds", + "type": "uint8[]" + } + ], + "stateMutability": "nonpayable", + "type": "function", + "name": "drop" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "feedId", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "function", + "name": "drop" + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "feedRegistrationMessage", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ] + }, + { + "inputs": [ + { + "internalType": "address", + "name": "who", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function", + "name": "feeds", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ] + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "feedId", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function", + "name": "feeds", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ] + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "feeds", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ] + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "message", + "type": "bytes32" + }, + { + "internalType": "struct IScribe.SchnorrData", + "name": "schnorrData", + "type": "tuple", + "components": [ + { + "internalType": "bytes32", + "name": "signature", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "commitment", + "type": "address" + }, + { + "internalType": "bytes", + "name": "feedIds", + "type": "bytes" + } + ] + } + ], + "stateMutability": "view", + "type": "function", + "name": "isAcceptableSchnorrSignatureNow", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ] + }, + { + "inputs": [ + { + "internalType": "address", + "name": "who", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function", + "name": "kiss" + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ] + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ] + }, + { + "inputs": [ + { + "internalType": "struct LibSecp256k1.Point", + "name": "pubKey", + "type": "tuple", + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ] + }, + { + "internalType": "struct IScribe.ECDSAData", + "name": "ecdsaData", + "type": "tuple", + "components": [ + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ] + } + ], + "stateMutability": "nonpayable", + "type": "function", + "name": "lift", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ] + }, + { + "inputs": [ + { + "internalType": "struct LibSecp256k1.Point[]", + "name": "pubKeys", + "type": "tuple[]", + "components": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ] + }, + { + "internalType": "struct IScribe.ECDSAData[]", + "name": "ecdsaDatas", + "type": "tuple[]", + "components": [ + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ] + } + ], + "stateMutability": "nonpayable", + "type": "function", + "name": "lift", + "outputs": [ + { + "internalType": "uint8[]", + "name": "", + "type": "uint8[]" + } + ] + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "maxChallengeReward", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ] + }, + { + "inputs": [ + { + "internalType": "struct IScribe.SchnorrData", + "name": "schnorrData", + "type": "tuple", + "components": [ + { + "internalType": "bytes32", + "name": "signature", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "commitment", + "type": "address" + }, + { + "internalType": "bytes", + "name": "feedIds", + "type": "bytes" + } + ] + } + ], + "stateMutability": "nonpayable", + "type": "function", + "name": "opChallenge", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ] + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "opChallengePeriod", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ] + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "opFeedId", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ] + }, + { + "inputs": [ + { + "internalType": "struct IScribe.PokeData", + "name": "pokeData", + "type": "tuple", + "components": [ + { + "internalType": "uint128", + "name": "val", + "type": "uint128" + }, + { + "internalType": "uint32", + "name": "age", + "type": "uint32" + } + ] + }, + { + "internalType": "struct IScribe.SchnorrData", + "name": "schnorrData", + "type": "tuple", + "components": [ + { + "internalType": "bytes32", + "name": "signature", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "commitment", + "type": "address" + }, + { + "internalType": "bytes", + "name": "feedIds", + "type": "bytes" + } + ] + }, + { + "internalType": "struct IScribe.ECDSAData", + "name": "ecdsaData", + "type": "tuple", + "components": [ + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ] + } + ], + "stateMutability": "nonpayable", + "type": "function", + "name": "opPoke" + }, + { + "inputs": [ + { + "internalType": "struct IScribe.PokeData", + "name": "pokeData", + "type": "tuple", + "components": [ + { + "internalType": "uint128", + "name": "val", + "type": "uint128" + }, + { + "internalType": "uint32", + "name": "age", + "type": "uint32" + } + ] + }, + { + "internalType": "struct IScribe.SchnorrData", + "name": "schnorrData", + "type": "tuple", + "components": [ + { + "internalType": "bytes32", + "name": "signature", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "commitment", + "type": "address" + }, + { + "internalType": "bytes", + "name": "feedIds", + "type": "bytes" + } + ] + }, + { + "internalType": "struct IScribe.ECDSAData", + "name": "ecdsaData", + "type": "tuple", + "components": [ + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ] + } + ], + "stateMutability": "payable", + "type": "function", + "name": "opPoke_optimized_397084999" + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "peek", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ] + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "peep", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ] + }, + { + "inputs": [ + { + "internalType": "struct IScribe.PokeData", + "name": "pokeData", + "type": "tuple", + "components": [ + { + "internalType": "uint128", + "name": "val", + "type": "uint128" + }, + { + "internalType": "uint32", + "name": "age", + "type": "uint32" + } + ] + }, + { + "internalType": "struct IScribe.SchnorrData", + "name": "schnorrData", + "type": "tuple", + "components": [ + { + "internalType": "bytes32", + "name": "signature", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "commitment", + "type": "address" + }, + { + "internalType": "bytes", + "name": "feedIds", + "type": "bytes" + } + ] + } + ], + "stateMutability": "nonpayable", + "type": "function", + "name": "poke" + }, + { + "inputs": [ + { + "internalType": "struct IScribe.PokeData", + "name": "pokeData", + "type": "tuple", + "components": [ + { + "internalType": "uint128", + "name": "val", + "type": "uint128" + }, + { + "internalType": "uint32", + "name": "age", + "type": "uint32" + } + ] + }, + { + "internalType": "struct IScribe.SchnorrData", + "name": "schnorrData", + "type": "tuple", + "components": [ + { + "internalType": "bytes32", + "name": "signature", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "commitment", + "type": "address" + }, + { + "internalType": "bytes", + "name": "feedIds", + "type": "bytes" + } + ] + } + ], + "stateMutability": "nonpayable", + "type": "function", + "name": "poke_optimized_7136211" + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "read", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ] + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "readWithAge", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ] + }, + { + "inputs": [ + { + "internalType": "address", + "name": "who", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function", + "name": "rely" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "bar_", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "function", + "name": "setBar" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "maxChallengeReward_", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function", + "name": "setMaxChallengeReward" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "opChallengePeriod_", + "type": "uint16" + } + ], + "stateMutability": "nonpayable", + "type": "function", + "name": "setOpChallengePeriod" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "who", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function", + "name": "tolled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ] + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "tolled", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ] + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "tryRead", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ] + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "tryReadWithAge", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ] + }, + { + "inputs": [ + { + "internalType": "address", + "name": "who", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function", + "name": "wards", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ] + }, + { + "inputs": [], + "stateMutability": "view", + "type": "function", + "name": "wat", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ] + }, + { + "inputs": [], + "stateMutability": "payable", + "type": "receive" + } + ], + "devdoc": { + "kind": "dev", + "methods": { + "authed()": { + "custom:invariant": "Only contains auth'ed addresses. ∀x ∊ authed(): _wards[x] == 1Contains all auth'ed addresses. ∀x ∊ Address: _wards[x] == 1 → x ∊ authed()", + "details": "May contain duplicates.", + "returns": { + "_0": "List of addresses granted auth." + } + }, + "authed(address)": { + "params": { + "who": "The address to check." + }, + "returns": { + "_0": "True if `who` is auth'ed, false otherwise." + } + }, + "bud(address)": { + "params": { + "who": "The address to check." + }, + "returns": { + "_0": "1 if `who` is tolled, 0 otherwise." + } + }, + "challengeReward()": { + "returns": { + "_0": "The ETH reward for successfully challenging an opPoke." + } + }, + "constructOpPokeMessage((uint128,uint32),(bytes32,address,bytes))": { + "details": "The message is defined as: H(tag ‖ H(wat ‖ pokeData ‖ schnorrData)), where H() is the keccak256 function.", + "params": { + "pokeData": "The pokeData being optimistically poked.", + "schnorrData": "The schnorrData proving `pokeData`'s integrity." + }, + "returns": { + "_0": "Message to be signed for an opPoke for `pokeData` and `schnorrData`." + } + }, + "constructPokeMessage((uint128,uint32))": { + "details": "The message is defined as: H(tag ‖ H(wat ‖ pokeData)), where H() is the keccak256 function.", + "params": { + "pokeData": "The pokeData to create the message for." + }, + "returns": { + "_0": "Message for `pokeData`." + } + }, + "deny(address)": { + "details": "Only callable by auth'ed address.", + "params": { + "who": "The address to renounce auth." + } + }, + "diss(address)": { + "details": "Only callable by auth'ed address.", + "params": { + "who": "The address to renounce toll." + } + }, + "drop(uint8)": { + "details": "Only callable by auth'ed address.", + "params": { + "feedId": "The feed id to drop." + } + }, + "drop(uint8[])": { + "details": "Only callable by auth'ed address.", + "params": { + "feedIds": "The feed ids to drop." + } + }, + "feeds()": { + "details": "Note that this function has a high gas consumption and is not intended to be called onchain.", + "returns": { + "_0": "List of feed addresses." + } + }, + "feeds(address)": { + "params": { + "who": "The address to check." + }, + "returns": { + "_0": "True if `who` is feed, false otherwise." + } + }, + "feeds(uint8)": { + "params": { + "feedId": "The feed id to check." + }, + "returns": { + "_0": "True if `feedId` is a feed, false otherwise.", + "_1": "Address of the feed with id `feedId` if `feedId` is a feed, zero-address otherwise." + } + }, + "isAcceptableSchnorrSignatureNow(bytes32,(bytes32,address,bytes))": { + "details": "Note that a valid Schnorr signature is only acceptable if the signature was signed by exactly bar many feeds. For more info, see `bar()(uint8)` and `feeds()(address[])`.Note that bar and feeds are configurable, meaning a once acceptable Schnorr signature may become unacceptable in the future.", + "params": { + "message": "The message expected to be signed via `schnorrData`.", + "schnorrData": "The SchnorrData to verify whether it proves the `message`'s integrity." + }, + "returns": { + "_0": "True if Schnorr signature is acceptable, false otherwise." + } + }, + "kiss(address)": { + "details": "Only callable by auth'ed address.", + "params": { + "who": "The address to grant toll." + } + }, + "latestAnswer()": { + "details": "Only callable by toll'ed address.", + "returns": { + "_0": "answer The oracle's latest value." + } + }, + "latestRoundData()": { + "details": "Only callable by toll'ed address.", + "returns": { + "answer": "The oracle's latest value.", + "answeredInRound": "1.", + "roundId": "1.", + "startedAt": "0.", + "updatedAt": "The timestamp of oracle's latest update." + } + }, + "lift((uint256,uint256),(uint8,bytes32,bytes32))": { + "details": "Only callable by auth'ed address.The message expected to be signed by `ecdsaData` is defined via `feedRegistrationMessage()(bytes32)`.", + "params": { + "ecdsaData": "ECDSA signed message by the feed's public key.", + "pubKey": "The public key of the feed." + }, + "returns": { + "_0": "The id of the newly lifted feed." + } + }, + "lift((uint256,uint256)[],(uint8,bytes32,bytes32)[])": { + "details": "Only callable by auth'ed address.The message expected to be signed by `ecdsaDatas` is defined via `feedRegistrationMessage()(bytes32)`.", + "params": { + "ecdsaDatas": "ECDSA signed message by the feeds' public keys.", + "pubKeys": "The public keys of the feeds." + }, + "returns": { + "_0": "List of feed ids of the newly lifted feeds." + } + }, + "opChallenge((bytes32,address,bytes))": { + "details": "If opPoke is determined to be invalid, the caller receives an ETH bounty. The bounty is defined via the `challengeReward()(uint)` function.If opPoke is determined to be invalid, the corresponding feed is dropped.", + "params": { + "schnorrData": "The SchnorrData initially provided via opPoke." + }, + "returns": { + "_0": "True if opPoke declared invalid, false otherwise." + } + }, + "opPoke((uint128,uint32),(bytes32,address,bytes),(uint8,bytes32,bytes32))": { + "details": "Expects `pokeData`'s age to be greater than the timestamp of the last successful poke.Expects `pokeData`'s age to not be greater than the current time.Expects `ecdsaData` to be a signature from a feed.Expects `ecdsaData` to prove the integrity of the `pokeData` and `schnorrData`.If the `schnorrData` is proven to be invalid via the opChallenge function, the `ecdsaData` signing feed will be dropped.", + "params": { + "ecdsaData": "The ECDSAData proving the integrity of the `pokeData` and `schnorrData`.", + "pokeData": "The PokeData being poked.", + "schnorrData": "The SchnorrData optimistically assumed to be proving the `pokeData`'s integrity." + } + }, + "opPoke_optimized_397084999((uint128,uint32),(bytes32,address,bytes),(uint8,bytes32,bytes32))": { + "details": "Optimized function selector: 0x00000000. Note that this function is _not_ defined via the IScribeOptimistic interface and one should _not_ depend on it." + }, + "peek()": { + "details": "Only callable by toll'ed address.", + "returns": { + "_0": "The oracle's current value if it exists, zero otherwise.", + "_1": "True if value exists, false otherwise." + } + }, + "peep()": { + "details": "Only callable by toll'ed address.", + "returns": { + "_0": "The oracle's current value if it exists, zero otherwise.", + "_1": "True if value exists, false otherwise." + } + }, + "poke((uint128,uint32),(bytes32,address,bytes))": { + "details": "Expects `pokeData`'s age to be greater than the timestamp of the last successful poke.Expects `pokeData`'s age to not be greater than the current time.Expects `schnorrData` to prove `pokeData`'s integrity. See `isAcceptableSchnorrSignatureNow(bytes32,SchnorrData)(bool)`.", + "params": { + "pokeData": "The PokeData being poked.", + "schnorrData": "The SchnorrData proving the `pokeData`'s integrity." + } + }, + "poke_optimized_7136211((uint128,uint32),(bytes32,address,bytes))": { + "details": "Optimized function selector: 0x00000082. Note that this function is _not_ defined via the IScribe interface and one should _not_ depend on it." + }, + "read()": { + "details": "Only callable by toll'ed address.", + "returns": { + "_0": "The oracle's current value." + } + }, + "readWithAge()": { + "details": "Only callable by toll'ed address.", + "returns": { + "_0": "The oracle's current value.", + "_1": "The value's age." + } + }, + "rely(address)": { + "details": "Only callable by auth'ed address.", + "params": { + "who": "The address to grant auth." + } + }, + "setBar(uint8)": { + "details": "Only callable by auth'ed address.Reverts if `bar` is zero.", + "params": { + "bar": "The value to update bar to." + } + }, + "setMaxChallengeReward(uint256)": { + "details": "Only callable by auth'ed address.", + "params": { + "maxChallengeReward": "The value to update maxChallengeReward to." + } + }, + "setOpChallengePeriod(uint16)": { + "details": "Only callable by auth'ed address.Reverts if opChallengePeriod is zero.Note that evaluating whether an opPoke is finalized happens via the _current_ opChallengePeriod. This means a finalized opPoke is dropped if opChallengePeriod is decreased to a value less than opPoke's age.", + "params": { + "opChallengePeriod": "The value to update opChallengePeriod to." + } + }, + "tolled()": { + "custom:invariant": "Only contains tolled addresses. ∀x ∊ tolled(): _tolled[x]Contains all tolled addresses. ∀x ∊ Address: _tolled[x] == 1 → x ∊ tolled()", + "details": "May contain duplicates.", + "returns": { + "_0": "List of addresses tolled." + } + }, + "tolled(address)": { + "params": { + "who": "The address to check." + }, + "returns": { + "_0": "True if `who` is tolled, false otherwise." + } + }, + "tryRead()": { + "details": "Only callable by toll'ed address.", + "returns": { + "_0": "True if value exists, false otherwise.", + "_1": "The oracle's current value if it exists, zero otherwise." + } + }, + "tryReadWithAge()": { + "details": "Only callable by toll'ed address.", + "returns": { + "_0": "True if value exists, false otherwise.", + "_1": "The oracle's current value if it exists, zero otherwise.", + "_2": "The value's age if value exists, zero otherwise." + } + }, + "wards(address)": { + "params": { + "who": "The address to check." + }, + "returns": { + "_0": "1 if `who` is auth'ed, 0 otherwise." + } + } + }, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "authed()": { + "notice": "Returns full list of addresses granted auth." + }, + "authed(address)": { + "notice": "Returns whether address `who` is auth'ed." + }, + "bar()": { + "notice": "Returns the bar security parameter." + }, + "bud(address)": { + "notice": "Returns whether address `who` is tolled." + }, + "challengeReward()": { + "notice": "Returns the ETH rewards being paid for successfully challenging an opPoke." + }, + "constructOpPokeMessage((uint128,uint32),(bytes32,address,bytes))": { + "notice": "Returns the message expected to be signed via ECDSA for calling opPoke." + }, + "constructPokeMessage((uint128,uint32))": { + "notice": "Returns the message expected to be signed via Schnorr for `pokeData`." + }, + "decimals()": { + "notice": "Returns the number of decimals of the oracle's value." + }, + "deny(address)": { + "notice": "Renounces address `who`'s auth." + }, + "diss(address)": { + "notice": "Renounces address `who`'s toll." + }, + "drop(uint8)": { + "notice": "Drops feed with id `feedId`." + }, + "drop(uint8[])": { + "notice": "Drops feeds with ids' `feedIds`." + }, + "feedRegistrationMessage()": { + "notice": "Returns the feed registration message." + }, + "feeds()": { + "notice": "Returns list of feed addresses." + }, + "feeds(address)": { + "notice": "Returns whether address `who` is a feed." + }, + "feeds(uint8)": { + "notice": "Returns whether feed id `feedId` is a feed and, if so, the feed's address." + }, + "isAcceptableSchnorrSignatureNow(bytes32,(bytes32,address,bytes))": { + "notice": "Returns whether the Schnorr signature `schnorrData` is currently acceptable for message `message`." + }, + "kiss(address)": { + "notice": "Grants address `who` toll." + }, + "latestAnswer()": { + "notice": "Returns the oracle's latest value." + }, + "latestRoundData()": { + "notice": "Returns the oracle's latest value." + }, + "lift((uint256,uint256),(uint8,bytes32,bytes32))": { + "notice": "Lifts public key `pubKey` to being a feed." + }, + "lift((uint256,uint256)[],(uint8,bytes32,bytes32)[])": { + "notice": "Lifts public keys `pubKeys` to being feeds." + }, + "maxChallengeReward()": { + "notice": "Returns the maxChallengeRewards parameter." + }, + "opChallenge((bytes32,address,bytes))": { + "notice": "Challenges the current challengeable opPoke." + }, + "opChallengePeriod()": { + "notice": "Returns the opChallengePeriod security parameter." + }, + "opFeedId()": { + "notice": "Returns the feed id of the feed last opPoke'd." + }, + "opPoke((uint128,uint32),(bytes32,address,bytes),(uint8,bytes32,bytes32))": { + "notice": "Optimistically pokes the oracle." + }, + "peek()": { + "notice": "Returns the oracle's current value." + }, + "peep()": { + "notice": "Returns the oracle's current value." + }, + "poke((uint128,uint32),(bytes32,address,bytes))": { + "notice": "Pokes the oracle." + }, + "read()": { + "notice": "Returns the oracle's current value." + }, + "readWithAge()": { + "notice": "Returns the oracle's current value and its age." + }, + "rely(address)": { + "notice": "Grants address `who` auth." + }, + "setBar(uint8)": { + "notice": "Updates the bar security parameters to `bar`." + }, + "setMaxChallengeReward(uint256)": { + "notice": "Updates the maxChallengeReward parameter." + }, + "setOpChallengePeriod(uint16)": { + "notice": "Updates the opChallengePeriod security parameter." + }, + "tolled()": { + "notice": "Returns full list of addresses tolled." + }, + "tolled(address)": { + "notice": "Returns whether address `who` is tolled." + }, + "tryRead()": { + "notice": "Returns the oracle's current value." + }, + "tryReadWithAge()": { + "notice": "Returns the oracle's current value and its age." + }, + "wards(address)": { + "notice": "Returns whether address `who` is auth'ed." + }, + "wat()": { + "notice": "Returns the oracle's identifier." + } + }, + "version": 1 + } + }, + "settings": { + "remappings": [ + "chronicle-std/=lib/chronicle-std/src/", + "ds-test/=lib/forge-std/lib/ds-test/src/", + "forge-std/=lib/forge-std/src/", + "lib/chronicle-std:ds-test/=lib/chronicle-std/lib/forge-std/lib/ds-test/src/", + "lib/chronicle-std:forge-std/=lib/chronicle-std/lib/forge-std/src/", + "lib/chronicle-std:src/=lib/chronicle-std/src/" + ], + "optimizer": { + "enabled": true, + "runs": 10000 + }, + "metadata": { + "bytecodeHash": "ipfs" + }, + "compilationTarget": { + "src/ScribeOptimistic.sol": "ScribeOptimistic" + }, + "evmVersion": "london", + "libraries": {}, + "viaIR": true + }, + "sources": { + "lib/chronicle-std/src/IChronicle.sol": { + "keccak256": "0x12c7c1ed4f56cc317ef6faa03c3a8a04a5227f473c18f3a9a3c59becaf1fafca", + "urls": [ + "bzz-raw://92655a68ffa472078a2a8d44205abcbecdff23a918e9b06b4a136f46eaedc418", + "dweb:/ipfs/QmP3Etq6HaAULg6X3GLjuhggGvzMiqfa3sn5LDXrqFiNjX" + ], + "license": "MIT" + }, + "lib/chronicle-std/src/auth/Auth.sol": { + "keccak256": "0x634eb69385999f28623b15a04424a12be66bc2b848c70f5217557bd1ffd20766", + "urls": [ + "bzz-raw://32680625804f064660cfe2b94fa9a0bd625b3fad5902cf1b074415760c72c55a", + "dweb:/ipfs/QmQtCDsrArnqWNs4C41zssxyRMc1UYSW3FL96Gdmxs38CG" + ], + "license": "MIT" + }, + "lib/chronicle-std/src/auth/IAuth.sol": { + "keccak256": "0x67e2920d75dc7c394f899fbb7d07e298f96059e8ae5bb3340fb6a5bf2a35624f", + "urls": [ + "bzz-raw://b0e308256c1e2a29f4fccef062475abac367cb973d77df21fd4903f58cb5a731", + "dweb:/ipfs/QmTipAgzavbPErRXWSGeb1tmKXB36zQUL4SAWJz5MYZwsP" + ], + "license": "MIT" + }, + "lib/chronicle-std/src/toll/IToll.sol": { + "keccak256": "0x138157bd72dcd67d9d6ac30ab9f43575730b18bc0a537eeb7799508fd2fa4df1", + "urls": [ + "bzz-raw://2f6aabad886cb76dca534fb50225f19dc02e42f3e254eddde9610af463adfc96", + "dweb:/ipfs/QmawqX98Pg6YdXWpWGfosrZmtMYp9FFE5LXKhWftRjbonA" + ], + "license": "MIT" + }, + "lib/chronicle-std/src/toll/Toll.sol": { + "keccak256": "0xbca48a68e702db0186c43df67bd74915df76bcc30ebc4f3d22d7449fd0963b41", + "urls": [ + "bzz-raw://41662b21eeab923c0cd05bcdfcbc87d957b1cbc66e81f9c1db6f9f4541ebc542", + "dweb:/ipfs/QmQqFDKURMZMYtatLLWPKbSugxNmz2MjxSF9dRaaqq5XJX" + ], + "license": "MIT" + }, + "src/IScribe.sol": { + "keccak256": "0x766d3a5dfc4937f30db89151e68d01b9b26585bbfb399aa3f50b58bdfb9c1d5a", + "urls": [ + "bzz-raw://81b821f53f4745ec704a4d52cc2bf325f27636b85fb66520dd3988ca1860e8e1", + "dweb:/ipfs/QmQSBoGib9rbBStwJNenU8PjAzai7Zo3c7uCKMXY41eLpj" + ], + "license": "MIT" + }, + "src/IScribeOptimistic.sol": { + "keccak256": "0xe6d69b8fe380c32811e30615994ecc6cf99b705626b63156021a8e86845bf64f", + "urls": [ + "bzz-raw://d10b5515f8b492d843fdd7f525fc74ecfca4e1051028bfc92c40ee737dc84e99", + "dweb:/ipfs/QmXqmPHLBRaowi9xppLUtPKZUgHeuW1Jc7iDG4b83jAsgr" + ], + "license": "MIT" + }, + "src/Scribe.sol": { + "keccak256": "0xb0c7b1b6a9e5bde23b4f3b3445a3b4959b3c3f98f38033fc3bb73985716b91f7", + "urls": [ + "bzz-raw://13adde6bcaa51ebd8494e6b36e00846429d93180c824d8c895f5f2a3b57cac1a", + "dweb:/ipfs/Qmf2BfWM5VJ4JJLCCCfBM2wG4t5PtMRgyUhjnegBD1fXwz" + ], + "license": "BUSL-1.1" + }, + "src/ScribeOptimistic.sol": { + "keccak256": "0x2673daf5c1381e72760acf5a548ed6a84ea669da22b479e83ed9af5e2f29fd6d", + "urls": [ + "bzz-raw://4fcc2b0fc471827fa7cd7e82ff75708ff9361e3f162a9b66305ad8a8fca7e6e0", + "dweb:/ipfs/QmW5D4A4MdvmgWCeefc7swCdJRnF4BMecEMbaxSTVgPfUN" + ], + "license": "BUSL-1.1" + }, + "src/libs/LibSchnorr.sol": { + "keccak256": "0xe0a3cc7cbeef80e86318a0f89dff4b05fb303a83fd43634a6ffd35df088fc838", + "urls": [ + "bzz-raw://f101a3c215ab131fc2a8c35f4d83ff42e8f39a995243760f762d280d171dc4d8", + "dweb:/ipfs/QmRgoTZeZmnVbvzrHA6vAYDwDHiRQ4Et9nf4NZZeVau4J1" + ], + "license": "MIT" + }, + "src/libs/LibSecp256k1.sol": { + "keccak256": "0x32c5d7f655e8493823a413e1579fdf05dcbc2f9bcaa0dc3da47e269006311245", + "urls": [ + "bzz-raw://150478306872245cbc9496f1eddf100ae3da3a628fef7a0ad9008e4296a30ba1", + "dweb:/ipfs/QmYao4oaCihTkmdaSUj82BEDus9ZsEg7ZrTgGFr7H9DtzD" + ], + "license": "MIT" + } + }, + "version": 1 + }, + "id": 29 +} From ab8d1bf8aedcee7b0be87c477e14ddadd05bbafd Mon Sep 17 00:00:00 2001 From: Max Wickham Date: Mon, 14 Oct 2024 13:14:26 +0100 Subject: [PATCH 13/40] refactor out anvil and scribe creation --- src/main.rs | 88 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 63 insertions(+), 25 deletions(-) diff --git a/src/main.rs b/src/main.rs index e1ae6dc..2ca868d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -397,25 +397,29 @@ mod tests { #[cfg(test)] mod integration_tests { + use std::future::Future; use std::sync::Arc; use alloy::hex; - use alloy::network::EthereumWallet; + use alloy::network::{Ethereum, EthereumWallet, Network}; use alloy::providers::ext::AnvilApi; - use alloy::providers::fillers::ChainIdFiller; - use alloy::providers::{ Provider, ProviderBuilder }; + use alloy::providers::fillers::{BlobGasFiller, ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, WalletFiller}; + use alloy::providers::{ Identity, Provider, ProviderBuilder, RootProvider }; use alloy::rpc::client::ClientBuilder; use alloy::signers::local::PrivateKeySigner; use alloy::signers::SignerSync; + use alloy::transports::http::{Client, Http}; use alloy::transports::layers::RetryBackoffLayer; + use alloy::transports::Transport; use alloy::{ primitives::U256, sol }; - use alloy::node_bindings::Anvil; + use alloy::node_bindings::{Anvil, AnvilInstance}; use alloy::primitives::FixedBytes; use scribe::contract::EventWithMetadata; use scribe::event_handler; use scribe::events_listener::Poller; use tokio::task::JoinSet; use tokio_util::sync::CancellationToken; + use ScribeOptimisitic::ScribeOptimisiticInstance; sol!( #[allow(missing_docs)] @@ -424,24 +428,15 @@ mod integration_tests { ScribeOptimisitic, "tests/fixtures/bytecode/ScribeOptimistic.json" ); - // type ConstructorArgs = sol!(uint256, address); - #[tokio::test] - async fn challenge() { - - // ------------------------------------------------------------------------------------------------------------ - // create anvil instance - // create provider - // create scribe instance - // set bar - // create lift validator args - // lift validator + type AnvilProvider = Arc>>>, WalletFiller>, ChainIdFiller>, RootProvider>, Http, Ethereum>>; - let anvil: alloy::node_bindings::AnvilInstance = Anvil::new().port(8545 as u16).chain_id(31337).block_time_f64(0.1).try_spawn().expect("Failed to spawn anvil"); + async fn create_anvil_instances(private_key: &str) -> (AnvilInstance, AnvilProvider, EthereumWallet) { + let anvil: AnvilInstance = Anvil::new().port(8545 as u16).chain_id(31337).block_time_f64(0.1).try_spawn().expect("Failed to spawn anvil"); // random private key - let private_key = "d4cf162c2e26ff75095922ea108d516ff07bdd732f050e64ced632980f11320b"; - let signer = EthereumWallet::new(private_key.parse::().unwrap()); + // let private_key = "d4cf162c2e26ff75095922ea108d516ff07bdd732f050e64ced632980f11320b"; + let signer: EthereumWallet = EthereumWallet::new(private_key.parse::().unwrap()); let anvil_provider = Arc::new( ProviderBuilder::new() @@ -464,18 +459,18 @@ mod integration_tests { anvil_provider.anvil_mine(Some(U256::from(3)), Some(U256::from(3))).await.expect("Failed to mine"); anvil_provider.anvil_impersonate_account(signer.default_signer().address().clone()).await.expect("Failed to impersonate account"); + return (anvil, anvil_provider, signer) + } + + async fn deploy_scribe

, T : Clone + Transport, N : Network>(provider: P, signer: EthereumWallet) -> ScribeOptimisiticInstance { + // deploy scribe instance let initial_authed = signer.default_signer().address(); let wat: [u8; 32] = hex!(" 0123456789abcdef0123456789abcdef 0123456789abcdef0123456789abcdef "); - - // set to a low current time for now, this avoids having stale poke error later - anvil_provider.anvil_set_time(1000).await.expect("Failed to set time"); - - // deploy scribe instance let scribe_optimisitic = ScribeOptimisitic - ::deploy(anvil_provider.clone(), initial_authed.clone(), FixedBytes(wat).clone()); + ::deploy(provider, initial_authed.clone(), FixedBytes(wat).clone()); let scribe_optimisitic = scribe_optimisitic.await.unwrap(); let receipt = scribe_optimisitic.setBar(1); @@ -503,6 +498,47 @@ mod integration_tests { let receipt = receipt.send().await.expect("Failed to lift validator"); receipt.watch().await.expect("Failed to set opChallenge"); + return scribe_optimisitic; + } + + #[tokio::test] + async fn challenge() { + + // ------------------------------------------------------------------------------------------------------------ + + // let anvil: alloy::node_bindings::AnvilInstance = Anvil::new().port(8545 as u16).chain_id(31337).block_time_f64(0.1).try_spawn().expect("Failed to spawn anvil"); + // random private key + let private_key = "d4cf162c2e26ff75095922ea108d516ff07bdd732f050e64ced632980f11320b"; + // let signer: EthereumWallet = EthereumWallet::new(private_key.parse::().unwrap()); + + // let anvil_provider = Arc::new( + // ProviderBuilder::new() + // .with_recommended_fillers() + // .wallet(signer.clone()) + // .filler(ChainIdFiller::new(Some(31337))) + // .on_http(anvil.endpoint_url()) + // ); + + // // set initial balance for deployer + // anvil_provider.anvil_set_balance( + // signer.default_signer().address(), + // U256::from_str_radix( + // "1000000000000000000000000000000000000000", 10 + // ).unwrap() + // ).await.expect("Unable to set balance"); + + // // configure anvil settings + // anvil_provider.anvil_set_auto_mine(true).await.expect("Failed to set auto mine"); + // anvil_provider.anvil_mine(Some(U256::from(3)), Some(U256::from(3))).await.expect("Failed to mine"); + // anvil_provider.anvil_impersonate_account(signer.default_signer().address().clone()).await.expect("Failed to impersonate account"); + + let (anvil, anvil_provider, signer) = create_anvil_instances(private_key).await; + // set to a low current time for now, this avoids having stale poke error later + anvil_provider.anvil_set_time(1000).await.expect("Failed to set time"); + + // deploy scribe instance + let scribe_optimisitic = deploy_scribe(anvil_provider.clone(), signer.clone()).await; + // deposit some balance for challenge testing anvil_provider.anvil_set_balance( scribe_optimisitic.address().clone(), @@ -510,6 +546,8 @@ mod integration_tests { "1000000000000000000000000000000000000000", 10 ).unwrap() ).await.expect("Unable to set balance"); + + // Update current anvil time to be far from last scribe config update let current_timestamp = chrono::Utc::now().timestamp() as u64 - 100; anvil_provider.anvil_set_time(current_timestamp).await.expect("Failed to set time"); @@ -521,7 +559,7 @@ mod integration_tests { let client = ClientBuilder::default() .layer(RetryBackoffLayer::new(15, 200, 300)) - .http(anvil.endpoint_url()); + .http( anvil.endpoint_url()); let provider = Arc::new( ProviderBuilder::new() From e468095ade5de440f9cb1439b4c03036a5472ceb Mon Sep 17 00:00:00 2001 From: Max Wickham Date: Tue, 15 Oct 2024 10:10:26 +0100 Subject: [PATCH 14/40] tests --- Cargo.lock | 10 + Cargo.toml | 1 + crates/scribe/src/event_handler.rs | 2 - crates/scribe/src/events_listener.rs | 5 - src/main.rs | 438 ++++++++++++++++----------- 5 files changed, 273 insertions(+), 183 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9658d97..0344fdb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1351,6 +1351,7 @@ dependencies = [ "metrics-process", "rpassword", "scribe", + "testing_logger", "tokio", "tokio-util", "tower 0.5.1", @@ -3801,6 +3802,15 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "testing_logger" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d92b727cb45d33ae956f7f46b966b25f1bc712092aeef9dba5ac798fc89f720" +dependencies = [ + "log", +] + [[package]] name = "thiserror" version = "1.0.63" diff --git a/Cargo.toml b/Cargo.toml index 3df6539..1309ae4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,3 +49,4 @@ alloy-contract = "0.4.2" alloy-rlp = "0.3.8" alloy-signer = "0.4.2" futures-util = "0.3.31" +testing_logger = "0.1.1" diff --git a/crates/scribe/src/event_handler.rs b/crates/scribe/src/event_handler.rs index cebd9c6..e50e4d1 100644 --- a/crates/scribe/src/event_handler.rs +++ b/crates/scribe/src/event_handler.rs @@ -193,8 +193,6 @@ impl EventHandler { let current_timestamp = chrono::Utc::now().timestamp() as u64; log::debug!("[{:?}] OpPoked, event_timestamp: {:?}, current_timestamp: {:?}", self.scribe_address, event_timestamp, current_timestamp); - println!("OpPoked, event_timestamp: {:?}, current_timestamp: {:?}", - event_timestamp, current_timestamp); if current_timestamp - event_timestamp > self.challenge_period.unwrap() { log::debug!( diff --git a/crates/scribe/src/events_listener.rs b/crates/scribe/src/events_listener.rs index 23bce45..ab51052 100644 --- a/crates/scribe/src/events_listener.rs +++ b/crates/scribe/src/events_listener.rs @@ -121,7 +121,6 @@ impl Poller { // Poll for new events in block range `self.last_processes_block..latest_block` async fn poll(&mut self) -> Result<()> { log::trace!("Polling for new events"); - println!("Polling for new events"); // Get latest block number let latest_block = self.provider.get_block_number().await.unwrap(); if None == self.last_processes_block { @@ -136,7 +135,6 @@ impl Poller { ); return Ok(()); } - println!("Checking blocks from {:?} to {:?}", self.last_processes_block, latest_block); // Split addresses into chunks of MAX_ADDRESS_PER_REQUEST to optimize amount of requests for chunk in self.addresses.chunks(MAX_ADDRESS_PER_REQUEST) { let logs = self @@ -150,12 +148,10 @@ impl Poller { match logs { Ok(logs) => { log::debug!("[{:?}] Received {} logs", chunk, logs.len()); - println!("Received {} logs", logs.len()); for log in logs { match EventWithMetadata::from_log(log) { Ok(event) => { log::debug!("[{:?}] Event received: {:?}", chunk, &event); - println!("Event received: {:?}", &event); // Send event to the channel self.tx.send(event).await?; } @@ -194,7 +190,6 @@ impl Poller { } _ = tokio::time::sleep(Duration::from_secs(self.poll_interval_seconds)) => { log::info!("Executing tick for events listener..."); - println!("Executing tick for events listener..."); self.poll().await?; } } diff --git a/src/main.rs b/src/main.rs index 2ca868d..41d5fde 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,13 +22,13 @@ use clap::Parser; use env_logger::Env; use eyre::Result; -use log::{error, info}; +use log::{ error, info }; use scribe::contract::EventWithMetadata; use scribe::events_listener::Poller; use scribe::metrics; use std::net::SocketAddr; use std::sync::Arc; -use std::{env, panic, path::PathBuf, time::Duration}; +use std::{ env, panic, path::PathBuf, time::Duration }; use tokio::time::sleep; use tokio_util::sync::CancellationToken; @@ -37,9 +37,9 @@ use scribe::event_handler; mod wallet; use tokio::task::JoinSet; -use tokio::{select, signal}; +use tokio::{ select, signal }; -use wallet::{CustomWallet, KeystoreWallet, PrivateKeyWallet}; +use wallet::{ CustomWallet, KeystoreWallet, PrivateKeyWallet }; use metrics_exporter_prometheus::PrometheusBuilder; use metrics_process::Collector; @@ -75,11 +75,7 @@ struct Cli { )] keystore_path: Option, - #[arg( - long = "password", - requires = "keystore_path", - help = "Key raw password as text" - )] + #[arg(long = "password", requires = "keystore_path", help = "Key raw password as text")] raw_password: Option, #[arg( @@ -90,10 +86,7 @@ struct Cli { )] password_file: Option, - #[arg( - long, - help = "If no chain_id provided binary will try to get chain_id from given RPC" - )] + #[arg(long, help = "If no chain_id provided binary will try to get chain_id from given RPC")] chain_id: Option, } @@ -130,10 +123,7 @@ async fn main() -> Result<()> { // Building tx signer for provider let signer = args.wallet()?.unwrap(); - info!( - "Using {:?} for signing transactions.", - signer.default_signer().address() - ); + info!("Using {:?} for signing transactions.", signer.default_signer().address()); // Create new HTTP client with retry backoff layer let client = ClientBuilder::default() @@ -148,7 +138,7 @@ async fn main() -> Result<()> { .filler(ChainIdFiller::new(args.chain_id)) // Add default signer .wallet(signer.clone()) - .on_client(client), + .on_client(client) ); // Create new HTTP client for flashbots @@ -165,10 +155,9 @@ async fn main() -> Result<()> { .filler(ChainIdFiller::new(args.chain_id)) // Add default signer .wallet(signer.clone()) - .on_client(flashbot_client), + .on_client(flashbot_client) ); - let mut set = JoinSet::new(); let cancel_token = CancellationToken::new(); @@ -183,10 +172,7 @@ async fn main() -> Result<()> { // Register Prometheus metrics let builder = PrometheusBuilder::new(); - let port = env::var("HTTP_PORT") - .unwrap_or(String::from("9090")) - .parse::() - .unwrap(); + let port = env::var("HTTP_PORT").unwrap_or(String::from("9090")).parse::().unwrap(); let addr = SocketAddr::from(([0, 0, 0, 0], port)); @@ -211,7 +197,7 @@ async fn main() -> Result<()> { cancel_token.clone(), provider.clone(), tx.clone(), - 30, + 30 ); // Create event distributor @@ -220,7 +206,7 @@ async fn main() -> Result<()> { cancel_token.clone(), provider.clone(), flashbot_provider.clone(), - rx, + rx ); // Run events listener process @@ -389,34 +375,44 @@ mod tests { ); } - - } - - #[cfg(test)] mod integration_tests { + use core::panic; use std::future::Future; use std::sync::Arc; + use std::vec; + use alloy::dyn_abi::abi::token; use alloy::hex; - use alloy::network::{Ethereum, EthereumWallet, Network}; + use alloy::network::{ Ethereum, EthereumWallet, Network }; use alloy::providers::ext::AnvilApi; - use alloy::providers::fillers::{BlobGasFiller, ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, WalletFiller}; + use alloy::providers::fillers::{ + BlobGasFiller, + ChainIdFiller, + FillProvider, + GasFiller, + JoinFill, + NonceFiller, + WalletFiller, + }; use alloy::providers::{ Identity, Provider, ProviderBuilder, RootProvider }; use alloy::rpc::client::ClientBuilder; use alloy::signers::local::PrivateKeySigner; use alloy::signers::SignerSync; - use alloy::transports::http::{Client, Http}; + use alloy::transports::http::reqwest::Url; + use alloy::transports::http::{ Client, Http }; use alloy::transports::layers::RetryBackoffLayer; use alloy::transports::Transport; use alloy::{ primitives::U256, sol }; - use alloy::node_bindings::{Anvil, AnvilInstance}; - use alloy::primitives::FixedBytes; + use alloy::node_bindings::{ Anvil, AnvilInstance }; + use alloy::primitives::{ Address, FixedBytes }; + use futures_util::SinkExt; + use log::Level; use scribe::contract::EventWithMetadata; use scribe::event_handler; - use scribe::events_listener::Poller; + use scribe::events_listener::{ Poller, RetryProviderWithSigner }; use tokio::task::JoinSet; use tokio_util::sync::CancellationToken; use ScribeOptimisitic::ScribeOptimisiticInstance; @@ -428,15 +424,138 @@ mod integration_tests { ScribeOptimisitic, "tests/fixtures/bytecode/ScribeOptimistic.json" ); - // type ConstructorArgs = sol!(uint256, address); - type AnvilProvider = Arc>>>, WalletFiller>, ChainIdFiller>, RootProvider>, Http, Ethereum>>; + type AnvilProvider = Arc< + FillProvider< + JoinFill< + JoinFill< + JoinFill< + Identity, + JoinFill< + GasFiller, + JoinFill> + > + >, + WalletFiller + >, + ChainIdFiller + >, + RootProvider>, + Http, + Ethereum + > + >; + + // -- Tests -- + + #[tokio::test] + async fn challenge_constract() { + // Test an invalid poke on multiple scribe instances is successfully challenged + const NUM_SCRIBE_INSTANCES: usize = 10; + // ------------------------------------------------------------------------------------------------------------ + let private_key = "d4cf162c2e26ff75095922ea108d516ff07bdd732f050e64ced632980f11320b"; + let (anvil, anvil_provider, signer) = create_anvil_instances(private_key).await; + + // set to a low current time for now, this avoids having stale poke error later + anvil_provider.anvil_set_time(1000).await.expect("Failed to set time"); + + // deploy scribe instance + let mut scribes = vec![]; + let mut scribe_addresses = vec![]; + for i in 0..NUM_SCRIBE_INSTANCES { + let scribe_optimistic = deploy_scribe(anvil_provider.clone(), signer.clone()).await; + scribe_addresses.push(scribe_optimistic.address().clone()); + scribes.push(scribe_optimistic); + anvil_provider + .anvil_set_balance( + scribes[i].address().clone(), + U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap() + ).await + .expect("Unable to set balance"); + } + // Update current anvil time to be far from last scribe config update + let current_timestamp = (chrono::Utc::now().timestamp() as u64) - 100; + anvil_provider.anvil_set_time(current_timestamp).await.expect("Failed to set time"); + + // ------------------------------------------------------------------------------------------------------------ + let cancel_token: CancellationToken = CancellationToken::new(); + + // start the event listener as a sub process + { + let addresses = scribe_addresses.clone(); + let cancel_token = cancel_token.clone(); + let url = anvil.endpoint_url(); + let signer = signer.clone(); + tokio::spawn(async move { + start_event_listener(url, signer, cancel_token, addresses).await; + }); + } + // Let first poll occur on poller + tokio::time::sleep(tokio::time::Duration::from_millis(1200)).await; - async fn create_anvil_instances(private_key: &str) -> (AnvilInstance, AnvilProvider, EthereumWallet) { - let anvil: AnvilInstance = Anvil::new().port(8545 as u16).chain_id(31337).block_time_f64(0.1).try_spawn().expect("Failed to spawn anvil"); + // Increase current block count to move away from poller intialisation block + anvil_provider + .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await + .expect("Failed to mine"); + + // ------------------------------------------------------------------------------------------------------------ + for i in 0..NUM_SCRIBE_INSTANCES{ + // Assert that the current contract balance is not 0 + let balance = anvil_provider + .get_balance(scribe_addresses[i]).await + .expect("Failed to get balance"); + assert_ne!(balance, U256::from(0)); + let current_timestamp = (chrono::Utc::now().timestamp() as u64) - 100; + + // Make invalid poke + make_invalid_op_poke(current_timestamp,private_key, &scribes[i]).await; + } + + // Mine at least one block to ensure log is processed + anvil_provider + .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await + .expect("Failed to mine"); + + let mut result = true; + for i in 0..NUM_SCRIBE_INSTANCES{ + result &= poll_balance_is_zero(&anvil_provider, &scribe_addresses[i], 10).await; + } + + // Poll the contract for up to 5 seconds to ensure that the challenge was successfull by querying balance + if result { + cancel_token.cancel(); + return; + } + cancel_token.cancel(); + panic!("Failed to challenge"); + } + + // test dont challenge if outside challenge period + + // test dont challenge if receive op challenged + + // test dont challenge if received new op poke + + // test flashbotnused first + + // test fallback to normal rpc + + // -- Helper functions -- + + async fn create_anvil_instances( + private_key: &str + ) -> (AnvilInstance, AnvilProvider, EthereumWallet) { + let anvil: AnvilInstance = Anvil::new() + .port(8545 as u16) + .chain_id(31337) + .block_time_f64(0.1) + .try_spawn() + .expect("Failed to spawn anvil"); // random private key // let private_key = "d4cf162c2e26ff75095922ea108d516ff07bdd732f050e64ced632980f11320b"; - let signer: EthereumWallet = EthereumWallet::new(private_key.parse::().unwrap()); + let signer: EthereumWallet = EthereumWallet::new( + private_key.parse::().unwrap() + ); let anvil_provider = Arc::new( ProviderBuilder::new() @@ -447,136 +566,98 @@ mod integration_tests { ); // set initial balance for deployer - anvil_provider.anvil_set_balance( - signer.default_signer().address(), - U256::from_str_radix( - "1000000000000000000000000000000000000000", 10 - ).unwrap() - ).await.expect("Unable to set balance"); + anvil_provider + .anvil_set_balance( + signer.default_signer().address(), + U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap() + ).await + .expect("Unable to set balance"); // configure anvil settings anvil_provider.anvil_set_auto_mine(true).await.expect("Failed to set auto mine"); - anvil_provider.anvil_mine(Some(U256::from(3)), Some(U256::from(3))).await.expect("Failed to mine"); - anvil_provider.anvil_impersonate_account(signer.default_signer().address().clone()).await.expect("Failed to impersonate account"); - - return (anvil, anvil_provider, signer) + anvil_provider + .anvil_mine(Some(U256::from(3)), Some(U256::from(3))).await + .expect("Failed to mine"); + anvil_provider + .anvil_impersonate_account(signer.default_signer().address().clone()).await + .expect("Failed to impersonate account"); + + return (anvil, anvil_provider, signer); } - async fn deploy_scribe

, T : Clone + Transport, N : Network>(provider: P, signer: EthereumWallet) -> ScribeOptimisiticInstance { + async fn deploy_scribe, T: Clone + Transport, N: Network>( + provider: P, + signer: EthereumWallet + ) -> ScribeOptimisiticInstance { // deploy scribe instance let initial_authed = signer.default_signer().address(); - let wat: [u8; 32] = hex!(" + let wat: [u8; 32] = hex!( + " 0123456789abcdef0123456789abcdef 0123456789abcdef0123456789abcdef - "); - let scribe_optimisitic = ScribeOptimisitic - ::deploy(provider, initial_authed.clone(), FixedBytes(wat).clone()); - let scribe_optimisitic = scribe_optimisitic.await.unwrap(); + " + ); + let scribe_optimistic = ScribeOptimisitic::deploy( + provider, + initial_authed.clone(), + FixedBytes(wat).clone() + ); + let scribe_optimistic = scribe_optimistic.await.unwrap(); - let receipt = scribe_optimisitic.setBar(1); + let receipt = scribe_optimistic.setBar(1); let receipt = receipt.send().await.expect("Failed to set bar"); receipt.watch().await.expect("Failed to set bar"); // lift validator let pub_key = LibSecp256k1::Point { x: U256::from_str_radix( - "95726579611468854699048782904089382286224374897874075137780214269565012360365", 10).unwrap(), + "95726579611468854699048782904089382286224374897874075137780214269565012360365", + 10 + ).unwrap(), y: U256::from_str_radix( - "95517337328947037046967076057450300670379811052080651187456799621439597083272",10).unwrap(), + "95517337328947037046967076057450300670379811052080651187456799621439597083272", + 10 + ).unwrap(), }; let ecdsa_data = IScribe::ECDSAData { v: 0x1b, r: FixedBytes(hex!("0ced9fd231ad454eaac301d6e15a56b6aaa839a55d664757e3ace927e95948ec")), s: FixedBytes(hex!("21b2813ad85945f320d7728fbfc9b83cbbb564135e67c16db16d5f4e74392119")), }; - let receipt = scribe_optimisitic.lift_0(pub_key, ecdsa_data); + let receipt = scribe_optimistic.lift_0(pub_key, ecdsa_data); let receipt = receipt.send().await.expect("Failed to lift validator"); receipt.watch().await.expect("Failed to lift validator"); // set challenge period - let receipt = scribe_optimisitic.setOpChallengePeriod(300); + let receipt = scribe_optimistic.setOpChallengePeriod(300); let receipt = receipt.send().await.expect("Failed to lift validator"); receipt.watch().await.expect("Failed to set opChallenge"); - return scribe_optimisitic; + return scribe_optimistic; } - #[tokio::test] - async fn challenge() { - - // ------------------------------------------------------------------------------------------------------------ - - // let anvil: alloy::node_bindings::AnvilInstance = Anvil::new().port(8545 as u16).chain_id(31337).block_time_f64(0.1).try_spawn().expect("Failed to spawn anvil"); - // random private key - let private_key = "d4cf162c2e26ff75095922ea108d516ff07bdd732f050e64ced632980f11320b"; - // let signer: EthereumWallet = EthereumWallet::new(private_key.parse::().unwrap()); - - // let anvil_provider = Arc::new( - // ProviderBuilder::new() - // .with_recommended_fillers() - // .wallet(signer.clone()) - // .filler(ChainIdFiller::new(Some(31337))) - // .on_http(anvil.endpoint_url()) - // ); - - // // set initial balance for deployer - // anvil_provider.anvil_set_balance( - // signer.default_signer().address(), - // U256::from_str_radix( - // "1000000000000000000000000000000000000000", 10 - // ).unwrap() - // ).await.expect("Unable to set balance"); - - // // configure anvil settings - // anvil_provider.anvil_set_auto_mine(true).await.expect("Failed to set auto mine"); - // anvil_provider.anvil_mine(Some(U256::from(3)), Some(U256::from(3))).await.expect("Failed to mine"); - // anvil_provider.anvil_impersonate_account(signer.default_signer().address().clone()).await.expect("Failed to impersonate account"); - - let (anvil, anvil_provider, signer) = create_anvil_instances(private_key).await; - // set to a low current time for now, this avoids having stale poke error later - anvil_provider.anvil_set_time(1000).await.expect("Failed to set time"); - - // deploy scribe instance - let scribe_optimisitic = deploy_scribe(anvil_provider.clone(), signer.clone()).await; - - // deposit some balance for challenge testing - anvil_provider.anvil_set_balance( - scribe_optimisitic.address().clone(), - U256::from_str_radix( - "1000000000000000000000000000000000000000", 10 - ).unwrap() - ).await.expect("Unable to set balance"); - - // Update current anvil time to be far from last scribe config update - let current_timestamp = chrono::Utc::now().timestamp() as u64 - 100; - anvil_provider.anvil_set_time(current_timestamp).await.expect("Failed to set time"); - - - // ------------------------------------------------------------------------------------------------------------ - // create clients - // create poller - // create handler - - let client = ClientBuilder::default() - .layer(RetryBackoffLayer::new(15, 200, 300)) - .http( anvil.endpoint_url()); + async fn start_event_listener( + url: Url, + signer: EthereumWallet, + cancel_token: CancellationToken, + addresses: Vec

+ ) { + let client = ClientBuilder::default().layer(RetryBackoffLayer::new(15, 200, 300)).http(url); let provider = Arc::new( ProviderBuilder::new() .with_recommended_fillers() .filler(ChainIdFiller::new(Some(31337))) .wallet(signer.clone()) - .on_client(client), + .on_client(client) ); let flashbot_provider = provider.clone(); - let mut set = JoinSet::new(); - let cancel_token = CancellationToken::new(); - + let mut set: JoinSet<()> = JoinSet::new(); let (tx, rx) = tokio::sync::mpsc::channel::(100); - let addresses = vec![scribe_optimisitic.address().clone()]; + // let addresses = vec![scribe_optimisitic.address().clone()]; // Create events listener let mut poller = Poller::new( @@ -584,7 +665,7 @@ mod integration_tests { cancel_token.clone(), provider.clone(), tx.clone(), - 1, + 1 ); // Create event distributor @@ -593,7 +674,7 @@ mod integration_tests { cancel_token.clone(), provider.clone(), flashbot_provider.clone(), - rx, + rx ); // Run events listener process @@ -612,70 +693,75 @@ mod integration_tests { } }); - // Sleep for 1 second to allow event listener to set the current block number after first poll - tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; - - // Increase current block count to move away from poller intialisation block - anvil_provider.anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await.expect("Failed to mine"); + while let Some(res) = set.join_next().await { + match res { + Ok(_) => log::info!("Task completed successfully"), + Err(err) => log::error!("Task failed: {:?}", err), + } + } + } - // ------------------------------------------------------------------------------------------------------------ - // create a bad poke - // wait for certain time period - // check that the challenge was made + async fn poll_balance_is_zero( + anvil_provider: &AnvilProvider, + address: &Address, + timeout: u64 + ) -> bool { + let start_time = chrono::Utc::now().timestamp() as u64; + while (chrono::Utc::now().timestamp() as u64) < start_time + timeout { + let balance = anvil_provider + .get_balance(address.clone()).await + .expect("Failed to get balance"); + if balance == U256::from(0) { + return true; + } + anvil_provider + .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await + .expect("Failed to mine"); + tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; + } + return false; + } - // Construct Poke and Schnorr data and ECDSA Sig + async fn make_invalid_op_poke( + age : u64, + private_key: &str, + scribe_optimisitic: &ScribeOptimisiticInstance, AnvilProvider, Ethereum> + ) { let poke_data: IScribe::PokeData = IScribe::PokeData { val: 10, - age: (current_timestamp - 1) as u32, + age: age as u32, }; let schnorr_data = IScribe::SchnorrData { - signature: FixedBytes(hex!("0000000000000000000000000000000000000000000000000000000000000000")), + signature: FixedBytes( + hex!("0000000000000000000000000000000000000000000000000000000000000000") + ), commitment: alloy::primitives::Address::ZERO.clone(), - feedIds: hex!("00").into() + feedIds: hex!("00").into(), }; - let op_poke_message = scribe_optimisitic.constructOpPokeMessage(poke_data.clone(), schnorr_data.clone()).call().await.expect("Failed to read current age"); + let op_poke_message = scribe_optimisitic + .constructOpPokeMessage(poke_data.clone(), schnorr_data.clone()) + .call().await + .expect("Failed to read current age"); let op_poke_message = op_poke_message._0; - let ecdsa_signer = "d4cf162c2e26ff75095922ea108d516ff07bdd732f050e64ced632980f11320b" - .parse::().expect("Failed to parse private key"); - let signature = ecdsa_signer.sign_hash_sync(&op_poke_message).expect("Failed to sign message"); + let ecdsa_signer = private_key + .parse::() + .expect("Failed to parse private key"); + let signature = ecdsa_signer + .sign_hash_sync(&op_poke_message) + .expect("Failed to sign message"); let ecdsa_data = IScribe::ECDSAData { v: 0x1b + (signature.v().to_u64() as u8), r: FixedBytes(signature.r().to_be_bytes()), s: FixedBytes(signature.s().to_be_bytes()), }; - // Assert that teh current contract balance is not 0 - let balance = anvil_provider.get_balance(scribe_optimisitic.address().clone()).await.expect("Failed to get balance"); - assert_ne!(balance, U256::from(0)); - // Make the invalid poke - let receipt = scribe_optimisitic.opPoke(poke_data.clone(), schnorr_data.clone(), ecdsa_data); + let receipt = scribe_optimisitic.opPoke( + poke_data.clone(), + schnorr_data.clone(), + ecdsa_data + ); let receipt = receipt.send().await.expect("Failed to send op poke"); receipt.watch().await.expect("Failed to watch op poke"); - anvil_provider.anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await.expect("Failed to mine"); - - // Poll the contract for up to 5 seconds to ensure that the challenge was successfull by querying balance - for _ in 1..10 { - let balance = anvil_provider.get_balance(scribe_optimisitic.address().clone()).await.expect("Failed to get balance"); - println!("Balance {:?} ", balance); - if balance == U256::from(0) { - return; - } - anvil_provider.anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await.expect("Failed to mine"); - tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; - } - } - - // test multiple scribes - - // test dont challenge if outside challenge period - - // test dont challenge if receive op challenged - - // test dont challenge if received new op poke - - // test flashbotnused first - - // test fallback to normal rpc } From f7fe6aed40e42402418f7d74d21a1b869e939392 Mon Sep 17 00:00:00 2001 From: Max Wickham Date: Wed, 16 Oct 2024 11:20:40 +0100 Subject: [PATCH 15/40] todo --- src/main.rs | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/src/main.rs b/src/main.rs index 41d5fde..fa78219 100644 --- a/src/main.rs +++ b/src/main.rs @@ -530,6 +530,81 @@ mod integration_tests { panic!("Failed to challenge"); } + #[tokio::test] + async fn dont_challenge_outside_challenge_period() { + testing_logger::setup(); + // Test an invalid poke on multiple scribe instances is successfully challenged + // ------------------------------------------------------------------------------------------------------------ + let private_key = "d4cf162c2e26ff75095922ea108d516ff07bdd732f050e64ced632980f11320b"; + let (anvil, anvil_provider, signer) = create_anvil_instances(private_key).await; + + // set to a low current time for now, this avoids having stale poke error later + anvil_provider.anvil_set_time(1000).await.expect("Failed to set time"); + + // deploy scribe instance + let scribe_optimistic = deploy_scribe(anvil_provider.clone(), signer.clone()).await; + anvil_provider + .anvil_set_balance( + scribe_optimistic.address().clone(), + U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap() + ).await + .expect("Unable to set balance"); + // Update current anvil time to be far from last scribe config update + let current_timestamp = (chrono::Utc::now().timestamp() as u64) - 100; + anvil_provider.anvil_set_time(current_timestamp).await.expect("Failed to set time"); + + // ------------------------------------------------------------------------------------------------------------ + let cancel_token: CancellationToken = CancellationToken::new(); + + // start the event listener as a sub process + { + let addresses = vec![scribe_optimistic.address().clone()]; + let cancel_token = cancel_token.clone(); + let url = anvil.endpoint_url(); + let signer = signer.clone(); + tokio::spawn(async move { + start_event_listener(url, signer, cancel_token, addresses).await; + }); + } + // Let first poll occur on poller + tokio::time::sleep(tokio::time::Duration::from_millis(1200)).await; + + // Increase current block count to move away from poller intialisation block + anvil_provider + .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await + .expect("Failed to mine"); + + // ------------------------------------------------------------------------------------------------------------ + // Assert that the current contract balance is not 0 + let balance = anvil_provider + .get_balance(scribe_optimistic.address().clone()).await + .expect("Failed to get balance"); + assert_ne!(balance, U256::from(0)); + + // Make invalid poke, but set the age way in the past + let current_timestamp = (chrono::Utc::now().timestamp() as u64) - 1000; + make_invalid_op_poke(current_timestamp, private_key, &scribe_optimistic).await; + + + // Mine at least one block to ensure log is processed + anvil_provider + .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await + .expect("Failed to mine"); + + // TODO nead to poll till contains + // Ensure the challenge was never created + testing_logger::validate( |captured_logs| { + let mut found: bool = false; + for log in captured_logs { + assert!(!log.body.to_lowercase().contains(&"Challenge started".to_lowercase()), "Challenge started log found"); + found |= log.body.to_lowercase().contains(&"OpPoked received outside of challenge period".to_lowercase()); + } + assert!(found, "Challenge cancelled log not fuond"); + }); + + } + + // test dont challenge if outside challenge period // test dont challenge if receive op challenged @@ -608,6 +683,7 @@ mod integration_tests { let receipt = receipt.send().await.expect("Failed to set bar"); receipt.watch().await.expect("Failed to set bar"); + // TODO generate the public key and v r s // lift validator let pub_key = LibSecp256k1::Point { x: U256::from_str_radix( From 3ba797ba79dd521d3905030b55c5697d177fec83 Mon Sep 17 00:00:00 2001 From: Max Wickham Date: Fri, 18 Oct 2024 10:33:29 +0100 Subject: [PATCH 16/40] passing tests --- src/main.rs | 126 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 111 insertions(+), 15 deletions(-) diff --git a/src/main.rs b/src/main.rs index fa78219..3e39336 100644 --- a/src/main.rs +++ b/src/main.rs @@ -380,6 +380,7 @@ mod tests { #[cfg(test)] mod integration_tests { use core::panic; + use std::cell::Cell; use std::future::Future; use std::sync::Arc; use std::vec; @@ -454,7 +455,7 @@ mod integration_tests { const NUM_SCRIBE_INSTANCES: usize = 10; // ------------------------------------------------------------------------------------------------------------ let private_key = "d4cf162c2e26ff75095922ea108d516ff07bdd732f050e64ced632980f11320b"; - let (anvil, anvil_provider, signer) = create_anvil_instances(private_key).await; + let (anvil, anvil_provider, signer) = create_anvil_instances(private_key, 8545).await; // set to a low current time for now, this avoids having stale poke error later anvil_provider.anvil_set_time(1000).await.expect("Failed to set time"); @@ -536,7 +537,7 @@ mod integration_tests { // Test an invalid poke on multiple scribe instances is successfully challenged // ------------------------------------------------------------------------------------------------------------ let private_key = "d4cf162c2e26ff75095922ea108d516ff07bdd732f050e64ced632980f11320b"; - let (anvil, anvil_provider, signer) = create_anvil_instances(private_key).await; + let (anvil, anvil_provider, signer) = create_anvil_instances(private_key, 8546).await; // set to a low current time for now, this avoids having stale poke error later anvil_provider.anvil_set_time(1000).await.expect("Failed to set time"); @@ -550,7 +551,8 @@ mod integration_tests { ).await .expect("Unable to set balance"); // Update current anvil time to be far from last scribe config update - let current_timestamp = (chrono::Utc::now().timestamp() as u64) - 100; + // Set the anvil time to be way in the past + let current_timestamp = (chrono::Utc::now().timestamp() as u64) - 400; anvil_provider.anvil_set_time(current_timestamp).await.expect("Failed to set time"); // ------------------------------------------------------------------------------------------------------------ @@ -581,10 +583,93 @@ mod integration_tests { .expect("Failed to get balance"); assert_ne!(balance, U256::from(0)); - // Make invalid poke, but set the age way in the past - let current_timestamp = (chrono::Utc::now().timestamp() as u64) - 1000; - make_invalid_op_poke(current_timestamp, private_key, &scribe_optimistic).await; + // Make invalid poke, but since the anvil time is not in sync with chrono time will be in the past + let current_timestamp = (chrono::Utc::now().timestamp() as u64); + make_invalid_op_poke(current_timestamp - 500, private_key, &scribe_optimistic).await; + + // Mine at least one block to ensure log is processed + anvil_provider + .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await + .expect("Failed to mine"); + // TODO nead to poll till contains + // Ensure the challenge was never created + for _ in 1..5 { + let success = Cell::new(false); + testing_logger::validate( |captured_logs| { + let mut found: bool = false; + for log in captured_logs { + assert!(!log.body.to_lowercase().contains(&"Challenge started".to_lowercase()), "Challenge started log found"); + found |= log.body.to_lowercase().contains(&"OpPoked received outside of challenge period".to_lowercase()); + } + success.set(found); + }); + if success.get() { + return; + } + tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; + } + panic!("Failed to find log"); + + + + } + + #[tokio::test] + async fn dont_challenge_outside_challenge_period() { + testing_logger::setup(); + // Test an invalid poke on multiple scribe instances is successfully challenged + // ------------------------------------------------------------------------------------------------------------ + let private_key = "d4cf162c2e26ff75095922ea108d516ff07bdd732f050e64ced632980f11320b"; + let (anvil, anvil_provider, signer) = create_anvil_instances(private_key, 8546).await; + + // set to a low current time for now, this avoids having stale poke error later + anvil_provider.anvil_set_time(1000).await.expect("Failed to set time"); + + // deploy scribe instance + let scribe_optimistic = deploy_scribe(anvil_provider.clone(), signer.clone()).await; + anvil_provider + .anvil_set_balance( + scribe_optimistic.address().clone(), + U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap() + ).await + .expect("Unable to set balance"); + // Update current anvil time to be far from last scribe config update + // Set the anvil time to be way in the past + let current_timestamp = (chrono::Utc::now().timestamp() as u64) - 400; + anvil_provider.anvil_set_time(current_timestamp).await.expect("Failed to set time"); + + // ------------------------------------------------------------------------------------------------------------ + let cancel_token: CancellationToken = CancellationToken::new(); + + // start the event listener as a sub process + { + let addresses = vec![scribe_optimistic.address().clone()]; + let cancel_token = cancel_token.clone(); + let url = anvil.endpoint_url(); + let signer = signer.clone(); + tokio::spawn(async move { + start_event_listener(url, signer, cancel_token, addresses).await; + }); + } + // Let first poll occur on poller + tokio::time::sleep(tokio::time::Duration::from_millis(1200)).await; + + // Increase current block count to move away from poller intialisation block + anvil_provider + .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await + .expect("Failed to mine"); + + // ------------------------------------------------------------------------------------------------------------ + // Assert that the current contract balance is not 0 + let balance = anvil_provider + .get_balance(scribe_optimistic.address().clone()).await + .expect("Failed to get balance"); + assert_ne!(balance, U256::from(0)); + + // Make invalid poke, but since the anvil time is not in sync with chrono time will be in the past + let current_timestamp = (chrono::Utc::now().timestamp() as u64); + make_invalid_op_poke(current_timestamp - 500, private_key, &scribe_optimistic).await; // Mine at least one block to ensure log is processed anvil_provider @@ -593,14 +678,24 @@ mod integration_tests { // TODO nead to poll till contains // Ensure the challenge was never created - testing_logger::validate( |captured_logs| { - let mut found: bool = false; - for log in captured_logs { - assert!(!log.body.to_lowercase().contains(&"Challenge started".to_lowercase()), "Challenge started log found"); - found |= log.body.to_lowercase().contains(&"OpPoked received outside of challenge period".to_lowercase()); + for _ in 1..5 { + let success = Cell::new(false); + testing_logger::validate( |captured_logs| { + let mut found: bool = false; + for log in captured_logs { + assert!(!log.body.to_lowercase().contains(&"Challenge started".to_lowercase()), "Challenge started log found"); + found |= log.body.to_lowercase().contains(&"OpPoked received outside of challenge period".to_lowercase()); + } + success.set(found); + }); + if success.get() { + return; } - assert!(found, "Challenge cancelled log not fuond"); - }); + tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; + } + panic!("Failed to find log"); + + } @@ -618,10 +713,10 @@ mod integration_tests { // -- Helper functions -- async fn create_anvil_instances( - private_key: &str + private_key: &str, port: u16 ) -> (AnvilInstance, AnvilProvider, EthereumWallet) { let anvil: AnvilInstance = Anvil::new() - .port(8545 as u16) + .port(port) .chain_id(31337) .block_time_f64(0.1) .try_spawn() @@ -666,6 +761,7 @@ mod integration_tests { ) -> ScribeOptimisiticInstance { // deploy scribe instance let initial_authed = signer.default_signer().address(); + // let private_key = signer. let wat: [u8; 32] = hex!( " 0123456789abcdef0123456789abcdef From c951616ee1c5b3a545c42d4ce5a9570e30d55553 Mon Sep 17 00:00:00 2001 From: Max Wickham Date: Fri, 18 Oct 2024 10:48:00 +0100 Subject: [PATCH 17/40] cleanup tets --- crates/scribe/src/event_handler.rs | 1 + src/main.rs | 145 ++++++----------------------- 2 files changed, 31 insertions(+), 115 deletions(-) diff --git a/crates/scribe/src/event_handler.rs b/crates/scribe/src/event_handler.rs index e50e4d1..198c316 100644 --- a/crates/scribe/src/event_handler.rs +++ b/crates/scribe/src/event_handler.rs @@ -266,6 +266,7 @@ impl EventHandler { async fn cancel_challenge(&mut self) { match &self.cancel_challenge { Some(cancel) => { + log::debug!("Cancelling existing challenge"); cancel.cancel(); self.cancel_challenge = None; } diff --git a/src/main.rs b/src/main.rs index 3e39336..0bb2045 100644 --- a/src/main.rs +++ b/src/main.rs @@ -283,7 +283,7 @@ mod tests { let cli = Cli { addresses: vec![], raw_secret_key: Some( - "def90b5b5cb2d68c5cd9de7b3e6d767cbb1b8d5fd8560bd6c42cbc4a4da30b16".to_string(), + "def90b5b5cb2d68c5cd9de7b3e6d767cbb1b8d5fd8560bd6c42cbc4a4da30b16".to_string() ), chain_id: None, keystore_path: None, @@ -304,7 +304,7 @@ mod tests { let cli = Cli { addresses: vec![], raw_secret_key: Some( - "0xdef90b5b5cb2d68c5cd9de7b3e6d767cbb1b8d5fd8560bd6c42cbc4a4da30b16".to_string(), + "0xdef90b5b5cb2d68c5cd9de7b3e6d767cbb1b8d5fd8560bd6c42cbc4a4da30b16".to_string() ), chain_id: None, keystore_path: None, @@ -325,11 +325,13 @@ mod tests { #[test] fn keystore_works_with_password_in_file() { let keystore = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/keystore"); - let keystore_file = keystore - .join("UTC--2022-12-20T10-30-43.591916000Z--ec554aeafe75601aaab43bd4621a22284db566c2"); + let keystore_file = keystore.join( + "UTC--2022-12-20T10-30-43.591916000Z--ec554aeafe75601aaab43bd4621a22284db566c2" + ); - let keystore_password_file = - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/keystore/password"); + let keystore_password_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join( + "tests/fixtures/keystore/password" + ); let cli = Cli { addresses: vec![], @@ -353,8 +355,9 @@ mod tests { #[test] fn keystore_works_with_raw_password() { let keystore = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/keystore"); - let keystore_file = keystore - .join("UTC--2022-12-20T10-30-43.591916000Z--ec554aeafe75601aaab43bd4621a22284db566c2"); + let keystore_file = keystore.join( + "UTC--2022-12-20T10-30-43.591916000Z--ec554aeafe75601aaab43bd4621a22284db566c2" + ); let cli = Cli { addresses: vec![], @@ -374,7 +377,6 @@ mod tests { address!("ec554aeafe75601aaab43bd4621a22284db566c2") ); } - } #[cfg(test)] @@ -500,8 +502,8 @@ mod integration_tests { .expect("Failed to mine"); // ------------------------------------------------------------------------------------------------------------ - for i in 0..NUM_SCRIBE_INSTANCES{ - // Assert that the current contract balance is not 0 + for i in 0..NUM_SCRIBE_INSTANCES { + // Assert that the current contract balance is not 0 let balance = anvil_provider .get_balance(scribe_addresses[i]).await .expect("Failed to get balance"); @@ -509,7 +511,7 @@ mod integration_tests { let current_timestamp = (chrono::Utc::now().timestamp() as u64) - 100; // Make invalid poke - make_invalid_op_poke(current_timestamp,private_key, &scribes[i]).await; + make_invalid_op_poke(current_timestamp, private_key, &scribes[i]).await; } // Mine at least one block to ensure log is processed @@ -518,7 +520,7 @@ mod integration_tests { .expect("Failed to mine"); let mut result = true; - for i in 0..NUM_SCRIBE_INSTANCES{ + for i in 0..NUM_SCRIBE_INSTANCES { result &= poll_balance_is_zero(&anvil_provider, &scribe_addresses[i], 10).await; } @@ -584,91 +586,7 @@ mod integration_tests { assert_ne!(balance, U256::from(0)); // Make invalid poke, but since the anvil time is not in sync with chrono time will be in the past - let current_timestamp = (chrono::Utc::now().timestamp() as u64); - make_invalid_op_poke(current_timestamp - 500, private_key, &scribe_optimistic).await; - - // Mine at least one block to ensure log is processed - anvil_provider - .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await - .expect("Failed to mine"); - - // TODO nead to poll till contains - // Ensure the challenge was never created - for _ in 1..5 { - let success = Cell::new(false); - testing_logger::validate( |captured_logs| { - let mut found: bool = false; - for log in captured_logs { - assert!(!log.body.to_lowercase().contains(&"Challenge started".to_lowercase()), "Challenge started log found"); - found |= log.body.to_lowercase().contains(&"OpPoked received outside of challenge period".to_lowercase()); - } - success.set(found); - }); - if success.get() { - return; - } - tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; - } - panic!("Failed to find log"); - - - - } - - #[tokio::test] - async fn dont_challenge_outside_challenge_period() { - testing_logger::setup(); - // Test an invalid poke on multiple scribe instances is successfully challenged - // ------------------------------------------------------------------------------------------------------------ - let private_key = "d4cf162c2e26ff75095922ea108d516ff07bdd732f050e64ced632980f11320b"; - let (anvil, anvil_provider, signer) = create_anvil_instances(private_key, 8546).await; - - // set to a low current time for now, this avoids having stale poke error later - anvil_provider.anvil_set_time(1000).await.expect("Failed to set time"); - - // deploy scribe instance - let scribe_optimistic = deploy_scribe(anvil_provider.clone(), signer.clone()).await; - anvil_provider - .anvil_set_balance( - scribe_optimistic.address().clone(), - U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap() - ).await - .expect("Unable to set balance"); - // Update current anvil time to be far from last scribe config update - // Set the anvil time to be way in the past - let current_timestamp = (chrono::Utc::now().timestamp() as u64) - 400; - anvil_provider.anvil_set_time(current_timestamp).await.expect("Failed to set time"); - - // ------------------------------------------------------------------------------------------------------------ - let cancel_token: CancellationToken = CancellationToken::new(); - - // start the event listener as a sub process - { - let addresses = vec![scribe_optimistic.address().clone()]; - let cancel_token = cancel_token.clone(); - let url = anvil.endpoint_url(); - let signer = signer.clone(); - tokio::spawn(async move { - start_event_listener(url, signer, cancel_token, addresses).await; - }); - } - // Let first poll occur on poller - tokio::time::sleep(tokio::time::Duration::from_millis(1200)).await; - - // Increase current block count to move away from poller intialisation block - anvil_provider - .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await - .expect("Failed to mine"); - - // ------------------------------------------------------------------------------------------------------------ - // Assert that the current contract balance is not 0 - let balance = anvil_provider - .get_balance(scribe_optimistic.address().clone()).await - .expect("Failed to get balance"); - assert_ne!(balance, U256::from(0)); - - // Make invalid poke, but since the anvil time is not in sync with chrono time will be in the past - let current_timestamp = (chrono::Utc::now().timestamp() as u64); + let current_timestamp = chrono::Utc::now().timestamp() as u64; make_invalid_op_poke(current_timestamp - 500, private_key, &scribe_optimistic).await; // Mine at least one block to ensure log is processed @@ -680,11 +598,16 @@ mod integration_tests { // Ensure the challenge was never created for _ in 1..5 { let success = Cell::new(false); - testing_logger::validate( |captured_logs| { + testing_logger::validate(|captured_logs| { let mut found: bool = false; for log in captured_logs { - assert!(!log.body.to_lowercase().contains(&"Challenge started".to_lowercase()), "Challenge started log found"); - found |= log.body.to_lowercase().contains(&"OpPoked received outside of challenge period".to_lowercase()); + assert!( + !log.body.to_lowercase().contains(&"Challenge started".to_lowercase()), + "Challenge started log found" + ); + found |= log.body + .to_lowercase() + .contains(&"OpPoked received outside of challenge period".to_lowercase()); } success.set(found); }); @@ -694,26 +617,18 @@ mod integration_tests { tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; } panic!("Failed to find log"); - - - } - // test dont challenge if outside challenge period - - // test dont challenge if receive op challenged - - // test dont challenge if received new op poke + // TODO: test dont challenge if receive op challenged - // test flashbotnused first - - // test fallback to normal rpc + // TODO: test flashbotnused first, fallback to normal rpc // -- Helper functions -- async fn create_anvil_instances( - private_key: &str, port: u16 + private_key: &str, + port: u16 ) -> (AnvilInstance, AnvilProvider, EthereumWallet) { let anvil: AnvilInstance = Anvil::new() .port(port) @@ -779,7 +694,7 @@ mod integration_tests { let receipt = receipt.send().await.expect("Failed to set bar"); receipt.watch().await.expect("Failed to set bar"); - // TODO generate the public key and v r s + // TODO generate the public key and v r s from private key // lift validator let pub_key = LibSecp256k1::Point { x: U256::from_str_radix( @@ -895,7 +810,7 @@ mod integration_tests { } async fn make_invalid_op_poke( - age : u64, + age: u64, private_key: &str, scribe_optimisitic: &ScribeOptimisiticInstance, AnvilProvider, Ethereum> ) { From f5a7ae9584b45ba9f2adfbf4b4cc61b1e6bdd185 Mon Sep 17 00:00:00 2001 From: Max Wickham Date: Fri, 18 Oct 2024 11:16:55 +0100 Subject: [PATCH 18/40] comments --- crates/scribe/src/event_handler.rs | 9 +--- crates/scribe/src/events_listener.rs | 2 +- src/main.rs | 65 +++++++++++++++++++--------- 3 files changed, 48 insertions(+), 28 deletions(-) diff --git a/crates/scribe/src/event_handler.rs b/crates/scribe/src/event_handler.rs index 198c316..76a4017 100644 --- a/crates/scribe/src/event_handler.rs +++ b/crates/scribe/src/event_handler.rs @@ -195,6 +195,7 @@ impl EventHandler { self.scribe_address, event_timestamp, current_timestamp); if current_timestamp - event_timestamp > self.challenge_period.unwrap() { + // This log is expected in tests, tests must be updated if log is changed log::debug!( "[{:?}] OpPoked received outside of challenge period", self.scribe_address @@ -308,6 +309,7 @@ impl ChallengeHandler { } pub async fn start(&self) -> Result<()> { + // This checked for in tests, tests must be updated if log is changed log::debug!("Challenge started"); // Perform the challenge after 200ms @@ -383,10 +385,3 @@ impl ChallengeHandler { Ok(()) } } - -// need to make a mock provider to test this -// provider needs to be able to respond to block time -// provider needs to be able to challenge request -// need to be able to make provider fail challenge request - -// test diff --git a/crates/scribe/src/events_listener.rs b/crates/scribe/src/events_listener.rs index ab51052..9862afb 100644 --- a/crates/scribe/src/events_listener.rs +++ b/crates/scribe/src/events_listener.rs @@ -20,7 +20,7 @@ use alloy::{ primitives::Address, providers::{ fillers::{ - BlobGasFiller, CachedNonceManager, ChainIdFiller, FillProvider, GasFiller, JoinFill, + BlobGasFiller, ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, WalletFiller, }, Identity, Provider, RootProvider, WalletProvider, diff --git a/src/main.rs b/src/main.rs index 0bb2045..bff807e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -383,11 +383,9 @@ mod tests { mod integration_tests { use core::panic; use std::cell::Cell; - use std::future::Future; use std::sync::Arc; use std::vec; - use alloy::dyn_abi::abi::token; use alloy::hex; use alloy::network::{ Ethereum, EthereumWallet, Network }; use alloy::providers::ext::AnvilApi; @@ -408,25 +406,33 @@ mod integration_tests { use alloy::transports::http::{ Client, Http }; use alloy::transports::layers::RetryBackoffLayer; use alloy::transports::Transport; - use alloy::{ primitives::U256, sol }; + use alloy::primitives::U256; use alloy::node_bindings::{ Anvil, AnvilInstance }; use alloy::primitives::{ Address, FixedBytes }; - use futures_util::SinkExt; - use log::Level; use scribe::contract::EventWithMetadata; use scribe::event_handler; - use scribe::events_listener::{ Poller, RetryProviderWithSigner }; + use scribe::events_listener::Poller; use tokio::task::JoinSet; use tokio_util::sync::CancellationToken; - use ScribeOptimisitic::ScribeOptimisiticInstance; - - sol!( - #[allow(missing_docs)] - #[sol(rpc)] - #[derive(Debug)] + use scribe_optimistic::{ + IScribe, + LibSecp256k1, ScribeOptimisitic, - "tests/fixtures/bytecode/ScribeOptimistic.json" - ); + ScribeOptimisitic::ScribeOptimisiticInstance, + }; + + // In a seperate module to problems with autoformatter + #[rustfmt::skip] + mod scribe_optimistic { + use alloy::sol; + sol!( + #[allow(missing_docs)] + #[sol(rpc)] + #[derive(Debug)] + ScribeOptimisitic, + "tests/fixtures/bytecode/ScribeOptimistic.json" + ); + } type AnvilProvider = Arc< FillProvider< @@ -449,6 +455,8 @@ mod integration_tests { > >; + const PRIVATE_KEY: &str = "d4cf162c2e26ff75095922ea108d516ff07bdd732f050e64ced632980f11320b"; + // -- Tests -- #[tokio::test] @@ -456,7 +464,7 @@ mod integration_tests { // Test an invalid poke on multiple scribe instances is successfully challenged const NUM_SCRIBE_INSTANCES: usize = 10; // ------------------------------------------------------------------------------------------------------------ - let private_key = "d4cf162c2e26ff75095922ea108d516ff07bdd732f050e64ced632980f11320b"; + let private_key = PRIVATE_KEY; let (anvil, anvil_provider, signer) = create_anvil_instances(private_key, 8545).await; // set to a low current time for now, this avoids having stale poke error later @@ -524,6 +532,24 @@ mod integration_tests { result &= poll_balance_is_zero(&anvil_provider, &scribe_addresses[i], 10).await; } + // Poll to check that the challenge started log is found + for _ in 1..5 { + let success = Cell::new(false); + testing_logger::validate(|captured_logs| { + let mut found: bool = false; + for log in captured_logs { + found |= log.body + .to_lowercase() + .contains(&"Challenge started".to_lowercase()); + } + success.set(found); + }); + if success.get() { + return; + } + tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; + } + // Poll the contract for up to 5 seconds to ensure that the challenge was successfull by querying balance if result { cancel_token.cancel(); @@ -538,7 +564,7 @@ mod integration_tests { testing_logger::setup(); // Test an invalid poke on multiple scribe instances is successfully challenged // ------------------------------------------------------------------------------------------------------------ - let private_key = "d4cf162c2e26ff75095922ea108d516ff07bdd732f050e64ced632980f11320b"; + let private_key = PRIVATE_KEY; let (anvil, anvil_provider, signer) = create_anvil_instances(private_key, 8546).await; // set to a low current time for now, this avoids having stale poke error later @@ -594,7 +620,8 @@ mod integration_tests { .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await .expect("Failed to mine"); - // TODO nead to poll till contains + // Poll till expected logs are found + // Assert challenge started log not found // Ensure the challenge was never created for _ in 1..5 { let success = Cell::new(false); @@ -619,7 +646,6 @@ mod integration_tests { panic!("Failed to find log"); } - // TODO: test dont challenge if receive op challenged // TODO: test flashbotnused first, fallback to normal rpc @@ -636,8 +662,7 @@ mod integration_tests { .block_time_f64(0.1) .try_spawn() .expect("Failed to spawn anvil"); - // random private key - // let private_key = "d4cf162c2e26ff75095922ea108d516ff07bdd732f050e64ced632980f11320b"; + let signer: EthereumWallet = EthereumWallet::new( private_key.parse::().unwrap() ); From 24ed56a5d995e4592f0fbf97be526915cf3d287f Mon Sep 17 00:00:00 2001 From: Max Wickham Date: Fri, 18 Oct 2024 11:40:23 +0100 Subject: [PATCH 19/40] comments --- src/main.rs | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/main.rs b/src/main.rs index bff807e..e2f1947 100644 --- a/src/main.rs +++ b/src/main.rs @@ -421,7 +421,7 @@ mod integration_tests { ScribeOptimisitic::ScribeOptimisiticInstance, }; - // In a seperate module to problems with autoformatter + // In a seperate module due to problems with autoformatter #[rustfmt::skip] mod scribe_optimistic { use alloy::sol; @@ -484,6 +484,7 @@ mod integration_tests { ).await .expect("Unable to set balance"); } + // Update current anvil time to be far from last scribe config update let current_timestamp = (chrono::Utc::now().timestamp() as u64) - 100; anvil_provider.anvil_set_time(current_timestamp).await.expect("Failed to set time"); @@ -527,14 +528,17 @@ mod integration_tests { .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await .expect("Failed to mine"); + + // Poll the contract for up to 5 seconds to ensure that the challenge was successfull by querying balance let mut result = true; for i in 0..NUM_SCRIBE_INSTANCES { - result &= poll_balance_is_zero(&anvil_provider, &scribe_addresses[i], 10).await; + result &= poll_balance_is_zero(&anvil_provider, &scribe_addresses[i], 5).await; } - // Poll to check that the challenge started log is found - for _ in 1..5 { - let success = Cell::new(false); + // Poll to check that the challenge started log is found, + // (to ensure log hasn't been changed as its non appearance is looked for in other tests) + for i in 0..5 { + let success: Cell = Cell::new(false); testing_logger::validate(|captured_logs| { let mut found: bool = false; for log in captured_logs { @@ -545,12 +549,14 @@ mod integration_tests { success.set(found); }); if success.get() { - return; + break; } tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; + if i == 4 { + panic!("Failed to find log"); + } } - // Poll the contract for up to 5 seconds to ensure that the challenge was successfull by querying balance if result { cancel_token.cancel(); return; @@ -565,12 +571,13 @@ mod integration_tests { // Test an invalid poke on multiple scribe instances is successfully challenged // ------------------------------------------------------------------------------------------------------------ let private_key = PRIVATE_KEY; + // Use a new port for each test to avoid conflicts if tests run in parrallel let (anvil, anvil_provider, signer) = create_anvil_instances(private_key, 8546).await; - // set to a low current time for now, this avoids having stale poke error later + // Set to a low current time for now, this avoids having stale poke error later anvil_provider.anvil_set_time(1000).await.expect("Failed to set time"); - // deploy scribe instance + // Deploy scribe instance let scribe_optimistic = deploy_scribe(anvil_provider.clone(), signer.clone()).await; anvil_provider .anvil_set_balance( @@ -578,8 +585,9 @@ mod integration_tests { U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap() ).await .expect("Unable to set balance"); + // Update current anvil time to be far from last scribe config update - // Set the anvil time to be way in the past + // Set the anvil time to be in the past to ensure the challenge period is exceeded later let current_timestamp = (chrono::Utc::now().timestamp() as u64) - 400; anvil_provider.anvil_set_time(current_timestamp).await.expect("Failed to set time"); From 454a5893d05eb0f1be07f0c0ab888ca900304004 Mon Sep 17 00:00:00 2001 From: Max Wickham Date: Fri, 25 Oct 2024 13:08:33 +0100 Subject: [PATCH 20/40] fmt --- crates/scribe/src/contract.rs | 24 +-- crates/scribe/src/event_handler.rs | 47 ++--- crates/scribe/src/events_listener.rs | 4 +- src/main.rs | 251 +++++++++++++++------------ 4 files changed, 178 insertions(+), 148 deletions(-) diff --git a/crates/scribe/src/contract.rs b/crates/scribe/src/contract.rs index 4e82104..c6fb171 100644 --- a/crates/scribe/src/contract.rs +++ b/crates/scribe/src/contract.rs @@ -16,7 +16,10 @@ use std::{fmt::Debug, sync::Arc}; use alloy::{ - primitives::{Address, FixedBytes, LogData}, rpc::types::Log, sol, sol_types::SolEvent + primitives::{Address, FixedBytes, LogData}, + rpc::types::Log, + sol, + sol_types::SolEvent, }; use eyre::{bail, Result, WrapErr}; use IScribe::SchnorrData; @@ -107,7 +110,7 @@ pub trait ScribeOptimisticProvider { /// Returns the address of the contract. fn address(&self) -> &Address; - /// Returns a new provider with the same signer. + /// Returns a new provider with the same signer. fn get_new_provider(&self) -> Arc; } @@ -120,9 +123,7 @@ impl ScribeOptimisticProviderInstance { /// Creates a new ScribeOptimisticInstance pub fn new(address: Address, provider: Arc) -> Self { let contract = ScribeOptimistic::new(address, provider.clone()); - Self { - contract, - } + Self { contract } } } @@ -155,11 +156,13 @@ impl ScribeOptimisticProvider for ScribeOptimisticProviderInstance { log::debug!("{:?} Challenging OpPoke", self.contract.address()); let from_address = self.contract.address(); log::info!("Challenging from address: {:?}", from_address); - let transaction = self.contract - .opChallenge(schnorr_data) - // TODO set gas limit properly - .gas(200000); - transaction.send() + let transaction = self + .contract + .opChallenge(schnorr_data) + // TODO set gas limit properly + .gas(200000); + transaction + .send() .await? .watch() .await @@ -173,5 +176,4 @@ impl ScribeOptimisticProvider for ScribeOptimisticProviderInstance { fn get_new_provider(&self) -> Arc { self.contract.provider().clone() } - } diff --git a/crates/scribe/src/event_handler.rs b/crates/scribe/src/event_handler.rs index 76a4017..4a4f484 100644 --- a/crates/scribe/src/event_handler.rs +++ b/crates/scribe/src/event_handler.rs @@ -21,7 +21,7 @@ use alloy::primitives::Address; use alloy::providers::Provider; use alloy::rpc::types::BlockTransactionsKind; use eyre::Result; -use tokio::sync::mpsc::{ Receiver, Sender }; +use tokio::sync::mpsc::{Receiver, Sender}; use tokio::sync::Mutex; use tokio::task::JoinSet; use tokio_util::sync::CancellationToken; @@ -29,9 +29,7 @@ use tokio_util::sync::CancellationToken; use crate::contract::Event; use crate::contract::ScribeOptimistic::OpPoked; use crate::contract::{ - EventWithMetadata, - ScribeOptimisticProvider, - ScribeOptimisticProviderInstance, + EventWithMetadata, ScribeOptimisticProvider, ScribeOptimisticProviderInstance, }; use crate::events_listener::RetryProviderWithSigner; @@ -56,7 +54,7 @@ impl EventDistributor { cancel: CancellationToken, provider: Arc, flashbot_provider: Arc, - rx: Receiver + rx: Receiver, ) -> Self { Self { addresses, @@ -80,7 +78,7 @@ impl EventDistributor { self.provider.clone(), self.flashbot_provider.clone(), self.cancel.clone(), - rx + rx, ); contract_handler_set.spawn(async move { let _ = contract_handler.start().await; @@ -135,7 +133,7 @@ impl EventHandler { provider: Arc, flashbot_provider: Arc, cancel: CancellationToken, - rx: Receiver + rx: Receiver, ) -> Self { Self { scribe_address, @@ -225,10 +223,10 @@ impl EventHandler { if self.challenge_period.is_some() { return Ok(()); } - let period = ScribeOptimisticProviderInstance::new( - self.scribe_address, - self.provider.clone() - ).get_challenge_period().await?; + let period = + ScribeOptimisticProviderInstance::new(self.scribe_address, self.provider.clone()) + .get_challenge_period() + .await?; self.challenge_period = Some(period as u64); Ok(()) } @@ -239,21 +237,15 @@ impl EventHandler { // Create a new cancellation token self.cancel_challenge = Some(CancellationToken::new()); // Create a new challenger instance - let challenge_handler = Some( - Arc::new( - Mutex::new( - ChallengeHandler::new( - op_poked, - self.cancel.clone(), - // cancel_challenge garunteed to be Some - self.cancel_challenge.as_ref().unwrap().clone(), - self.scribe_address, - self.provider.clone(), - self.flashbot_provider.clone() - ) - ) - ) - ); + let challenge_handler = Some(Arc::new(Mutex::new(ChallengeHandler::new( + op_poked, + self.cancel.clone(), + // cancel_challenge garunteed to be Some + self.cancel_challenge.as_ref().unwrap().clone(), + self.scribe_address, + self.provider.clone(), + self.flashbot_provider.clone(), + )))); // TODO: Validate challenge period first... // Spawn the asynchronous task @@ -296,7 +288,7 @@ impl ChallengeHandler { cancel: CancellationToken, address: Address, provider: Arc, - flashbot_provider: Arc + flashbot_provider: Arc, ) -> Self { Self { op_poked, @@ -313,7 +305,6 @@ impl ChallengeHandler { log::debug!("Challenge started"); // Perform the challenge after 200ms - // TODO: better way to retry !!!!!!!!!!!!!! let mut challenge_attempts: u64 = 0; loop { tokio::select! { diff --git a/crates/scribe/src/events_listener.rs b/crates/scribe/src/events_listener.rs index 9862afb..579e666 100644 --- a/crates/scribe/src/events_listener.rs +++ b/crates/scribe/src/events_listener.rs @@ -20,8 +20,8 @@ use alloy::{ primitives::Address, providers::{ fillers::{ - BlobGasFiller, ChainIdFiller, FillProvider, GasFiller, JoinFill, - NonceFiller, WalletFiller, + BlobGasFiller, ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, + WalletFiller, }, Identity, Provider, RootProvider, WalletProvider, }, diff --git a/src/main.rs b/src/main.rs index e2f1947..0be884d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,13 +22,13 @@ use clap::Parser; use env_logger::Env; use eyre::Result; -use log::{ error, info }; +use log::{error, info}; use scribe::contract::EventWithMetadata; use scribe::events_listener::Poller; use scribe::metrics; use std::net::SocketAddr; use std::sync::Arc; -use std::{ env, panic, path::PathBuf, time::Duration }; +use std::{env, panic, path::PathBuf, time::Duration}; use tokio::time::sleep; use tokio_util::sync::CancellationToken; @@ -37,9 +37,9 @@ use scribe::event_handler; mod wallet; use tokio::task::JoinSet; -use tokio::{ select, signal }; +use tokio::{select, signal}; -use wallet::{ CustomWallet, KeystoreWallet, PrivateKeyWallet }; +use wallet::{CustomWallet, KeystoreWallet, PrivateKeyWallet}; use metrics_exporter_prometheus::PrometheusBuilder; use metrics_process::Collector; @@ -59,7 +59,10 @@ struct Cli { #[arg(long, help = "Node HTTP RPC_URL, normally starts with https://****")] rpc_url: String, - #[arg(long, help = "Flashbot Node HTTP RPC_URL, normally starts with https://****")] + #[arg( + long, + help = "Flashbot Node HTTP RPC_URL, normally starts with https://****" + )] flashbot_rpc_url: String, #[arg( @@ -75,7 +78,11 @@ struct Cli { )] keystore_path: Option, - #[arg(long = "password", requires = "keystore_path", help = "Key raw password as text")] + #[arg( + long = "password", + requires = "keystore_path", + help = "Key raw password as text" + )] raw_password: Option, #[arg( @@ -86,7 +93,10 @@ struct Cli { )] password_file: Option, - #[arg(long, help = "If no chain_id provided binary will try to get chain_id from given RPC")] + #[arg( + long, + help = "If no chain_id provided binary will try to get chain_id from given RPC" + )] chain_id: Option, } @@ -123,7 +133,10 @@ async fn main() -> Result<()> { // Building tx signer for provider let signer = args.wallet()?.unwrap(); - info!("Using {:?} for signing transactions.", signer.default_signer().address()); + info!( + "Using {:?} for signing transactions.", + signer.default_signer().address() + ); // Create new HTTP client with retry backoff layer let client = ClientBuilder::default() @@ -138,7 +151,7 @@ async fn main() -> Result<()> { .filler(ChainIdFiller::new(args.chain_id)) // Add default signer .wallet(signer.clone()) - .on_client(client) + .on_client(client), ); // Create new HTTP client for flashbots @@ -155,7 +168,7 @@ async fn main() -> Result<()> { .filler(ChainIdFiller::new(args.chain_id)) // Add default signer .wallet(signer.clone()) - .on_client(flashbot_client) + .on_client(flashbot_client), ); let mut set = JoinSet::new(); @@ -172,7 +185,10 @@ async fn main() -> Result<()> { // Register Prometheus metrics let builder = PrometheusBuilder::new(); - let port = env::var("HTTP_PORT").unwrap_or(String::from("9090")).parse::().unwrap(); + let port = env::var("HTTP_PORT") + .unwrap_or(String::from("9090")) + .parse::() + .unwrap(); let addr = SocketAddr::from(([0, 0, 0, 0], port)); @@ -197,7 +213,7 @@ async fn main() -> Result<()> { cancel_token.clone(), provider.clone(), tx.clone(), - 30 + 30, ); // Create event distributor @@ -206,7 +222,7 @@ async fn main() -> Result<()> { cancel_token.clone(), provider.clone(), flashbot_provider.clone(), - rx + rx, ); // Run events listener process @@ -283,7 +299,7 @@ mod tests { let cli = Cli { addresses: vec![], raw_secret_key: Some( - "def90b5b5cb2d68c5cd9de7b3e6d767cbb1b8d5fd8560bd6c42cbc4a4da30b16".to_string() + "def90b5b5cb2d68c5cd9de7b3e6d767cbb1b8d5fd8560bd6c42cbc4a4da30b16".to_string(), ), chain_id: None, keystore_path: None, @@ -304,7 +320,7 @@ mod tests { let cli = Cli { addresses: vec![], raw_secret_key: Some( - "0xdef90b5b5cb2d68c5cd9de7b3e6d767cbb1b8d5fd8560bd6c42cbc4a4da30b16".to_string() + "0xdef90b5b5cb2d68c5cd9de7b3e6d767cbb1b8d5fd8560bd6c42cbc4a4da30b16".to_string(), ), chain_id: None, keystore_path: None, @@ -325,13 +341,11 @@ mod tests { #[test] fn keystore_works_with_password_in_file() { let keystore = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/keystore"); - let keystore_file = keystore.join( - "UTC--2022-12-20T10-30-43.591916000Z--ec554aeafe75601aaab43bd4621a22284db566c2" - ); + let keystore_file = keystore + .join("UTC--2022-12-20T10-30-43.591916000Z--ec554aeafe75601aaab43bd4621a22284db566c2"); - let keystore_password_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join( - "tests/fixtures/keystore/password" - ); + let keystore_password_file = + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/keystore/password"); let cli = Cli { addresses: vec![], @@ -355,9 +369,8 @@ mod tests { #[test] fn keystore_works_with_raw_password() { let keystore = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/keystore"); - let keystore_file = keystore.join( - "UTC--2022-12-20T10-30-43.591916000Z--ec554aeafe75601aaab43bd4621a22284db566c2" - ); + let keystore_file = keystore + .join("UTC--2022-12-20T10-30-43.591916000Z--ec554aeafe75601aaab43bd4621a22284db566c2"); let cli = Cli { addresses: vec![], @@ -387,39 +400,30 @@ mod integration_tests { use std::vec; use alloy::hex; - use alloy::network::{ Ethereum, EthereumWallet, Network }; + use alloy::network::{Ethereum, EthereumWallet, Network}; + use alloy::node_bindings::{Anvil, AnvilInstance}; + use alloy::primitives::U256; + use alloy::primitives::{Address, FixedBytes}; use alloy::providers::ext::AnvilApi; use alloy::providers::fillers::{ - BlobGasFiller, - ChainIdFiller, - FillProvider, - GasFiller, - JoinFill, - NonceFiller, - WalletFiller, + BlobGasFiller, ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, WalletFiller, }; - use alloy::providers::{ Identity, Provider, ProviderBuilder, RootProvider }; + use alloy::providers::{Identity, Provider, ProviderBuilder, RootProvider}; use alloy::rpc::client::ClientBuilder; use alloy::signers::local::PrivateKeySigner; use alloy::signers::SignerSync; use alloy::transports::http::reqwest::Url; - use alloy::transports::http::{ Client, Http }; + use alloy::transports::http::{Client, Http}; use alloy::transports::layers::RetryBackoffLayer; use alloy::transports::Transport; - use alloy::primitives::U256; - use alloy::node_bindings::{ Anvil, AnvilInstance }; - use alloy::primitives::{ Address, FixedBytes }; use scribe::contract::EventWithMetadata; use scribe::event_handler; use scribe::events_listener::Poller; - use tokio::task::JoinSet; - use tokio_util::sync::CancellationToken; use scribe_optimistic::{ - IScribe, - LibSecp256k1, - ScribeOptimisitic, - ScribeOptimisitic::ScribeOptimisiticInstance, + IScribe, LibSecp256k1, ScribeOptimisitic, ScribeOptimisitic::ScribeOptimisiticInstance, }; + use tokio::task::JoinSet; + use tokio_util::sync::CancellationToken; // In a seperate module due to problems with autoformatter #[rustfmt::skip] @@ -442,17 +446,17 @@ mod integration_tests { Identity, JoinFill< GasFiller, - JoinFill> - > + JoinFill>, + >, >, - WalletFiller + WalletFiller, >, - ChainIdFiller + ChainIdFiller, >, RootProvider>, Http, - Ethereum - > + Ethereum, + >, >; const PRIVATE_KEY: &str = "d4cf162c2e26ff75095922ea108d516ff07bdd732f050e64ced632980f11320b"; @@ -468,7 +472,10 @@ mod integration_tests { let (anvil, anvil_provider, signer) = create_anvil_instances(private_key, 8545).await; // set to a low current time for now, this avoids having stale poke error later - anvil_provider.anvil_set_time(1000).await.expect("Failed to set time"); + anvil_provider + .anvil_set_time(1000) + .await + .expect("Failed to set time"); // deploy scribe instance let mut scribes = vec![]; @@ -480,14 +487,18 @@ mod integration_tests { anvil_provider .anvil_set_balance( scribes[i].address().clone(), - U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap() - ).await + U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap(), + ) + .await .expect("Unable to set balance"); } // Update current anvil time to be far from last scribe config update let current_timestamp = (chrono::Utc::now().timestamp() as u64) - 100; - anvil_provider.anvil_set_time(current_timestamp).await.expect("Failed to set time"); + anvil_provider + .anvil_set_time(current_timestamp) + .await + .expect("Failed to set time"); // ------------------------------------------------------------------------------------------------------------ let cancel_token: CancellationToken = CancellationToken::new(); @@ -507,14 +518,16 @@ mod integration_tests { // Increase current block count to move away from poller intialisation block anvil_provider - .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await + .anvil_mine(Some(U256::from(1)), Some(U256::from(1))) + .await .expect("Failed to mine"); // ------------------------------------------------------------------------------------------------------------ for i in 0..NUM_SCRIBE_INSTANCES { // Assert that the current contract balance is not 0 let balance = anvil_provider - .get_balance(scribe_addresses[i]).await + .get_balance(scribe_addresses[i]) + .await .expect("Failed to get balance"); assert_ne!(balance, U256::from(0)); let current_timestamp = (chrono::Utc::now().timestamp() as u64) - 100; @@ -525,10 +538,10 @@ mod integration_tests { // Mine at least one block to ensure log is processed anvil_provider - .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await + .anvil_mine(Some(U256::from(1)), Some(U256::from(1))) + .await .expect("Failed to mine"); - // Poll the contract for up to 5 seconds to ensure that the challenge was successfull by querying balance let mut result = true; for i in 0..NUM_SCRIBE_INSTANCES { @@ -542,7 +555,8 @@ mod integration_tests { testing_logger::validate(|captured_logs| { let mut found: bool = false; for log in captured_logs { - found |= log.body + found |= log + .body .to_lowercase() .contains(&"Challenge started".to_lowercase()); } @@ -575,21 +589,28 @@ mod integration_tests { let (anvil, anvil_provider, signer) = create_anvil_instances(private_key, 8546).await; // Set to a low current time for now, this avoids having stale poke error later - anvil_provider.anvil_set_time(1000).await.expect("Failed to set time"); + anvil_provider + .anvil_set_time(1000) + .await + .expect("Failed to set time"); // Deploy scribe instance let scribe_optimistic = deploy_scribe(anvil_provider.clone(), signer.clone()).await; anvil_provider .anvil_set_balance( scribe_optimistic.address().clone(), - U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap() - ).await + U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap(), + ) + .await .expect("Unable to set balance"); // Update current anvil time to be far from last scribe config update // Set the anvil time to be in the past to ensure the challenge period is exceeded later let current_timestamp = (chrono::Utc::now().timestamp() as u64) - 400; - anvil_provider.anvil_set_time(current_timestamp).await.expect("Failed to set time"); + anvil_provider + .anvil_set_time(current_timestamp) + .await + .expect("Failed to set time"); // ------------------------------------------------------------------------------------------------------------ let cancel_token: CancellationToken = CancellationToken::new(); @@ -609,13 +630,15 @@ mod integration_tests { // Increase current block count to move away from poller intialisation block anvil_provider - .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await + .anvil_mine(Some(U256::from(1)), Some(U256::from(1))) + .await .expect("Failed to mine"); // ------------------------------------------------------------------------------------------------------------ // Assert that the current contract balance is not 0 let balance = anvil_provider - .get_balance(scribe_optimistic.address().clone()).await + .get_balance(scribe_optimistic.address().clone()) + .await .expect("Failed to get balance"); assert_ne!(balance, U256::from(0)); @@ -625,7 +648,8 @@ mod integration_tests { // Mine at least one block to ensure log is processed anvil_provider - .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await + .anvil_mine(Some(U256::from(1)), Some(U256::from(1))) + .await .expect("Failed to mine"); // Poll till expected logs are found @@ -637,10 +661,13 @@ mod integration_tests { let mut found: bool = false; for log in captured_logs { assert!( - !log.body.to_lowercase().contains(&"Challenge started".to_lowercase()), + !log.body + .to_lowercase() + .contains(&"Challenge started".to_lowercase()), "Challenge started log found" ); - found |= log.body + found |= log + .body .to_lowercase() .contains(&"OpPoked received outside of challenge period".to_lowercase()); } @@ -662,7 +689,7 @@ mod integration_tests { async fn create_anvil_instances( private_key: &str, - port: u16 + port: u16, ) -> (AnvilInstance, AnvilProvider, EthereumWallet) { let anvil: AnvilInstance = Anvil::new() .port(port) @@ -671,33 +698,38 @@ mod integration_tests { .try_spawn() .expect("Failed to spawn anvil"); - let signer: EthereumWallet = EthereumWallet::new( - private_key.parse::().unwrap() - ); + let signer: EthereumWallet = + EthereumWallet::new(private_key.parse::().unwrap()); let anvil_provider = Arc::new( ProviderBuilder::new() .with_recommended_fillers() .wallet(signer.clone()) .filler(ChainIdFiller::new(Some(31337))) - .on_http(anvil.endpoint_url()) + .on_http(anvil.endpoint_url()), ); // set initial balance for deployer anvil_provider .anvil_set_balance( signer.default_signer().address(), - U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap() - ).await + U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap(), + ) + .await .expect("Unable to set balance"); // configure anvil settings - anvil_provider.anvil_set_auto_mine(true).await.expect("Failed to set auto mine"); anvil_provider - .anvil_mine(Some(U256::from(3)), Some(U256::from(3))).await + .anvil_set_auto_mine(true) + .await + .expect("Failed to set auto mine"); + anvil_provider + .anvil_mine(Some(U256::from(3)), Some(U256::from(3))) + .await .expect("Failed to mine"); anvil_provider - .anvil_impersonate_account(signer.default_signer().address().clone()).await + .anvil_impersonate_account(signer.default_signer().address().clone()) + .await .expect("Failed to impersonate account"); return (anvil, anvil_provider, signer); @@ -705,7 +737,7 @@ mod integration_tests { async fn deploy_scribe, T: Clone + Transport, N: Network>( provider: P, - signer: EthereumWallet + signer: EthereumWallet, ) -> ScribeOptimisiticInstance { // deploy scribe instance let initial_authed = signer.default_signer().address(); @@ -716,11 +748,8 @@ mod integration_tests { 0123456789abcdef0123456789abcdef " ); - let scribe_optimistic = ScribeOptimisitic::deploy( - provider, - initial_authed.clone(), - FixedBytes(wat).clone() - ); + let scribe_optimistic = + ScribeOptimisitic::deploy(provider, initial_authed.clone(), FixedBytes(wat).clone()); let scribe_optimistic = scribe_optimistic.await.unwrap(); let receipt = scribe_optimistic.setBar(1); @@ -732,17 +761,23 @@ mod integration_tests { let pub_key = LibSecp256k1::Point { x: U256::from_str_radix( "95726579611468854699048782904089382286224374897874075137780214269565012360365", - 10 - ).unwrap(), + 10, + ) + .unwrap(), y: U256::from_str_radix( "95517337328947037046967076057450300670379811052080651187456799621439597083272", - 10 - ).unwrap(), + 10, + ) + .unwrap(), }; let ecdsa_data = IScribe::ECDSAData { v: 0x1b, - r: FixedBytes(hex!("0ced9fd231ad454eaac301d6e15a56b6aaa839a55d664757e3ace927e95948ec")), - s: FixedBytes(hex!("21b2813ad85945f320d7728fbfc9b83cbbb564135e67c16db16d5f4e74392119")), + r: FixedBytes(hex!( + "0ced9fd231ad454eaac301d6e15a56b6aaa839a55d664757e3ace927e95948ec" + )), + s: FixedBytes(hex!( + "21b2813ad85945f320d7728fbfc9b83cbbb564135e67c16db16d5f4e74392119" + )), }; let receipt = scribe_optimistic.lift_0(pub_key, ecdsa_data); let receipt = receipt.send().await.expect("Failed to lift validator"); @@ -760,16 +795,18 @@ mod integration_tests { url: Url, signer: EthereumWallet, cancel_token: CancellationToken, - addresses: Vec
+ addresses: Vec
, ) { - let client = ClientBuilder::default().layer(RetryBackoffLayer::new(15, 200, 300)).http(url); + let client = ClientBuilder::default() + .layer(RetryBackoffLayer::new(15, 200, 300)) + .http(url); let provider = Arc::new( ProviderBuilder::new() .with_recommended_fillers() .filler(ChainIdFiller::new(Some(31337))) .wallet(signer.clone()) - .on_client(client) + .on_client(client), ); let flashbot_provider = provider.clone(); @@ -785,7 +822,7 @@ mod integration_tests { cancel_token.clone(), provider.clone(), tx.clone(), - 1 + 1, ); // Create event distributor @@ -794,7 +831,7 @@ mod integration_tests { cancel_token.clone(), provider.clone(), flashbot_provider.clone(), - rx + rx, ); // Run events listener process @@ -824,18 +861,20 @@ mod integration_tests { async fn poll_balance_is_zero( anvil_provider: &AnvilProvider, address: &Address, - timeout: u64 + timeout: u64, ) -> bool { let start_time = chrono::Utc::now().timestamp() as u64; while (chrono::Utc::now().timestamp() as u64) < start_time + timeout { let balance = anvil_provider - .get_balance(address.clone()).await + .get_balance(address.clone()) + .await .expect("Failed to get balance"); if balance == U256::from(0) { return true; } anvil_provider - .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await + .anvil_mine(Some(U256::from(1)), Some(U256::from(1))) + .await .expect("Failed to mine"); tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; } @@ -845,22 +884,23 @@ mod integration_tests { async fn make_invalid_op_poke( age: u64, private_key: &str, - scribe_optimisitic: &ScribeOptimisiticInstance, AnvilProvider, Ethereum> + scribe_optimisitic: &ScribeOptimisiticInstance, AnvilProvider, Ethereum>, ) { let poke_data: IScribe::PokeData = IScribe::PokeData { val: 10, age: age as u32, }; let schnorr_data = IScribe::SchnorrData { - signature: FixedBytes( - hex!("0000000000000000000000000000000000000000000000000000000000000000") - ), + signature: FixedBytes(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), commitment: alloy::primitives::Address::ZERO.clone(), feedIds: hex!("00").into(), }; let op_poke_message = scribe_optimisitic .constructOpPokeMessage(poke_data.clone(), schnorr_data.clone()) - .call().await + .call() + .await .expect("Failed to read current age"); let op_poke_message = op_poke_message._0; let ecdsa_signer = private_key @@ -876,11 +916,8 @@ mod integration_tests { }; // Make the invalid poke - let receipt = scribe_optimisitic.opPoke( - poke_data.clone(), - schnorr_data.clone(), - ecdsa_data - ); + let receipt = + scribe_optimisitic.opPoke(poke_data.clone(), schnorr_data.clone(), ecdsa_data); let receipt = receipt.send().await.expect("Failed to send op poke"); receipt.watch().await.expect("Failed to watch op poke"); } From f23b4e95da578529ac92c2cf1b8b5d21c9ff1015 Mon Sep 17 00:00:00 2001 From: Konstantin Zolotarev Date: Fri, 25 Oct 2024 17:37:38 +0300 Subject: [PATCH 21/40] Formatted --- src/main.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 0be884d..571d0ef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -189,7 +189,6 @@ async fn main() -> Result<()> { .unwrap_or(String::from("9090")) .parse::() .unwrap(); - let addr = SocketAddr::from(([0, 0, 0, 0], port)); let _ = builder.with_http_listener(addr).install(); From 5881c96a795c0ba768bb577a54aa755ad33beb11 Mon Sep 17 00:00:00 2001 From: Max Wickham Date: Mon, 28 Oct 2024 13:06:27 +0000 Subject: [PATCH 22/40] test 100 scribes --- Cargo.lock | 1 + crates/scribe/Cargo.toml | 1 + crates/scribe/src/contract.rs | 9 + crates/scribe/src/event_handler.rs | 7 +- crates/scribe/src/events_listener.rs | 19 +- src/main.rs | 342 ++++++++++++++------------- 6 files changed, 195 insertions(+), 184 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0344fdb..a6adde1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3452,6 +3452,7 @@ dependencies = [ "eyre", "log", "metrics", + "once_cell", "tokio", "tokio-util", "tower 0.5.1", diff --git a/crates/scribe/Cargo.toml b/crates/scribe/Cargo.toml index a7e657f..b765b29 100644 --- a/crates/scribe/Cargo.toml +++ b/crates/scribe/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] alloy = {workspace = true, features = ["full"]} eyre = {workspace = true} +once_cell = "1.10.0" log = {workspace = true} tokio = {workspace = true, features = ["full"]} tokio-util = {workspace = true} diff --git a/crates/scribe/src/contract.rs b/crates/scribe/src/contract.rs index c6fb171..2c89043 100644 --- a/crates/scribe/src/contract.rs +++ b/crates/scribe/src/contract.rs @@ -21,7 +21,9 @@ use alloy::{ sol, sol_types::SolEvent, }; +use once_cell::sync::Lazy; use eyre::{bail, Result, WrapErr}; +use tokio::sync::Mutex; use IScribe::SchnorrData; use ScribeOptimistic::{OpPoked, ScribeOptimisticInstance}; @@ -127,6 +129,7 @@ impl ScribeOptimisticProviderInstance { } } + impl ScribeOptimisticProvider for ScribeOptimisticProviderInstance { async fn get_challenge_period(&self) -> Result { Ok(self.contract.opChallengePeriod().call().await?._0) @@ -152,7 +155,13 @@ impl ScribeOptimisticProvider for ScribeOptimisticProviderInstance { Ok(acceptable) } + async fn challenge(&self, schnorr_data: SchnorrData) -> Result> { + // This ensures only one challenge is happening at a time, + // (this way multiple signed transactions with same nonce aren't sent) + // static CHALLENGE_GAURD: Lazy> = Lazy::new(|| tokio::sync::Mutex::new(())); + // let _gaurd = CHALLENGE_GAURD.lock().await; + log::debug!("{:?} Challenging OpPoke", self.contract.address()); let from_address = self.contract.address(); log::info!("Challenging from address: {:?}", from_address); diff --git a/crates/scribe/src/event_handler.rs b/crates/scribe/src/event_handler.rs index 4a4f484..1bc5cac 100644 --- a/crates/scribe/src/event_handler.rs +++ b/crates/scribe/src/event_handler.rs @@ -18,7 +18,7 @@ use std::sync::Arc; use std::time::Duration; use alloy::primitives::Address; -use alloy::providers::Provider; +use alloy::providers::{Provider, WalletProvider}; use alloy::rpc::types::BlockTransactionsKind; use eyre::Result; use tokio::sync::mpsc::{Receiver, Sender}; @@ -185,6 +185,7 @@ impl EventHandler { } }; + // Commented code left incase we need to cahnge to using chain as source of truth of time // let latest_block_number = self.provider.get_block_number().await?; // let latest_block = self.provider.get_block(latest_block_number.into(), BlockTransactionsKind::Hashes).await.unwrap(); // let current_timestamp = latest_block.unwrap().header.timestamp; @@ -304,6 +305,7 @@ impl ChallengeHandler { // This checked for in tests, tests must be updated if log is changed log::debug!("Challenge started"); // Perform the challenge after 200ms + let provider = Mutex::new(self.provider.clone()); let mut challenge_attempts: u64 = 0; loop { @@ -331,9 +333,12 @@ impl ChallengeHandler { const RETRY_RANGE_END: u64 = CLASSIC_CHALLENGE_RETRY_COUNT+FLASHBOT_CHALLENGE_RETRY_COUNT; match challenge_attempts { 0..FLASHBOT_CHALLENGE_RETRY_COUNT => { + let contract = ScribeOptimisticProviderInstance::new( self.address, self.flashbot_provider.clone() ); + + let result = contract.challenge( self.op_poked.schnorrData.clone() ).await; diff --git a/crates/scribe/src/events_listener.rs b/crates/scribe/src/events_listener.rs index 579e666..c3dca2a 100644 --- a/crates/scribe/src/events_listener.rs +++ b/crates/scribe/src/events_listener.rs @@ -20,8 +20,7 @@ use alloy::{ primitives::Address, providers::{ fillers::{ - BlobGasFiller, ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, - WalletFiller, + BlobGasFiller, CachedNonceManager, ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, WalletFiller }, Identity, Provider, RootProvider, WalletProvider, }, @@ -51,21 +50,7 @@ const MAX_ADDRESS_PER_REQUEST: usize = 50; pub type RpcRetryProvider = RetryBackoffService>; /// The provider type used to interact with the Ethereum network with a signer. -pub type RetryProviderWithSigner = FillProvider< - JoinFill< - JoinFill< - JoinFill< - Identity, - JoinFill>>, - >, - ChainIdFiller, - >, - WalletFiller, - >, - RootProvider>>, - RetryBackoffService>, - Ethereum, ->; +pub type RetryProviderWithSigner = FillProvider>>>, ChainIdFiller>, NonceFiller>, WalletFiller>, RootProvider>>, RetryBackoffService>, Ethereum>; #[derive(Debug, Clone)] pub struct Poller { diff --git a/src/main.rs b/src/main.rs index 0be884d..b780cf5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,21 +14,23 @@ // along with this program. If not, see . use alloy::primitives::Address; -use alloy::providers::fillers::ChainIdFiller; -use alloy::providers::ProviderBuilder; +use alloy::providers::fillers::{CachedNonceManager, ChainIdFiller, NonceFiller}; +use alloy::providers::{ Provider, ProviderBuilder }; use alloy::rpc::client::ClientBuilder; use alloy::transports::layers::RetryBackoffLayer; use clap::Parser; use env_logger::Env; use eyre::Result; -use log::{error, info}; +use futures_util::lock::Mutex; +use futures_util::TryFutureExt; +use log::{ error, info }; use scribe::contract::EventWithMetadata; use scribe::events_listener::Poller; use scribe::metrics; use std::net::SocketAddr; use std::sync::Arc; -use std::{env, panic, path::PathBuf, time::Duration}; +use std::{ env, panic, path::PathBuf, time::Duration }; use tokio::time::sleep; use tokio_util::sync::CancellationToken; @@ -37,9 +39,9 @@ use scribe::event_handler; mod wallet; use tokio::task::JoinSet; -use tokio::{select, signal}; +use tokio::{ select, signal }; -use wallet::{CustomWallet, KeystoreWallet, PrivateKeyWallet}; +use wallet::{ CustomWallet, KeystoreWallet, PrivateKeyWallet }; use metrics_exporter_prometheus::PrometheusBuilder; use metrics_process::Collector; @@ -59,10 +61,7 @@ struct Cli { #[arg(long, help = "Node HTTP RPC_URL, normally starts with https://****")] rpc_url: String, - #[arg( - long, - help = "Flashbot Node HTTP RPC_URL, normally starts with https://****" - )] + #[arg(long, help = "Flashbot Node HTTP RPC_URL, normally starts with https://****")] flashbot_rpc_url: String, #[arg( @@ -78,11 +77,7 @@ struct Cli { )] keystore_path: Option, - #[arg( - long = "password", - requires = "keystore_path", - help = "Key raw password as text" - )] + #[arg(long = "password", requires = "keystore_path", help = "Key raw password as text")] raw_password: Option, #[arg( @@ -93,10 +88,7 @@ struct Cli { )] password_file: Option, - #[arg( - long, - help = "If no chain_id provided binary will try to get chain_id from given RPC" - )] + #[arg(long, help = "If no chain_id provided binary will try to get chain_id from given RPC")] chain_id: Option, } @@ -133,10 +125,9 @@ async fn main() -> Result<()> { // Building tx signer for provider let signer = args.wallet()?.unwrap(); - info!( - "Using {:?} for signing transactions.", - signer.default_signer().address() - ); + info!("Using {:?} for signing transactions.", signer.default_signer().address()); + let nonce_mananger = NonceFiller::::default(); + // Create new HTTP client with retry backoff layer let client = ClientBuilder::default() @@ -149,9 +140,10 @@ async fn main() -> Result<()> { .with_recommended_fillers() // Add chain id request from rpc .filler(ChainIdFiller::new(args.chain_id)) + .filler(nonce_mananger.clone()) // Add default signer .wallet(signer.clone()) - .on_client(client), + .on_client(client) ); // Create new HTTP client for flashbots @@ -166,11 +158,14 @@ async fn main() -> Result<()> { .with_recommended_fillers() // Add chain id request from rpc .filler(ChainIdFiller::new(args.chain_id)) + .filler(nonce_mananger.clone()) // Add default signer .wallet(signer.clone()) - .on_client(flashbot_client), + .on_client(flashbot_client) ); + // let signer_lock = Arc::new(Mutex::new(signer)); + let mut set = JoinSet::new(); let cancel_token = CancellationToken::new(); @@ -185,10 +180,7 @@ async fn main() -> Result<()> { // Register Prometheus metrics let builder = PrometheusBuilder::new(); - let port = env::var("HTTP_PORT") - .unwrap_or(String::from("9090")) - .parse::() - .unwrap(); + let port = env::var("HTTP_PORT").unwrap_or(String::from("9090")).parse::().unwrap(); let addr = SocketAddr::from(([0, 0, 0, 0], port)); @@ -213,7 +205,7 @@ async fn main() -> Result<()> { cancel_token.clone(), provider.clone(), tx.clone(), - 30, + 30 ); // Create event distributor @@ -222,7 +214,7 @@ async fn main() -> Result<()> { cancel_token.clone(), provider.clone(), flashbot_provider.clone(), - rx, + rx ); // Run events listener process @@ -299,7 +291,7 @@ mod tests { let cli = Cli { addresses: vec![], raw_secret_key: Some( - "def90b5b5cb2d68c5cd9de7b3e6d767cbb1b8d5fd8560bd6c42cbc4a4da30b16".to_string(), + "def90b5b5cb2d68c5cd9de7b3e6d767cbb1b8d5fd8560bd6c42cbc4a4da30b16".to_string() ), chain_id: None, keystore_path: None, @@ -320,7 +312,7 @@ mod tests { let cli = Cli { addresses: vec![], raw_secret_key: Some( - "0xdef90b5b5cb2d68c5cd9de7b3e6d767cbb1b8d5fd8560bd6c42cbc4a4da30b16".to_string(), + "0xdef90b5b5cb2d68c5cd9de7b3e6d767cbb1b8d5fd8560bd6c42cbc4a4da30b16".to_string() ), chain_id: None, keystore_path: None, @@ -341,11 +333,13 @@ mod tests { #[test] fn keystore_works_with_password_in_file() { let keystore = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/keystore"); - let keystore_file = keystore - .join("UTC--2022-12-20T10-30-43.591916000Z--ec554aeafe75601aaab43bd4621a22284db566c2"); + let keystore_file = keystore.join( + "UTC--2022-12-20T10-30-43.591916000Z--ec554aeafe75601aaab43bd4621a22284db566c2" + ); - let keystore_password_file = - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/keystore/password"); + let keystore_password_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join( + "tests/fixtures/keystore/password" + ); let cli = Cli { addresses: vec![], @@ -369,8 +363,9 @@ mod tests { #[test] fn keystore_works_with_raw_password() { let keystore = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/keystore"); - let keystore_file = keystore - .join("UTC--2022-12-20T10-30-43.591916000Z--ec554aeafe75601aaab43bd4621a22284db566c2"); + let keystore_file = keystore.join( + "UTC--2022-12-20T10-30-43.591916000Z--ec554aeafe75601aaab43bd4621a22284db566c2" + ); let cli = Cli { addresses: vec![], @@ -397,30 +392,42 @@ mod integration_tests { use core::panic; use std::cell::Cell; use std::sync::Arc; + use std::time::Duration; use std::vec; use alloy::hex; - use alloy::network::{Ethereum, EthereumWallet, Network}; - use alloy::node_bindings::{Anvil, AnvilInstance}; + use alloy::network::{ Ethereum, EthereumWallet, Network, NetworkWallet }; + use alloy::node_bindings::{ Anvil, AnvilInstance }; use alloy::primitives::U256; - use alloy::primitives::{Address, FixedBytes}; + use alloy::primitives::{ Address, FixedBytes }; use alloy::providers::ext::AnvilApi; use alloy::providers::fillers::{ - BlobGasFiller, ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, WalletFiller, + BlobGasFiller, + CachedNonceManager, + ChainIdFiller, + FillProvider, + GasFiller, + JoinFill, + NonceFiller, + WalletFiller, }; - use alloy::providers::{Identity, Provider, ProviderBuilder, RootProvider}; + use alloy::providers::{ Identity, Provider, ProviderBuilder, RootProvider }; use alloy::rpc::client::ClientBuilder; use alloy::signers::local::PrivateKeySigner; use alloy::signers::SignerSync; use alloy::transports::http::reqwest::Url; - use alloy::transports::http::{Client, Http}; + use alloy::transports::http::{ Client, Http }; use alloy::transports::layers::RetryBackoffLayer; use alloy::transports::Transport; + use futures_util::future::join_all; use scribe::contract::EventWithMetadata; use scribe::event_handler; use scribe::events_listener::Poller; use scribe_optimistic::{ - IScribe, LibSecp256k1, ScribeOptimisitic, ScribeOptimisitic::ScribeOptimisiticInstance, + IScribe, + LibSecp256k1, + ScribeOptimisitic, + ScribeOptimisitic::ScribeOptimisiticInstance, }; use tokio::task::JoinSet; use tokio_util::sync::CancellationToken; @@ -443,20 +450,23 @@ mod integration_tests { JoinFill< JoinFill< JoinFill< - Identity, JoinFill< - GasFiller, - JoinFill>, + Identity, + JoinFill< + GasFiller, + JoinFill> + > >, + WalletFiller >, - WalletFiller, + ChainIdFiller >, - ChainIdFiller, + NonceFiller >, RootProvider>, Http, - Ethereum, - >, + Ethereum + > >; const PRIVATE_KEY: &str = "d4cf162c2e26ff75095922ea108d516ff07bdd732f050e64ced632980f11320b"; @@ -464,47 +474,63 @@ mod integration_tests { // -- Tests -- #[tokio::test] - async fn challenge_constract() { + async fn challenge_contract() { // Test an invalid poke on multiple scribe instances is successfully challenged - const NUM_SCRIBE_INSTANCES: usize = 10; + const NUM_SCRIBE_INSTANCES: usize = 100; + // ------------------------------------------------------------------------------------------------------------ let private_key = PRIVATE_KEY; let (anvil, anvil_provider, signer) = create_anvil_instances(private_key, 8545).await; // set to a low current time for now, this avoids having stale poke error later - anvil_provider - .anvil_set_time(1000) - .await - .expect("Failed to set time"); + anvil_provider.anvil_set_time(1000).await.expect("Failed to set time"); - // deploy scribe instance + + + let mut deployments = vec![]; let mut scribes = vec![]; + for i in 0..NUM_SCRIBE_INSTANCES { + deployments.push(deploy_scribe(anvil_provider.clone(), signer.clone())); + // Only deploy at most 30 in parralell + if i%30 == 0{ + scribes.append(&mut join_all(deployments).await); + deployments = vec![]; + } + } + scribes.append(&mut join_all(deployments).await); let mut scribe_addresses = vec![]; + + let mut balance_updates = vec![]; for i in 0..NUM_SCRIBE_INSTANCES { - let scribe_optimistic = deploy_scribe(anvil_provider.clone(), signer.clone()).await; + let scribe_optimistic = &scribes[i]; scribe_addresses.push(scribe_optimistic.address().clone()); - scribes.push(scribe_optimistic); - anvil_provider - .anvil_set_balance( + balance_updates.push( + anvil_provider.anvil_set_balance( scribes[i].address().clone(), - U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap(), + U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap() ) - .await - .expect("Unable to set balance"); + ); + } + for balance_update in join_all(balance_updates).await { + balance_update.expect("Unable to set balance"); } // Update current anvil time to be far from last scribe config update let current_timestamp = (chrono::Utc::now().timestamp() as u64) - 100; - anvil_provider - .anvil_set_time(current_timestamp) - .await - .expect("Failed to set time"); + anvil_provider.anvil_set_time(current_timestamp).await.expect("Failed to set time"); // ------------------------------------------------------------------------------------------------------------ let cancel_token: CancellationToken = CancellationToken::new(); // start the event listener as a sub process { + let signer: EthereumWallet = EthereumWallet::new(PrivateKeySigner::random()); + anvil_provider + .anvil_set_balance( + signer.default_signer().address().clone(), + U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap() + ).await + .expect("Unable to set balance"); let addresses = scribe_addresses.clone(); let cancel_token = cancel_token.clone(); let url = anvil.endpoint_url(); @@ -513,40 +539,38 @@ mod integration_tests { start_event_listener(url, signer, cancel_token, addresses).await; }); } + // Let first poll occur on poller tokio::time::sleep(tokio::time::Duration::from_millis(1200)).await; // Increase current block count to move away from poller intialisation block anvil_provider - .anvil_mine(Some(U256::from(1)), Some(U256::from(1))) - .await + .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await .expect("Failed to mine"); // ------------------------------------------------------------------------------------------------------------ + let mut invalid_pokes = vec![]; for i in 0..NUM_SCRIBE_INSTANCES { // Assert that the current contract balance is not 0 let balance = anvil_provider - .get_balance(scribe_addresses[i]) - .await + .get_balance(scribe_addresses[i]).await .expect("Failed to get balance"); assert_ne!(balance, U256::from(0)); - let current_timestamp = (chrono::Utc::now().timestamp() as u64) - 100; - - // Make invalid poke - make_invalid_op_poke(current_timestamp, private_key, &scribes[i]).await; + invalid_pokes.push(make_invalid_op_poke(current_timestamp, private_key, &scribes[i])); } + join_all(invalid_pokes).await; // Mine at least one block to ensure log is processed anvil_provider - .anvil_mine(Some(U256::from(1)), Some(U256::from(1))) - .await + .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await .expect("Failed to mine"); - // Poll the contract for up to 5 seconds to ensure that the challenge was successfull by querying balance - let mut result = true; - for i in 0..NUM_SCRIBE_INSTANCES { - result &= poll_balance_is_zero(&anvil_provider, &scribe_addresses[i], 5).await; - } + let result = join_all( + (0..NUM_SCRIBE_INSTANCES) + .map(|i| poll_balance_is_zero(&anvil_provider, &scribe_addresses[i], 10)) + ) + .await.iter().all(|&result| result); + // Poll to check that the challenge started log is found, // (to ensure log hasn't been changed as its non appearance is looked for in other tests) @@ -555,10 +579,7 @@ mod integration_tests { testing_logger::validate(|captured_logs| { let mut found: bool = false; for log in captured_logs { - found |= log - .body - .to_lowercase() - .contains(&"Challenge started".to_lowercase()); + found |= log.body.to_lowercase().contains(&"Challenge started".to_lowercase()); } success.set(found); }); @@ -589,34 +610,34 @@ mod integration_tests { let (anvil, anvil_provider, signer) = create_anvil_instances(private_key, 8546).await; // Set to a low current time for now, this avoids having stale poke error later - anvil_provider - .anvil_set_time(1000) - .await - .expect("Failed to set time"); + anvil_provider.anvil_set_time(1000).await.expect("Failed to set time"); // Deploy scribe instance let scribe_optimistic = deploy_scribe(anvil_provider.clone(), signer.clone()).await; anvil_provider .anvil_set_balance( scribe_optimistic.address().clone(), - U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap(), - ) - .await + U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap() + ).await .expect("Unable to set balance"); // Update current anvil time to be far from last scribe config update // Set the anvil time to be in the past to ensure the challenge period is exceeded later let current_timestamp = (chrono::Utc::now().timestamp() as u64) - 400; - anvil_provider - .anvil_set_time(current_timestamp) - .await - .expect("Failed to set time"); + anvil_provider.anvil_set_time(current_timestamp).await.expect("Failed to set time"); // ------------------------------------------------------------------------------------------------------------ let cancel_token: CancellationToken = CancellationToken::new(); // start the event listener as a sub process { + let signer: EthereumWallet = EthereumWallet::new(PrivateKeySigner::random()); + anvil_provider + .anvil_set_balance( + signer.default_signer().address().clone(), + U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap() + ).await + .expect("Unable to set balance"); let addresses = vec![scribe_optimistic.address().clone()]; let cancel_token = cancel_token.clone(); let url = anvil.endpoint_url(); @@ -630,15 +651,13 @@ mod integration_tests { // Increase current block count to move away from poller intialisation block anvil_provider - .anvil_mine(Some(U256::from(1)), Some(U256::from(1))) - .await + .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await .expect("Failed to mine"); // ------------------------------------------------------------------------------------------------------------ // Assert that the current contract balance is not 0 let balance = anvil_provider - .get_balance(scribe_optimistic.address().clone()) - .await + .get_balance(scribe_optimistic.address().clone()).await .expect("Failed to get balance"); assert_ne!(balance, U256::from(0)); @@ -648,8 +667,7 @@ mod integration_tests { // Mine at least one block to ensure log is processed anvil_provider - .anvil_mine(Some(U256::from(1)), Some(U256::from(1))) - .await + .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await .expect("Failed to mine"); // Poll till expected logs are found @@ -661,13 +679,10 @@ mod integration_tests { let mut found: bool = false; for log in captured_logs { assert!( - !log.body - .to_lowercase() - .contains(&"Challenge started".to_lowercase()), + !log.body.to_lowercase().contains(&"Challenge started".to_lowercase()), "Challenge started log found" ); - found |= log - .body + found |= log.body .to_lowercase() .contains(&"OpPoked received outside of challenge period".to_lowercase()); } @@ -689,7 +704,7 @@ mod integration_tests { async fn create_anvil_instances( private_key: &str, - port: u16, + port: u16 ) -> (AnvilInstance, AnvilProvider, EthereumWallet) { let anvil: AnvilInstance = Anvil::new() .port(port) @@ -698,38 +713,35 @@ mod integration_tests { .try_spawn() .expect("Failed to spawn anvil"); - let signer: EthereumWallet = - EthereumWallet::new(private_key.parse::().unwrap()); + let signer: EthereumWallet = EthereumWallet::new( + private_key.parse::().unwrap() + ); let anvil_provider = Arc::new( ProviderBuilder::new() + // .with_cached_nonce_management() .with_recommended_fillers() .wallet(signer.clone()) .filler(ChainIdFiller::new(Some(31337))) - .on_http(anvil.endpoint_url()), + .filler(NonceFiller::::default()) + .on_http(anvil.endpoint_url()) ); // set initial balance for deployer anvil_provider .anvil_set_balance( signer.default_signer().address(), - U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap(), - ) - .await + U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap() + ).await .expect("Unable to set balance"); // configure anvil settings + anvil_provider.anvil_set_auto_mine(true).await.expect("Failed to set auto mine"); anvil_provider - .anvil_set_auto_mine(true) - .await - .expect("Failed to set auto mine"); - anvil_provider - .anvil_mine(Some(U256::from(3)), Some(U256::from(3))) - .await + .anvil_mine(Some(U256::from(3)), Some(U256::from(3))).await .expect("Failed to mine"); anvil_provider - .anvil_impersonate_account(signer.default_signer().address().clone()) - .await + .anvil_impersonate_account(signer.default_signer().address().clone()).await .expect("Failed to impersonate account"); return (anvil, anvil_provider, signer); @@ -737,7 +749,7 @@ mod integration_tests { async fn deploy_scribe, T: Clone + Transport, N: Network>( provider: P, - signer: EthereumWallet, + signer: EthereumWallet ) -> ScribeOptimisiticInstance { // deploy scribe instance let initial_authed = signer.default_signer().address(); @@ -748,45 +760,41 @@ mod integration_tests { 0123456789abcdef0123456789abcdef " ); - let scribe_optimistic = - ScribeOptimisitic::deploy(provider, initial_authed.clone(), FixedBytes(wat).clone()); + let scribe_optimistic = ScribeOptimisitic::deploy( + provider, + initial_authed.clone(), + FixedBytes(wat).clone() + ); let scribe_optimistic = scribe_optimistic.await.unwrap(); - let receipt = scribe_optimistic.setBar(1); let receipt = receipt.send().await.expect("Failed to set bar"); - receipt.watch().await.expect("Failed to set bar"); + receipt.with_timeout(Some(Duration::from_secs(15))).watch().await.expect("Failed to set bar"); // TODO generate the public key and v r s from private key // lift validator let pub_key = LibSecp256k1::Point { x: U256::from_str_radix( "95726579611468854699048782904089382286224374897874075137780214269565012360365", - 10, - ) - .unwrap(), + 10 + ).unwrap(), y: U256::from_str_radix( "95517337328947037046967076057450300670379811052080651187456799621439597083272", - 10, - ) - .unwrap(), + 10 + ).unwrap(), }; let ecdsa_data = IScribe::ECDSAData { v: 0x1b, - r: FixedBytes(hex!( - "0ced9fd231ad454eaac301d6e15a56b6aaa839a55d664757e3ace927e95948ec" - )), - s: FixedBytes(hex!( - "21b2813ad85945f320d7728fbfc9b83cbbb564135e67c16db16d5f4e74392119" - )), + r: FixedBytes(hex!("0ced9fd231ad454eaac301d6e15a56b6aaa839a55d664757e3ace927e95948ec")), + s: FixedBytes(hex!("21b2813ad85945f320d7728fbfc9b83cbbb564135e67c16db16d5f4e74392119")), }; let receipt = scribe_optimistic.lift_0(pub_key, ecdsa_data); let receipt = receipt.send().await.expect("Failed to lift validator"); - receipt.watch().await.expect("Failed to lift validator"); + receipt.with_timeout(Some(Duration::from_secs(15))).watch().await.expect("Failed to lift validator"); // set challenge period let receipt = scribe_optimistic.setOpChallengePeriod(300); let receipt = receipt.send().await.expect("Failed to lift validator"); - receipt.watch().await.expect("Failed to set opChallenge"); + receipt.with_timeout(Some(Duration::from_secs(15))).watch().await.expect("Failed to set opChallenge"); return scribe_optimistic; } @@ -795,18 +803,20 @@ mod integration_tests { url: Url, signer: EthereumWallet, cancel_token: CancellationToken, - addresses: Vec
, + addresses: Vec
) { - let client = ClientBuilder::default() - .layer(RetryBackoffLayer::new(15, 200, 300)) - .http(url); + let client = ClientBuilder::default().layer(RetryBackoffLayer::new(15, 200, 300)).http(url); + + let nonce_mananger = NonceFiller::::default(); let provider = Arc::new( ProviderBuilder::new() .with_recommended_fillers() + .filler(ChainIdFiller::new(Some(31337))) + .filler(nonce_mananger.clone()) .wallet(signer.clone()) - .on_client(client), + .on_client(client) ); let flashbot_provider = provider.clone(); @@ -822,7 +832,7 @@ mod integration_tests { cancel_token.clone(), provider.clone(), tx.clone(), - 1, + 1 ); // Create event distributor @@ -831,7 +841,7 @@ mod integration_tests { cancel_token.clone(), provider.clone(), flashbot_provider.clone(), - rx, + rx ); // Run events listener process @@ -861,20 +871,18 @@ mod integration_tests { async fn poll_balance_is_zero( anvil_provider: &AnvilProvider, address: &Address, - timeout: u64, + timeout: u64 ) -> bool { let start_time = chrono::Utc::now().timestamp() as u64; while (chrono::Utc::now().timestamp() as u64) < start_time + timeout { let balance = anvil_provider - .get_balance(address.clone()) - .await + .get_balance(address.clone()).await .expect("Failed to get balance"); if balance == U256::from(0) { return true; } anvil_provider - .anvil_mine(Some(U256::from(1)), Some(U256::from(1))) - .await + .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await .expect("Failed to mine"); tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; } @@ -884,23 +892,22 @@ mod integration_tests { async fn make_invalid_op_poke( age: u64, private_key: &str, - scribe_optimisitic: &ScribeOptimisiticInstance, AnvilProvider, Ethereum>, + scribe_optimisitic: &ScribeOptimisiticInstance, AnvilProvider, Ethereum> ) { let poke_data: IScribe::PokeData = IScribe::PokeData { val: 10, age: age as u32, }; let schnorr_data = IScribe::SchnorrData { - signature: FixedBytes(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), + signature: FixedBytes( + hex!("0000000000000000000000000000000000000000000000000000000000000000") + ), commitment: alloy::primitives::Address::ZERO.clone(), feedIds: hex!("00").into(), }; let op_poke_message = scribe_optimisitic .constructOpPokeMessage(poke_data.clone(), schnorr_data.clone()) - .call() - .await + .call().await .expect("Failed to read current age"); let op_poke_message = op_poke_message._0; let ecdsa_signer = private_key @@ -916,8 +923,11 @@ mod integration_tests { }; // Make the invalid poke - let receipt = - scribe_optimisitic.opPoke(poke_data.clone(), schnorr_data.clone(), ecdsa_data); + let receipt = scribe_optimisitic.opPoke( + poke_data.clone(), + schnorr_data.clone(), + ecdsa_data + ); let receipt = receipt.send().await.expect("Failed to send op poke"); receipt.watch().await.expect("Failed to watch op poke"); } From d62eb5520d6eb5d00bdd8d3cf982753b067b27b0 Mon Sep 17 00:00:00 2001 From: Max Wickham Date: Mon, 28 Oct 2024 13:40:59 +0000 Subject: [PATCH 23/40] test: 100 scribe instances running stably --- src/main.rs | 71 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 26 deletions(-) diff --git a/src/main.rs b/src/main.rs index c5958c2..be977c7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,16 +14,14 @@ // along with this program. If not, see . use alloy::primitives::Address; -use alloy::providers::fillers::{CachedNonceManager, ChainIdFiller, NonceFiller}; -use alloy::providers::{ Provider, ProviderBuilder }; +use alloy::providers::fillers::{ CachedNonceManager, ChainIdFiller, NonceFiller }; +use alloy::providers::ProviderBuilder; use alloy::rpc::client::ClientBuilder; use alloy::transports::layers::RetryBackoffLayer; use clap::Parser; use env_logger::Env; use eyre::Result; -use futures_util::lock::Mutex; -use futures_util::TryFutureExt; use log::{ error, info }; use scribe::contract::EventWithMetadata; use scribe::events_listener::Poller; @@ -128,7 +126,6 @@ async fn main() -> Result<()> { info!("Using {:?} for signing transactions.", signer.default_signer().address()); let nonce_mananger = NonceFiller::::default(); - // Create new HTTP client with retry backoff layer let client = ClientBuilder::default() .layer(RetryBackoffLayer::new(15, 200, 300)) @@ -180,10 +177,7 @@ async fn main() -> Result<()> { // Register Prometheus metrics let builder = PrometheusBuilder::new(); - let port = env::var("HTTP_PORT") - .unwrap_or(String::from("9090")) - .parse::() - .unwrap(); + let port = env::var("HTTP_PORT").unwrap_or(String::from("9090")).parse::().unwrap(); let addr = SocketAddr::from(([0, 0, 0, 0], port)); let _ = builder.with_http_listener(addr).install(); @@ -477,8 +471,9 @@ mod integration_tests { #[tokio::test] async fn challenge_contract() { - // Test an invalid poke on multiple scribe instances is successfully challenged + // Test an invalid poke on multiple scribe instances in parrallel are successfully challenged const NUM_SCRIBE_INSTANCES: usize = 100; + const CHALLENGE_PERIOD: u16 = 1000; // ------------------------------------------------------------------------------------------------------------ let private_key = PRIVATE_KEY; @@ -487,14 +482,14 @@ mod integration_tests { // set to a low current time for now, this avoids having stale poke error later anvil_provider.anvil_set_time(1000).await.expect("Failed to set time"); - - let mut deployments = vec![]; let mut scribes = vec![]; for i in 0..NUM_SCRIBE_INSTANCES { - deployments.push(deploy_scribe(anvil_provider.clone(), signer.clone())); - // Only deploy at most 30 in parralell - if i%30 == 0{ + deployments.push( + deploy_scribe(anvil_provider.clone(), signer.clone(), CHALLENGE_PERIOD) + ); + // Only deploy at most 20 in parrallel + if i % 20 == 0 { scribes.append(&mut join_all(deployments).await); deployments = vec![]; } @@ -518,7 +513,10 @@ mod integration_tests { } // Update current anvil time to be far from last scribe config update - let current_timestamp = (chrono::Utc::now().timestamp() as u64) - 100; + // The more scribe instances the firther back in time a the anvil block timestamp doesn't stay in sync with chrono + // TODO add challenge period variable + let current_timestamp = + (chrono::Utc::now().timestamp() as u64) - (CHALLENGE_PERIOD as u64) + 1; anvil_provider.anvil_set_time(current_timestamp).await.expect("Failed to set time"); // ------------------------------------------------------------------------------------------------------------ @@ -559,6 +557,11 @@ mod integration_tests { .expect("Failed to get balance"); assert_ne!(balance, U256::from(0)); invalid_pokes.push(make_invalid_op_poke(current_timestamp, private_key, &scribes[i])); + // Do at most 50 at the same time + if i % 50 == 0 { + join_all(invalid_pokes).await; + invalid_pokes = vec![]; + } } join_all(invalid_pokes).await; @@ -568,11 +571,16 @@ mod integration_tests { .expect("Failed to mine"); let result = join_all( - (0..NUM_SCRIBE_INSTANCES) - .map(|i| poll_balance_is_zero(&anvil_provider, &scribe_addresses[i], 10)) + (0..NUM_SCRIBE_INSTANCES).map(|i| + poll_balance_is_zero( + &anvil_provider, + &scribe_addresses[i], + (20 + (10 * NUM_SCRIBE_INSTANCES) / 100).try_into().unwrap() + ) ) - .await.iter().all(|&result| result); - + ).await + .iter() + .all(|&result| result); // Poll to check that the challenge started log is found, // (to ensure log hasn't been changed as its non appearance is looked for in other tests) @@ -615,7 +623,7 @@ mod integration_tests { anvil_provider.anvil_set_time(1000).await.expect("Failed to set time"); // Deploy scribe instance - let scribe_optimistic = deploy_scribe(anvil_provider.clone(), signer.clone()).await; + let scribe_optimistic = deploy_scribe(anvil_provider.clone(), signer.clone(), 300).await; anvil_provider .anvil_set_balance( scribe_optimistic.address().clone(), @@ -751,7 +759,8 @@ mod integration_tests { async fn deploy_scribe, T: Clone + Transport, N: Network>( provider: P, - signer: EthereumWallet + signer: EthereumWallet, + challenge_period: u16 ) -> ScribeOptimisiticInstance { // deploy scribe instance let initial_authed = signer.default_signer().address(); @@ -767,10 +776,14 @@ mod integration_tests { initial_authed.clone(), FixedBytes(wat).clone() ); + let scribe_optimistic = scribe_optimistic.await.unwrap(); let receipt = scribe_optimistic.setBar(1); let receipt = receipt.send().await.expect("Failed to set bar"); - receipt.with_timeout(Some(Duration::from_secs(15))).watch().await.expect("Failed to set bar"); + receipt + .with_timeout(Some(Duration::from_secs(15))) + .watch().await + .expect("Failed to set bar"); // TODO generate the public key and v r s from private key // lift validator @@ -791,12 +804,18 @@ mod integration_tests { }; let receipt = scribe_optimistic.lift_0(pub_key, ecdsa_data); let receipt = receipt.send().await.expect("Failed to lift validator"); - receipt.with_timeout(Some(Duration::from_secs(15))).watch().await.expect("Failed to lift validator"); + receipt + .with_timeout(Some(Duration::from_secs(15))) + .watch().await + .expect("Failed to lift validator"); // set challenge period - let receipt = scribe_optimistic.setOpChallengePeriod(300); + let receipt = scribe_optimistic.setOpChallengePeriod(challenge_period as u16); let receipt = receipt.send().await.expect("Failed to lift validator"); - receipt.with_timeout(Some(Duration::from_secs(15))).watch().await.expect("Failed to set opChallenge"); + receipt + .with_timeout(Some(Duration::from_secs(15))) + .watch().await + .expect("Failed to set opChallenge"); return scribe_optimistic; } From 1667f4d99848d9e57078c95ca4573b68cb5abfd1 Mon Sep 17 00:00:00 2001 From: Konstantin Zolotarev Date: Mon, 28 Oct 2024 22:46:21 +0200 Subject: [PATCH 24/40] Updated logic + some rename --- challenger_old/Cargo.toml | 27 - challenger_old/src/contract.rs | 212 ------- challenger_old/src/lib.rs | 879 --------------------------- crates/scribe/src/contract.rs | 47 +- crates/scribe/src/event_handler.rs | 251 ++++---- crates/scribe/src/events_listener.rs | 27 +- src/main.rs | 313 ++++++---- src/wallet.rs | 1 + 8 files changed, 368 insertions(+), 1389 deletions(-) delete mode 100644 challenger_old/Cargo.toml delete mode 100644 challenger_old/src/contract.rs delete mode 100644 challenger_old/src/lib.rs diff --git a/challenger_old/Cargo.toml b/challenger_old/Cargo.toml deleted file mode 100644 index 6107888..0000000 --- a/challenger_old/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "challenger-lib" -version = "0.1.0" -edition = "2021" - -[lib] -doctest = false - -[dependencies] -ethers = { workspace = true } -tokio = { workspace = true, features = ["full"] } -hex-literal = { workspace = true } -env_logger = { workspace = true } -futures = { workspace = true } -eyre = { workspace = true } -log = { workspace = true, features = ["kv"] } -hex = { workspace = true } -chrono = { workspace = true } -lazy_static = { workspace = true } -tokio-util = { workspace = true } -async-trait = { workspace = true } -metrics = { workspace = true } - -prometheus = { version = "0.13.3", features = ["process"] } - -[dev-dependencies] -mockall = "0.12.1" diff --git a/challenger_old/src/contract.rs b/challenger_old/src/contract.rs deleted file mode 100644 index df82d5a..0000000 --- a/challenger_old/src/contract.rs +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright (C) 2021-2023 Chronicle Labs, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -use async_trait::async_trait; -use std::{fmt::Debug, sync::Arc}; - -use ethers::{ - contract::{abigen, Contract, LogMeta}, - providers::Middleware, - types::{Address, Block, TransactionReceipt, ValueOrArray, H256, U64}, -}; -use eyre::{Result, WrapErr}; -use log::{debug, warn}; - -// Yes it generates smart contract code based on given ABI -abigen!(ScribeOptimistic, "./abi/ScribeOptimistic.json"); - -#[async_trait] -pub trait ScribeOptimisticProvider: Send + Sync { - /// Returns the latest block number from RPC. - async fn get_latest_block_number(&self) -> Result; - - /// Returns block details with given `block_number` from RPC. - async fn get_block(&self, block_number: U64) -> Result>>; - - /// Returns challenge period from ScribeOptimistic smart contract deployed to `address`. - async fn get_challenge_period(&self) -> Result; - - /// Returns list of `OpPokeChallengedSuccessfully` events and log metadata in between `from_block` and `to_block` - /// from smart contract deployed to `address`. - async fn get_successful_challenges( - &self, - from_block: U64, - to_block: U64, - ) -> Result>; - - /// Returns list of `OpPoked` events and log metadata in between `from_block` and `to_block` from smart contract - /// deployed to `address`. - async fn get_op_pokes( - &self, - from_block: U64, - to_block: U64, - ) -> Result>; - - /// Returns true if given `OpPoked` schnorr signature is valid. - async fn is_schnorr_signature_valid(&self, op_poked: OpPokedFilter) -> Result; - - /// Challenges given `OpPoked` event. - async fn challenge(&self, schnorr_data: SchnorrData) -> Result>; - - /// Returns the from account address. - fn get_from(&self) -> Option
{ - warn!( - "get_from() uses default sender address, it's not recommended to use it in production" - ); - Some(Address::zero()) - } -} - -#[derive(Debug)] -pub struct HttpScribeOptimisticProvider { - address: Address, - client: Arc, - contract: ScribeOptimistic, -} - -impl HttpScribeOptimisticProvider { - /// Creates new instance of `HttpScribeOptimisticProvider` under given `client` and `address`. - pub fn new(address: Address, client: Arc) -> Self { - let contract = ScribeOptimistic::new(address, client.clone()); - Self { - address, - client, - contract, - } - } -} - -#[async_trait] -impl ScribeOptimisticProvider for HttpScribeOptimisticProvider -where - M: 'static, - M::Error: 'static, -{ - fn get_from(&self) -> Option
{ - self.client.default_sender() - } - - /// Returns the latest block number from RPC. - async fn get_latest_block_number(&self) -> Result { - self.client - .get_block_number() - .await - .wrap_err("Failed to get latest block number") - } - - /// Returns block details with given `block_number` from RPC. - async fn get_block(&self, block_number: U64) -> Result>> { - self.client - .get_block(block_number) - .await - .wrap_err("Failed to get block details") - } - - /// Returns challenge period from ScribeOptimistic smart contract deployed to `address`. - async fn get_challenge_period(&self) -> Result { - debug!("[{:?}] Getting challenge period", self.address); - - self.contract - .op_challenge_period() - .call() - .await - .wrap_err("Failed to get challenge period") - } - - /// Returns list of `OpPokeChallengedSuccessfully` events and log metadata in between `from_block` and `to_block` - /// from smart contract deployed to `address`. - async fn get_successful_challenges( - &self, - from_block: U64, - to_block: U64, - ) -> Result> { - debug!( - "[{:?}] Searching OpPokeChallengedSuccessfully events from block {:?} to block {:?}", - self.address, from_block, to_block - ); - let event = - Contract::event_of_type::(self.client.clone()) - .address(ValueOrArray::Array(vec![self.address])) - .from_block(from_block) - .to_block(to_block); - - event.query_with_meta().await.wrap_err(format!( - "Failed to get OpPokeChallengedSuccessfully events from block {:?} to block {:?}", - from_block, to_block - )) - } - - /// Returns list of `OpPoked` events and log metadata in between `from_block` and `to_block` from smart contract - /// deployed to `address`. - async fn get_op_pokes( - &self, - from_block: U64, - to_block: U64, - ) -> Result> { - debug!( - "[{:?}] Searching OpPoked events from block {:?} to block {:?}", - self.address, from_block, to_block - ); - let event = Contract::event_of_type::(self.client.clone()) - .address(ValueOrArray::Array(vec![self.address])) - .from_block(from_block) - .to_block(to_block); - - event.query_with_meta().await.wrap_err(format!( - "Failed to get OpPoked events from block {:?} to block {:?}", - from_block, to_block - )) - } - - /// Returns true if given `OpPoked` schnorr signature is valid. - /// Validation process is based on given `OpPoked` event. - /// Validation logic described in here: https://github.com/chronicleprotocol/scribe/blob/main/docs/Scribe.md#verifying-optimistic-pokes - async fn is_schnorr_signature_valid(&self, op_poked: OpPokedFilter) -> Result { - debug!( - "[{:?}] Validating schnorr signature for {:?}", - self.address, op_poked - ); - - let message = self - .contract - .construct_poke_message(op_poked.poke_data) - .call() - .await?; - - self.contract - .is_acceptable_schnorr_signature_now(message, op_poked.schnorr_data) - .call() - .await - .wrap_err("Failed to validate schnorr signature for OpPoked event") - } - - /// Challenges given `OpPoked` event. - /// Executes `opChallenge` function from ScribeOptimistic smart contract with shnorr signature - /// taken from `OpPoked` event. - /// NOTE: You have to validate if schnorr signature is INVALID before calling this function ! - async fn challenge(&self, schnorr_data: SchnorrData) -> Result> { - debug!( - "[{:?}] Challenging schnorr data {:?}", - self.address, schnorr_data - ); - - self.contract - .op_challenge(schnorr_data) - .send() - .await? - .await - .wrap_err("Failed to challenge OpPoked event") - } -} diff --git a/challenger_old/src/lib.rs b/challenger_old/src/lib.rs deleted file mode 100644 index 59a9788..0000000 --- a/challenger_old/src/lib.rs +++ /dev/null @@ -1,879 +0,0 @@ -// Copyright (C) 2021-2023 Chronicle Labs, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -use chrono::{DateTime, Utc}; -use ethers::{ - contract::LogMeta, - core::types::{Address, U64}, -}; -use eyre::{bail, Result}; -use log::{debug, error, info, warn}; -use std::time::Duration; -use tokio::time; - -pub mod contract; -pub mod metrics; - -use contract::{OpPokeChallengedSuccessfullyFilter, OpPokedFilter, ScribeOptimisticProvider}; - -// Note: this is true virtually all of the time but because of leap seconds not always. -// We take minimal time just to be sure, it's always better to check outdated blocks -// rather than miss some. -const SLOT_PERIOD_SECONDS: u16 = 12; - -// Time interval in seconds to reload challenge period from contract. -const DEFAULT_CHALLENGE_PERIOD_RELOAD_INTERVAL: Duration = Duration::from_secs(600); - -// Time interval for checking new pokes in milliseconds. -const DEFAULT_CHECK_INTERVAL_IN_MS: u64 = 30_000; - -// Max number of failures before we stop processing address. -const MAX_FAILURE_COUNT: u8 = 3; - -#[derive(Debug)] -pub struct Challenger { - address: Address, - contract_provider: P, - last_processed_block: Option, - challenge_period_in_sec: u16, - challenge_period_last_updated_at: Option>, - max_failure_count: u8, - failure_count: u8, - tick_interval: Duration, -} - -impl

Challenger

-where - P: ScribeOptimisticProvider + 'static, -{ - pub fn new( - address: Address, - contract_provider: P, - tick_interval: Option, - max_failure_count: Option, - ) -> Self { - Self { - address, - contract_provider, - last_processed_block: None, - challenge_period_in_sec: 0, - challenge_period_last_updated_at: None, - failure_count: 0, - max_failure_count: max_failure_count.unwrap_or(MAX_FAILURE_COUNT), - tick_interval: Duration::from_millis( - tick_interval.unwrap_or(DEFAULT_CHECK_INTERVAL_IN_MS), - ), - } - } - - // Sets last processed block number in challenger - fn set_last_processed_block(&mut self, block: U64) { - self.last_processed_block = Some(block); - - // Updating last scanned block metric - metrics::set_last_scanned_block( - self.address, - self.contract_provider.get_from().unwrap_or_default(), - block.as_u64() as i64, - ); - } - - // Reloads challenge period from contract. - // This function have to be called every N time, because challenge period can be changed by contract owner. - async fn reload_challenge_period(&mut self) -> Result<()> { - let challenge_period_in_sec = self.contract_provider.get_challenge_period().await?; - - debug!( - "[{:?}] Reloaded opChallenge period for contract is {:?}", - self.address, challenge_period_in_sec - ); - self.challenge_period_in_sec = challenge_period_in_sec; - self.challenge_period_last_updated_at = Some(Utc::now()); - - Ok(()) - } - - // Reloads the challenge period from the contract if it has not been updated within the default challenge period reload interval. - async fn reload_challenge_period_if_needed(&mut self) -> Result<()> { - let need_update = match self.challenge_period_last_updated_at { - None => true, - Some(utc) => { - let diff = Utc::now() - utc; - diff.to_std().unwrap() > DEFAULT_CHALLENGE_PERIOD_RELOAD_INTERVAL - } - }; - - if need_update { - self.reload_challenge_period().await.unwrap(); - } - - Ok(()) - } - - // Gets earliest block number we can search for non challenged `opPokes` - async fn get_starting_block_number( - &self, - last_block_number: U64, - challenge_period_in_sec: u16, - ) -> Result { - debug!( - "[{:?}] Calculating starting block number, latest from chain {:?}, period {:?}", - self.address, last_block_number, challenge_period_in_sec - ); - - let blocks_per_period = challenge_period_in_sec / SLOT_PERIOD_SECONDS; - - Ok(last_block_number - blocks_per_period) - } - - // Check if given block_number for log is already non challengeable - async fn is_challengeable( - &self, - block_number: U64, - challenge_period_in_sec: u16, - ) -> Result { - // Checking if log is possible to challenge ? - let block = self - .contract_provider - .get_block(block_number) - .await? - .unwrap(); - - let diff = Utc::now().timestamp() as u64 - block.timestamp.as_u64(); - - Ok(challenge_period_in_sec > diff as u16) - } - - // This function is called every tick. - // Deciedes blocks range we have to process, processing them and if no error happened sets new `last_processed_block` for next tick. - // If error happened on next tick it will again try to process same blocks. - async fn process(&mut self) -> Result<()> { - // Reloads challenge period value - self.reload_challenge_period_if_needed().await.unwrap(); - - // Getting last block from chain - let latest_block_number = self - .contract_provider - .get_latest_block_number() - .await - .unwrap(); - - // Fetching block we have to start with - let from_block = self.last_processed_block.unwrap_or( - self.get_starting_block_number(latest_block_number, self.challenge_period_in_sec) - .await?, - ); - - // In some cases (block drop) our latest processed block can be bigger than latest block from chain, - // in this case we have to skip processing and reset last processed block, so on next tick we will retry. - // Also we returning error to increase failure count. - if from_block > latest_block_number { - // Resetting last processed block with latest chain block - self.set_last_processed_block(latest_block_number); - - bail!( - "Invalid block range {:?} - {:?}, from block is bigger than to block, resetting last processed block to latest block from chain.", - from_block, latest_block_number - ); - } - - // Processing blocks range - self.process_blocks_range(from_block, latest_block_number) - .await?; - - // Updating last processed block with latest chain block - self.set_last_processed_block(latest_block_number); - - Ok(()) - } - - // Validates all `OpPoked` events and challenges them if needed. - async fn process_blocks_range(&mut self, from_block: U64, to_block: U64) -> Result<()> { - debug!( - "[{:?}] Processing blocks range {:?} - {:?}", - self.address, from_block, to_block - ); - - // Fetch list of `OpPokeChallengedSuccessfully` events - let challenges = self - .contract_provider - .get_successful_challenges(from_block, to_block) - .await?; - - // Fetches `OpPoked` events - let op_pokes = self - .contract_provider - .get_op_pokes(from_block, to_block) - .await?; - - // ignoring already challenged pokes - let unchallenged_pokes = reject_challenged_pokes(op_pokes, challenges); - - // Check if we have unchallenged pokes - if unchallenged_pokes.is_empty() { - debug!( - "[{:?}] No unchallenged opPokes found in block range {:?} - {:?}, skipping...", - self.address, from_block, to_block - ); - return Ok(()); - } - - for (poke, meta) in unchallenged_pokes { - let challengeable = self - .is_challengeable(meta.block_number, self.challenge_period_in_sec) - .await?; - - if !challengeable { - error!( - "[{:?}] Block is to old for `opChallenge` block number: {:?}", - self.address, meta.block_number - ); - continue; - } - - let valid = self - .contract_provider - .is_schnorr_signature_valid(poke.clone()) - .await?; - - // If schnorr data is valid, we should not challenge it... - if valid { - debug!( - "[{:?}] Schnorr data for block {:?} is valid, nothing to do...", - self.address, meta.block_number - ); - - continue; - } - - info!( - "[{:?}] Schnorr data for block {:?} is not valid, trying to challenge...", - self.address, meta.block_number - ); - - // TODO: handle error gracefully, we should go further even if error happened - match self.contract_provider.challenge(poke.schnorr_data).await { - Ok(receipt) => { - if let Some(receipt) = receipt { - info!( - "[{:?}] Successfully sent `opChallenge` transaction for OpPoke on block {:?}: {:?}", - self.address, meta.block_number, receipt - ); - // Add challenge to metrics - metrics::inc_challenge_counter( - self.address, - self.contract_provider.get_from().unwrap_or_default(), - receipt.transaction_hash, - ); - } else { - warn!( - "[{:?}] Successfully sent `opChallenge` for block {:?} transaction but no receipt returned", - self.address, meta.block_number - ); - } - } - Err(err) => { - error!( - "[{:?}] Failed to make `opChallenge` call for block {:?}: {:?}", - self.address, meta.block_number, err - ); - } - }; - } - - Ok(()) - } - - /// Starts processing pokes for the given contract address using the specified provider and tick interval. - /// - /// The function uses a tokio::time::interval to run the process method at regular intervals specified by the tick_interval field. - /// - /// # Used arguments - /// - /// * `contract_address` - The address of the contract to process pokes for. - /// * `provider` - The provider to use for interacting with the Ethereum network. - /// * `tick_interval` - The interval at which to check for new pokes. - /// - /// # Examples - /// - /// ``` - /// use eyre::Result; - /// use ethers::providers::{Http, Provider}; - /// use challenger::{Challenger, HttpScribeOptimisticProvider}; - /// use std::time::Duration; - /// - /// #[tokio::main] - /// async fn main() -> Result<()> { - /// let rpc_provider = Provider::::connect("https://mainnet.infura.io/v3/your-project-id").await?; - /// let contract_address = "0x1234567890123456789012345678901234567890".parse()?; - /// let provider = HttpScribeOptimisticProvider::new(contract_address, rpc_provider); - /// let mut challenger = Challenger::new(contract_address, provider, Duration::from_secs(30), None); - /// - /// challenger.start().await? - /// } - /// ``` - pub async fn start(&mut self) -> Result<()> { - let mut interval = time::interval(self.tick_interval); - - loop { - match self.process().await { - Ok(_) => { - // Reset error counter - self.failure_count = 0; - } - Err(err) => { - error!("[{:?}] Failed to process opPokes: {:?}", self.address, err); - - // Increment error counter - metrics::inc_errors_counter( - self.address, - self.contract_provider.get_from().unwrap_or_default(), - &err.to_string(), - ); - - // Increment and check error counter - self.failure_count += 1; - if self.failure_count >= self.max_failure_count { - error!( - "[{:?}] Reached max failure count, stopping processing...", - self.address - ); - return Err(err); - } - } - } - - interval.tick().await; - } - } -} - -// Removes challenged pokes from list of loaded pokes. -// Logic is very simple, if `OpPokeChallengedSuccessfully` event is after `OpPoked` event, then we can safely -// say that `OpPoked` event is already challenged. So we need to validate sequence of events and remove all -// `OpPoked` events that has `OpPokeChallengedSuccessfully` event after it. -fn reject_challenged_pokes( - pokes: Vec<(OpPokedFilter, LogMeta)>, - challenges: Vec<(OpPokeChallengedSuccessfullyFilter, LogMeta)>, -) -> Vec<(OpPokedFilter, LogMeta)> { - if challenges.is_empty() || pokes.is_empty() { - return pokes; - } - let mut result: Vec<(OpPokedFilter, LogMeta)> = vec![]; - - if pokes.len() == 1 { - let (_, meta) = &pokes[0]; - for (_, c_meta) in challenges.clone() { - if c_meta.block_number > meta.block_number { - // empty result - return result; - } - } - return pokes; - } - - 'pokes_loop: for i in 0..pokes.len() { - let (poke, meta) = &pokes.get(i).unwrap(); - // If we do have next poke in list - if let Some((_, next_meta)) = &pokes.get(i + 1) { - for (_, c_meta) in challenges.clone() { - if meta.block_number < c_meta.block_number - && next_meta.block_number > c_meta.block_number - { - // poke already challenged - continue 'pokes_loop; - } - } - } else { - for (_, c_meta) in challenges.clone() { - if c_meta.block_number > meta.block_number { - // poke already challenged - continue 'pokes_loop; - } - } - } - result.push((poke.clone(), meta.clone())); - } - - result -} - -#[cfg(test)] -mod tests { - use super::*; - - use async_trait::async_trait; - use contract::SchnorrData; - use ethers::{ - contract::LogMeta, - types::{Block, TransactionReceipt, H160, H256, U256, U64}, - }; - use eyre::Result; - use mockall::{mock, predicate::*}; - - mock! { - pub TestScribe{} - - #[async_trait] - impl ScribeOptimisticProvider for TestScribe { - async fn get_latest_block_number(&self) -> Result; - - async fn get_block(&self, block_number: U64) -> Result>>; - - async fn get_challenge_period(&self) -> Result; - - async fn get_successful_challenges( - &self, - from_block: U64, - to_block: U64, - ) -> Result>; - - async fn get_op_pokes( - &self, - from_block: U64, - to_block: U64, - ) -> Result>; - - async fn is_schnorr_signature_valid(&self, op_poked: OpPokedFilter) -> Result; - - async fn challenge(&self, schnorr_data: SchnorrData) -> Result>; - } - } - - // Builds new LogMeta with default values, only `block_number` is useful for us. - fn new_log_meta(block_number: U64) -> LogMeta { - LogMeta { - block_number, - address: H160::from_low_u64_be(0), - block_hash: H256::from_low_u64_be(0), - transaction_hash: H256::from_low_u64_be(0), - transaction_index: U64::from(0), - log_index: U256::from(0), - } - } - - #[test] - fn test_reject_challenged_pokes() { - { - // Does nothing if no pokes or challenges - let pokes: Vec<(OpPokedFilter, LogMeta)> = vec![]; - let challenges: Vec<(OpPokeChallengedSuccessfullyFilter, LogMeta)> = vec![]; - - let result = reject_challenged_pokes(pokes.clone(), challenges); - - assert_eq!(result, pokes); - } - - { - // Only 1 poke - returns it back - let pokes: Vec<(OpPokedFilter, LogMeta)> = vec![( - OpPokedFilter { - ..Default::default() - }, - new_log_meta(U64::from(1)), - )]; - let challenges: Vec<(OpPokeChallengedSuccessfullyFilter, LogMeta)> = vec![]; - - let result = reject_challenged_pokes(pokes.clone(), challenges); - assert_eq!(result, pokes); - } - - { - // One poke one challenge after it - let pokes: Vec<(OpPokedFilter, LogMeta)> = vec![( - OpPokedFilter { - ..Default::default() - }, - new_log_meta(U64::from(1)), - )]; - let challenges: Vec<(OpPokeChallengedSuccessfullyFilter, LogMeta)> = vec![( - OpPokeChallengedSuccessfullyFilter { - ..Default::default() - }, - new_log_meta(U64::from(2)), - )]; - - let result = reject_challenged_pokes(pokes, challenges); - assert!(result.is_empty()); - } - - { - // One poke one challenge before it - let pokes: Vec<(OpPokedFilter, LogMeta)> = vec![( - OpPokedFilter { - ..Default::default() - }, - new_log_meta(U64::from(2)), - )]; - let challenges: Vec<(OpPokeChallengedSuccessfullyFilter, LogMeta)> = vec![( - OpPokeChallengedSuccessfullyFilter { - ..Default::default() - }, - new_log_meta(U64::from(1)), - )]; - - let result = reject_challenged_pokes(pokes.clone(), challenges); - assert_eq!(result, pokes); - } - - { - // Multi pokes - one challenge after first poke - let pokes: Vec<(OpPokedFilter, LogMeta)> = vec![ - ( - OpPokedFilter { - ..Default::default() - }, - new_log_meta(U64::from(1)), - ), - ( - OpPokedFilter { - ..Default::default() - }, - new_log_meta(U64::from(3)), - ), - ]; - let challenges: Vec<(OpPokeChallengedSuccessfullyFilter, LogMeta)> = vec![( - OpPokeChallengedSuccessfullyFilter { - ..Default::default() - }, - new_log_meta(U64::from(2)), - )]; - - let result = reject_challenged_pokes(pokes.clone(), challenges); - assert_eq!(result.len(), 1); - - let (_, meta) = result.first().unwrap(); - assert_eq!(meta.block_number, U64::from(3)); - } - - { - // Multi pokes & multi challenges in random order - let pokes: Vec<(OpPokedFilter, LogMeta)> = vec![ - ( - OpPokedFilter { - ..Default::default() - }, - new_log_meta(U64::from(1)), - ), - ( - OpPokedFilter { - ..Default::default() - }, - new_log_meta(U64::from(3)), - ), - ( - OpPokedFilter { - ..Default::default() - }, - new_log_meta(U64::from(4)), - ), - ( - OpPokedFilter { - ..Default::default() - }, - new_log_meta(U64::from(7)), - ), - ]; - let challenges: Vec<(OpPokeChallengedSuccessfullyFilter, LogMeta)> = vec![ - ( - OpPokeChallengedSuccessfullyFilter { - ..Default::default() - }, - new_log_meta(U64::from(2)), - ), - ( - OpPokeChallengedSuccessfullyFilter { - ..Default::default() - }, - new_log_meta(U64::from(5)), - ), - ( - OpPokeChallengedSuccessfullyFilter { - ..Default::default() - }, - new_log_meta(U64::from(6)), - ), - ]; - - let result = reject_challenged_pokes(pokes.clone(), challenges); - assert_eq!(result.len(), 2); - - let (_, meta) = result.first().unwrap(); - assert_eq!(meta.block_number, U64::from(3)); - - let (_, meta2) = result.get(1).unwrap(); - assert_eq!(meta2.block_number, U64::from(7)); - } - } - - #[tokio::test] - async fn test_challenger_returns_error_on_max_failures() { - // Just random address - let address = "0x3D4c07Bd3cf5FB80ACB6Ec31531DBB338329b5F5" - .parse::

() - .unwrap(); - let max_failures: u8 = 3; - - // Setting up mock - let mut mock = MockTestScribe::new(); - mock.expect_get_latest_block_number() - .returning(|| Ok(U64::from(100))); - - mock.expect_get_block().returning(|_| Ok(None)); - - mock.expect_get_challenge_period().returning(|| Ok(600)); - - // Let's fail on this function. - mock.expect_get_successful_challenges() - .times(usize::from(max_failures)) - .returning(|_, _| eyre::bail!("Error challenges")); - - mock.expect_get_op_pokes() - .returning(|_, _| eyre::bail!("Error op_pokes")); - - // Setting up challenger - let mut challenger = Challenger::new(address, mock, Some(10), Some(max_failures)); - - let res = challenger.start().await; - assert!(res.is_err()); - } - - #[tokio::test] - #[should_panic] - async fn test_challenge_period_requires_to_be_fetched() { - // Just random address - let address = "0x3D4c07Bd3cf5FB80ACB6Ec31531DBB338329b5F5" - .parse::
() - .unwrap(); - - // Setting up mock - let mut mock = MockTestScribe::new(); - mock.expect_get_latest_block_number() - .returning(|| Ok(U64::from(100))); - - mock.expect_get_block().returning(|_| Ok(None)); - - mock.expect_get_challenge_period() - .returning(|| eyre::bail!("Error get challenge period")); - - // Let's fail on this function. - mock.expect_get_successful_challenges() - .returning(|_, _| Ok(vec![])); - - mock.expect_get_op_pokes().returning(|_, _| Ok(vec![])); - - // Setting up challenger - let mut challenger = Challenger::new(address, mock, Some(10), None); - - challenger.process().await.unwrap(); - } - - #[tokio::test] - async fn test_last_processed_block_bigger_than_last_chain_block() { - // Just random address - let address = "0x3D4c07Bd3cf5FB80ACB6Ec31531DBB338329b5F5" - .parse::
() - .unwrap(); - let max_failures: u8 = 3; - - // Setting up mock - let mut mock = MockTestScribe::new(); - mock.expect_get_latest_block_number() - .returning(|| Ok(U64::from(1000))); - - mock.expect_get_challenge_period().returning(|| Ok(600)); - mock.expect_get_successful_challenges().never(); - mock.expect_get_op_pokes().never(); - mock.expect_challenge().never(); - mock.expect_get_block().never(); - - // Setting up challenger - let mut challenger = Challenger::new(address, mock, Some(10), Some(max_failures)); - challenger.set_last_processed_block(U64::from(1001)); - - let res = challenger.process().await; - // Should be error in result. - assert!(res.is_err()); - // Check we reseted the last processed block - assert!(challenger.last_processed_block == Some(U64::from(1000))); - } - - #[tokio::test] - async fn test_no_pokes_no_execution() { - // Just random address - let address = "0x3D4c07Bd3cf5FB80ACB6Ec31531DBB338329b5F5" - .parse::
() - .unwrap(); - let max_failures: u8 = 3; - - // Setting up mock - let mut mock = MockTestScribe::new(); - mock.expect_get_latest_block_number() - .returning(|| Ok(U64::from(1000))); - - mock.expect_get_challenge_period().returning(|| Ok(600)); - // Let's fail on this function. - mock.expect_get_successful_challenges() - .returning(|_, _| Ok(vec![])); - mock.expect_get_op_pokes().returning(|_, _| Ok(vec![])); - mock.expect_challenge().never(); - - mock.expect_get_block().never(); - - // Setting up challenger - let mut challenger = Challenger::new(address, mock, Some(10), Some(max_failures)); - - let res = challenger.process().await; - assert!(res.is_ok()); - } - - #[tokio::test] - async fn test_calls_challenge_on_invalid_signature() { - // Just random address - let address = "0x3D4c07Bd3cf5FB80ACB6Ec31531DBB338329b5F5" - .parse::
() - .unwrap(); - let max_failures: u8 = 3; - - // Setting up mock - let mut mock = MockTestScribe::new(); - mock.expect_get_latest_block_number() - .returning(|| Ok(U64::from(1000))); - - mock.expect_get_challenge_period().returning(|| Ok(600)); - // Let's fail on this function. - mock.expect_get_successful_challenges() - .returning(|_, _| Ok(vec![])); - - mock.expect_get_op_pokes().returning(|_, _| { - Ok(vec![( - OpPokedFilter { - ..Default::default() - }, - new_log_meta(U64::from(999)), - )]) - }); - mock.expect_is_schnorr_signature_valid() - .once() - .returning(|_| Ok(false)); - // Called only once ! - mock.expect_challenge().once().returning(|_| Ok(None)); - - mock.expect_get_block().returning(|_| { - Ok(Some(Block { - timestamp: U256::from(Utc::now().timestamp()), - ..Default::default() - })) - }); - - // Setting up challenger - let mut challenger = Challenger::new(address, mock, Some(10), Some(max_failures)); - - let res = challenger.process().await; - assert!(res.is_ok()); - } - - #[tokio::test] - async fn test_ignores_challenge_on_valid_signature() { - // Just random address - let address = "0x3D4c07Bd3cf5FB80ACB6Ec31531DBB338329b5F5" - .parse::
() - .unwrap(); - let max_failures: u8 = 3; - - // Setting up mock - let mut mock = MockTestScribe::new(); - mock.expect_get_latest_block_number() - .returning(|| Ok(U64::from(1000))); - - mock.expect_get_challenge_period().returning(|| Ok(600)); - // Let's fail on this function. - mock.expect_get_successful_challenges() - .returning(|_, _| Ok(vec![])); - - mock.expect_get_op_pokes().returning(|_, _| { - Ok(vec![( - OpPokedFilter { - ..Default::default() - }, - new_log_meta(U64::from(999)), - )]) - }); - mock.expect_is_schnorr_signature_valid() - .once() - .returning(|_| Ok(true)); - // Never called - mock.expect_challenge().never(); - - mock.expect_get_block().returning(|_| { - Ok(Some(Block { - timestamp: U256::from(Utc::now().timestamp()), - ..Default::default() - })) - }); - - // Setting up challenger - let mut challenger = Challenger::new(address, mock, Some(10), Some(max_failures)); - - let res = challenger.process().await; - assert!(res.is_ok()); - } - - #[tokio::test] - async fn test_blocks_older_than_challenge_period_are_ignored() { - // Just random address - let address = "0x3D4c07Bd3cf5FB80ACB6Ec31531DBB338329b5F5" - .parse::
() - .unwrap(); - let max_failures: u8 = 3; - - // Setting up mock - let mut mock = MockTestScribe::new(); - mock.expect_get_latest_block_number() - .returning(|| Ok(U64::from(1000))); - - mock.expect_get_challenge_period().returning(|| Ok(600)); - // Let's fail on this function. - mock.expect_get_successful_challenges() - .returning(|_, _| Ok(vec![])); - - mock.expect_get_op_pokes().returning(|_, _| { - Ok(vec![( - OpPokedFilter { - ..Default::default() - }, - new_log_meta(U64::from(999)), - )]) - }); - // Never called, due to expired timestamp for poke - mock.expect_is_schnorr_signature_valid().never(); - // Never called - mock.expect_challenge().never(); - - // Returning block older than `get_challenge_period` - mock.expect_get_block().once().returning(|_| { - Ok(Some(Block { - timestamp: U256::from(Utc::now().timestamp() - 601), - ..Default::default() - })) - }); - - // Setting up challenger - let mut challenger = Challenger::new(address, mock, Some(10), Some(max_failures)); - - let res = challenger.process().await; - assert!(res.is_ok()); - } -} diff --git a/crates/scribe/src/contract.rs b/crates/scribe/src/contract.rs index 2c89043..4184a0f 100644 --- a/crates/scribe/src/contract.rs +++ b/crates/scribe/src/contract.rs @@ -21,9 +21,7 @@ use alloy::{ sol, sol_types::SolEvent, }; -use once_cell::sync::Lazy; use eyre::{bail, Result, WrapErr}; -use tokio::sync::Mutex; use IScribe::SchnorrData; use ScribeOptimistic::{OpPoked, ScribeOptimisticInstance}; @@ -41,7 +39,7 @@ sol! { // Decode a log into a specific event // Example: // ```rust -// let event = decode_log::(&log)?; +// let event: ScribeOptimistic::OpPoked = decode_log::(&log)?; // ``` fn decode_log(log: &Log) -> Result { let log_data: &LogData = log.as_ref(); @@ -50,19 +48,23 @@ fn decode_log(log: &Log) -> Result { .wrap_err_with(|| "Failed to decode log") } -#[derive(Debug)] +/// Events emitted by the ScribeOptimistic contract. +/// This enum is used to decode logs into specific events. +/// In `challenger` we only care about `OpPoked` and `OpPokeChallengedSuccessfully` events. +#[derive(Debug, Clone)] pub enum Event { OpPoked(ScribeOptimistic::OpPoked), OpPokeChallengedSuccessfully(ScribeOptimistic::OpPokeChallengedSuccessfully), } impl Event { - /// Creates a new GeneralPokedEvent from a Log + /// Creates a new [Event] from a Log pub fn from_log(log: Log) -> Result { let Some(topic) = log.topic0() else { bail!("No topic found in log for tx {:?}", log.transaction_hash) }; + // Match the event by `topic0 match *topic { ScribeOptimistic::OpPoked::SIGNATURE_HASH => { let event = decode_log::(&log)?; @@ -72,12 +74,15 @@ impl Event { let event = decode_log::(&log)?; Ok(Self::OpPokeChallengedSuccessfully(event)) } - _ => bail!("Unknown event {:#x}", topic), + _ => bail!("Unknown event with topic0: {:#x}", topic), } } } -#[derive(Debug)] +/// Event with initial onchain data. +/// This struct is used to store the [Event], [alloy::rpc::types::Log] that emitted it +/// and contract [alloy::primitives::Address] that emitted `log`. +#[derive(Debug, Clone)] pub struct EventWithMetadata { pub event: Event, pub log: Log, @@ -85,7 +90,7 @@ pub struct EventWithMetadata { } impl EventWithMetadata { - /// Creates a new EventWithMetadata from a Log + /// Creates a new [EventWithMetadata] from a Log pub fn from_log(log: Log) -> Result { let event: Event = Event::from_log(log.clone())?; let address = log.address(); @@ -98,15 +103,19 @@ impl EventWithMetadata { } } +/// This trait provides methods required for `challenger` to interact with the ScribeOptimistic smart contract. +/// #[allow(async_fn_in_trait)] pub trait ScribeOptimisticProvider { /// Returns challenge period from ScribeOptimistic smart contract deployed to `address`. + /// NOTE: From time to time challenger might need to refresh this value, because it might be changed by the contract owner. async fn get_challenge_period(&self) -> Result; /// Returns true if given `OpPoked` schnorr signature is valid. async fn is_schnorr_signature_valid(&self, op_poked: OpPoked) -> Result; - /// Challenges given `OpPoked` event. + /// Challenges given `OpPoked` event with given `schnorr_data`. + /// See: `IScribeOptimistic::opChallenge(SchnorrData calldata schnorrData)` for more details. async fn challenge(&self, schnorr_data: SchnorrData) -> Result>; /// Returns the address of the contract. @@ -116,6 +125,7 @@ pub trait ScribeOptimisticProvider { fn get_new_provider(&self) -> Arc; } +/// ScribeOptimisticProviderInstance is a real implementation of ScribeOptimisticProvider based on raw JSON-RPC calls. #[derive(Debug, Clone)] pub struct ScribeOptimisticProviderInstance { pub contract: ScribeOptimisticInstance>, @@ -129,14 +139,16 @@ impl ScribeOptimisticProviderInstance { } } - impl ScribeOptimisticProvider for ScribeOptimisticProviderInstance { async fn get_challenge_period(&self) -> Result { Ok(self.contract.opChallengePeriod().call().await?._0) } async fn is_schnorr_signature_valid(&self, op_poked: OpPoked) -> Result { - log::trace!("{:?} Validating OpPoke signature", self.contract.address()); + log::trace!( + "Contract {:?}: Validating OpPoke signature", + self.contract.address() + ); let message = self .contract @@ -155,21 +167,14 @@ impl ScribeOptimisticProvider for ScribeOptimisticProviderInstance { Ok(acceptable) } - async fn challenge(&self, schnorr_data: SchnorrData) -> Result> { - // This ensures only one challenge is happening at a time, - // (this way multiple signed transactions with same nonce aren't sent) - // static CHALLENGE_GAURD: Lazy> = Lazy::new(|| tokio::sync::Mutex::new(())); - // let _gaurd = CHALLENGE_GAURD.lock().await; - - log::debug!("{:?} Challenging OpPoke", self.contract.address()); - let from_address = self.contract.address(); - log::info!("Challenging from address: {:?}", from_address); + log::info!("Contract {:?}: Challenging OpPoke", self.contract.address(),); let transaction = self .contract .opChallenge(schnorr_data) - // TODO set gas limit properly + // TODO: set gas limit properly .gas(200000); + transaction .send() .await? diff --git a/crates/scribe/src/event_handler.rs b/crates/scribe/src/event_handler.rs index 1bc5cac..48119c5 100644 --- a/crates/scribe/src/event_handler.rs +++ b/crates/scribe/src/event_handler.rs @@ -18,9 +18,9 @@ use std::sync::Arc; use std::time::Duration; use alloy::primitives::Address; -use alloy::providers::{Provider, WalletProvider}; +use alloy::providers::Provider; use alloy::rpc::types::BlockTransactionsKind; -use eyre::Result; +use eyre::{bail, Result}; use tokio::sync::mpsc::{Receiver, Sender}; use tokio::sync::Mutex; use tokio::task::JoinSet; @@ -37,8 +37,8 @@ const CHALLENGE_POKE_DELAY_MS: u64 = 200; const FLASHBOT_CHALLENGE_RETRY_COUNT: u64 = 3; const CLASSIC_CHALLENGE_RETRY_COUNT: u64 = 3; -// Take event logs and distribute them to the appropriate contract handler -// Each scribe address has its own contract handler instance +// Takes event logs and distributes them to the appropriate contract handler +// Each `ScribeOptimistic` instance deployed to address has its own `EventHandler` process. pub struct EventDistributor { addresses: Vec
, cancel: CancellationToken, @@ -71,10 +71,10 @@ impl EventDistributor { // Create a contract handler for each scribe address for address in &self.addresses { let (tx, rx) = tokio::sync::mpsc::channel::(100); - self.txs.insert(address.clone(), tx); + self.txs.insert(*address, tx); - let mut contract_handler = EventHandler::new( - address.clone(), + let mut contract_handler = ScribeEventsProcessor::new( + *address, self.provider.clone(), self.flashbot_provider.clone(), self.cancel.clone(), @@ -91,21 +91,17 @@ impl EventDistributor { break; } event = self.rx.recv() => { - match event { - Some(event) => { - event.address; - // Send the event to the appropriate contract handler - match self.txs.get(&event.address) { - Some(tx) => { - let _ = tx.send(event).await; - } - _ => { - // Should never happen - log::warn!("Received event for unknown address: {:?}", event.address); - } + if let Some(event) = event { + // Send the event to the appropriate contract handler + match self.txs.get(&event.address) { + Some(tx) => { + let _ = tx.send(event).await; + } + _ => { + // Should never happen + log::warn!("Received event for unknown address: {:?}", event.address); } } - _ => {} } } } @@ -116,18 +112,19 @@ impl EventDistributor { } } -// Receive events for a specific scribe address and create or cancel challenge processes -struct EventHandler { - scribe_address: Address, +// Receives preparsed [crate::contract::EventWithMetadata] events for a `ScribeOptimistic` instance on address, +// validates `OpPoked` events and challenges them if they are invalid and within the challenge period. +struct ScribeEventsProcessor { + address: Address, provider: Arc, flashbot_provider: Arc, - cancel: CancellationToken, + cancellation_token: CancellationToken, rx: Receiver, cancel_challenge: Option, challenge_period: Option, } -impl EventHandler { +impl ScribeEventsProcessor { pub fn new( scribe_address: Address, provider: Arc, @@ -136,10 +133,10 @@ impl EventHandler { rx: Receiver, ) -> Self { Self { - scribe_address, + address: scribe_address, provider, flashbot_provider, - cancel, + cancellation_token: cancel, rx, cancel_challenge: None, challenge_period: None, @@ -149,74 +146,109 @@ impl EventHandler { pub async fn start(&mut self) -> Result<()> { // We have to fail if no challenge period is fetched self.fetch_challenge_period().await.unwrap(); + loop { tokio::select! { - _ = self.cancel.cancelled() => { + // main process terminates, need to finish work and exit... + _ = self.cancellation_token.cancelled() => { log::info!( "[{:?}] Cancellation requested, stopping contract handler", - self.scribe_address + self.address ); - break; + return Ok(()); } + // new [EventWithMetadata] received, process it... event = self.rx.recv() => { - log::debug!("[{:?}] Received event: {:?}", self.scribe_address, event); - match event { - Some(event) => { - match event.event { - Event::OpPoked(op_poked) => { - log::debug!("[{:?}] OpPoked received", self.scribe_address); - // Check if the poke is within the challenge period - let event_timestamp = match event.log.block_timestamp { - Some(timestamp) => timestamp, - None => { - let block = self.provider.get_block( - event.log.block_number.unwrap().into(), - BlockTransactionsKind::Hashes - ).await?; - let block = match block { - Some(block) => block, - None => { - log::error!("Block not found"); - continue; - } - }; - let timestamp = block.header.timestamp; - timestamp - } - }; - - // Commented code left incase we need to cahnge to using chain as source of truth of time - // let latest_block_number = self.provider.get_block_number().await?; - // let latest_block = self.provider.get_block(latest_block_number.into(), BlockTransactionsKind::Hashes).await.unwrap(); - // let current_timestamp = latest_block.unwrap().header.timestamp; - let current_timestamp = chrono::Utc::now().timestamp() as u64; - log::debug!("[{:?}] OpPoked, event_timestamp: {:?}, current_timestamp: {:?}", - self.scribe_address, event_timestamp, current_timestamp); - if current_timestamp - event_timestamp > - self.challenge_period.unwrap() { - // This log is expected in tests, tests must be updated if log is changed - log::debug!( - "[{:?}] OpPoked received outside of challenge period", - self.scribe_address - ); - continue; - } - log::debug!("Spawning challenge..."); - self.spawn_challenge(op_poked).await; - } - Event::OpPokeChallengedSuccessfully(_) => { - self.cancel_challenge().await; + log::debug!("[{:?}] Received event: {:?}", self.address, event); + + if let Some(event) = event { + match &event.event { + // For `OpPoked` events, check if `schnorr_signature` is valid, + // if not - check if event is within the challenge period, send challenge. + // If `schnorr_signature` is valid, do nothing. + Event::OpPoked(op_poked) => { + log::debug!("[{:?}] OpPoked received", self.address); + + if let Err(err) = self.process_op_poked(event.clone(), op_poked.clone()).await { + log::error!( + "[{:?}] Error processing OpPoked event: {:?}", + self.address, + err + ); } } - } - _ => { - // if event is None + // If the challenge is already successful, cancel the previous challenge process + Event::OpPokeChallengedSuccessfully(_) => { + self.cancel_challenge().await; + } } } } } } - todo!() + } + + async fn process_op_poked( + &mut self, + event: EventWithMetadata, + op_poked: OpPoked, + ) -> Result<()> { + // Check if the poke is within the challenge period + let event_timestamp = match event.log.block_timestamp { + Some(timestamp) => timestamp, + None => self.get_timestamp_from_block(&event).await?, + }; + + let current_timestamp = chrono::Utc::now().timestamp() as u64; + log::debug!( + "[{:?}] OpPoked, event_timestamp: {:?}, current_timestamp: {:?}", + self.address, + event_timestamp, + current_timestamp + ); + if current_timestamp - event_timestamp > self.challenge_period.unwrap() { + // This log is expected in tests, tests must be updated if log message is changed + log::debug!( + "[{:?}] OpPoked received outside of challenge period", + self.address + ); + return Ok(()); + } + log::debug!("Spawning challenge..."); + self.spawn_challenge(op_poked).await; + + Ok(()) + } + + // Gets the timestamp from the block by `event.log.block_number`, if it is missing, returns an error + async fn get_timestamp_from_block(&self, event: &EventWithMetadata) -> Result { + // Possible block number to be missing for unconfirmed blocks + if event.log.block_number.is_none() { + bail!( + "[{:?}] Block number is missing for log {:?}", + self.address, + event.log + ); + } + let block_number = event.log.block_number.unwrap(); + + let block = self + .provider + .get_block(block_number.into(), BlockTransactionsKind::Hashes) + .await?; + + let block = match block { + Some(block) => block, + None => { + bail!( + "[{:?}] Block with number {:?} not found", + self.address, + block_number + ); + } + }; + + Ok(block.header.timestamp) } async fn fetch_challenge_period(&mut self) -> Result<()> { @@ -224,10 +256,9 @@ impl EventHandler { if self.challenge_period.is_some() { return Ok(()); } - let period = - ScribeOptimisticProviderInstance::new(self.scribe_address, self.provider.clone()) - .get_challenge_period() - .await?; + let period = ScribeOptimisticProviderInstance::new(self.address, self.provider.clone()) + .get_challenge_period() + .await?; self.challenge_period = Some(period as u64); Ok(()) } @@ -238,12 +269,12 @@ impl EventHandler { // Create a new cancellation token self.cancel_challenge = Some(CancellationToken::new()); // Create a new challenger instance - let challenge_handler = Some(Arc::new(Mutex::new(ChallengeHandler::new( + let challenge_handler = Some(Arc::new(Mutex::new(ChallengeSender::new( op_poked, - self.cancel.clone(), + self.cancellation_token.clone(), // cancel_challenge garunteed to be Some self.cancel_challenge.as_ref().unwrap().clone(), - self.scribe_address, + self.address, self.provider.clone(), self.flashbot_provider.clone(), )))); @@ -254,17 +285,14 @@ impl EventHandler { let handler = challenge_handler.as_ref().unwrap().lock().await; let _ = handler.start().await; }); - log::debug!("Spawned New challenger"); + log::debug!("[{:?}] Spawned New challenger process", self.address); } async fn cancel_challenge(&mut self) { - match &self.cancel_challenge { - Some(cancel) => { - log::debug!("Cancelling existing challenge"); - cancel.cancel(); - self.cancel_challenge = None; - } - _ => {} + if let Some(cancel) = &self.cancel_challenge { + log::debug!("[{:?}] Cancelling existing challenge", self.address); + cancel.cancel(); + self.cancel_challenge = None; } } } @@ -272,7 +300,7 @@ impl EventHandler { // Handle the challenge process for a specific OpPoked event after a delay // If cancelled before end of delay or inbewteen retries stop process // First try challenge with flashbot provider, then with normal provider -struct ChallengeHandler { +struct ChallengeSender { pub op_poked: OpPoked, pub global_cancel: CancellationToken, pub cancel: CancellationToken, @@ -282,7 +310,7 @@ struct ChallengeHandler { flashbot_provider: Arc, } -impl ChallengeHandler { +impl ChallengeSender { pub fn new( op_poked: OpPoked, global_cancel: CancellationToken, @@ -303,21 +331,19 @@ impl ChallengeHandler { pub async fn start(&self) -> Result<()> { // This checked for in tests, tests must be updated if log is changed - log::debug!("Challenge started"); + log::debug!("[{:?}] Challenge started", self.address); // Perform the challenge after 200ms - let provider = Mutex::new(self.provider.clone()); - let mut challenge_attempts: u64 = 0; loop { tokio::select! { // Check if the challenge has been cancelled _ = self.cancel.cancelled() => { - log::debug!("Challenge cancelled"); + log::debug!("[{:?}] Challenge cancelled", self.address); break; } // Check if the global cancel command sent _ = self.global_cancel.cancelled() => { - log::debug!("Global cancel command received"); + log::debug!("[{:?}] Global cancel command received", self.address); break; } _ = tokio::time::sleep(Duration::from_millis(CHALLENGE_POKE_DELAY_MS)) => { @@ -326,10 +352,12 @@ impl ChallengeHandler { self.address, self.provider.clone() ).is_schnorr_signature_valid(self.op_poked.clone()).await?; + if is_valid { - log::debug!("OpPoked is valid, no need to challenge"); + log::debug!("[{:?}] OpPoked is valid, no need to challenge", self.address); break; } + const RETRY_RANGE_END: u64 = CLASSIC_CHALLENGE_RETRY_COUNT+FLASHBOT_CHALLENGE_RETRY_COUNT; match challenge_attempts { 0..FLASHBOT_CHALLENGE_RETRY_COUNT => { @@ -342,14 +370,13 @@ impl ChallengeHandler { let result = contract.challenge( self.op_poked.schnorrData.clone() ).await; - log::debug!("Challenge result: {:?}", result); match result { Ok(tx_hash) => { - log::debug!("Flashbot transaction sent: {:?}", tx_hash); + log::debug!("[{:?}] Flashbot transaction sent: {:?}", self.address, tx_hash); break; } Err(e) => { - log::error!("Failed to send flashbot transaction: {:?}", e); + log::error!("[{:?}] Failed to send flashbot transaction: {:?}", self.address, e); } } } @@ -357,20 +384,18 @@ impl ChallengeHandler { let contract = ScribeOptimisticProviderInstance::new( self.address, self.provider.clone() ); - let result = contract.challenge(self.op_poked.schnorrData.clone()).await; - log::debug!("Challenge result: {:?}", result); - match result { + match contract.challenge(self.op_poked.schnorrData.clone()).await { Ok(tx_hash) => { - log::debug!("Challenge transaction sent: {:?}", tx_hash); + log::debug!("[{:?}] Challenge transaction sent: {:?}", self.address, tx_hash); break; } Err(e) => { - log::error!("Failed to send challenge transaction: {:?}", e); + log::error!("[{:?}] Failed to send challenge transaction: {:?}", self.address, e); } } } _ => { - log::debug!("Challenge failed"); + log::error!("[{:?}] Challenge failed, total attempts {:?}", self.address, challenge_attempts); break; } } diff --git a/crates/scribe/src/events_listener.rs b/crates/scribe/src/events_listener.rs index c3dca2a..22cb9da 100644 --- a/crates/scribe/src/events_listener.rs +++ b/crates/scribe/src/events_listener.rs @@ -20,7 +20,8 @@ use alloy::{ primitives::Address, providers::{ fillers::{ - BlobGasFiller, CachedNonceManager, ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, WalletFiller + BlobGasFiller, CachedNonceManager, ChainIdFiller, FillProvider, GasFiller, JoinFill, + NonceFiller, WalletFiller, }, Identity, Provider, RootProvider, WalletProvider, }, @@ -50,7 +51,27 @@ const MAX_ADDRESS_PER_REQUEST: usize = 50; pub type RpcRetryProvider = RetryBackoffService>; /// The provider type used to interact with the Ethereum network with a signer. -pub type RetryProviderWithSigner = FillProvider>>>, ChainIdFiller>, NonceFiller>, WalletFiller>, RootProvider>>, RetryBackoffService>, Ethereum>; +pub type RetryProviderWithSigner = FillProvider< + JoinFill< + JoinFill< + JoinFill< + JoinFill< + Identity, + JoinFill< + GasFiller, + JoinFill>, + >, + >, + ChainIdFiller, + >, + NonceFiller, + >, + WalletFiller, + >, + RootProvider>>, + RetryBackoffService>, + Ethereum, +>; #[derive(Debug, Clone)] pub struct Poller { @@ -108,7 +129,7 @@ impl Poller { log::trace!("Polling for new events"); // Get latest block number let latest_block = self.provider.get_block_number().await.unwrap(); - if None == self.last_processes_block { + if self.last_processes_block.is_none() { self.last_processes_block = Some(latest_block); } // TODO remove this line diff --git a/src/main.rs b/src/main.rs index be977c7..3b576f9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,7 @@ // along with this program. If not, see . use alloy::primitives::Address; -use alloy::providers::fillers::{ CachedNonceManager, ChainIdFiller, NonceFiller }; +use alloy::providers::fillers::{CachedNonceManager, ChainIdFiller, NonceFiller}; use alloy::providers::ProviderBuilder; use alloy::rpc::client::ClientBuilder; use alloy::transports::layers::RetryBackoffLayer; @@ -22,13 +22,13 @@ use clap::Parser; use env_logger::Env; use eyre::Result; -use log::{ error, info }; +use log::{error, info}; use scribe::contract::EventWithMetadata; use scribe::events_listener::Poller; use scribe::metrics; use std::net::SocketAddr; use std::sync::Arc; -use std::{ env, panic, path::PathBuf, time::Duration }; +use std::{env, panic, path::PathBuf, time::Duration}; use tokio::time::sleep; use tokio_util::sync::CancellationToken; @@ -37,17 +37,16 @@ use scribe::event_handler; mod wallet; use tokio::task::JoinSet; -use tokio::{ select, signal }; +use tokio::{select, signal}; -use wallet::{ CustomWallet, KeystoreWallet, PrivateKeyWallet }; +use wallet::{CustomWallet, KeystoreWallet, PrivateKeyWallet}; use metrics_exporter_prometheus::PrometheusBuilder; use metrics_process::Collector; +/// Cli interface for the challenger. #[derive(Parser, Debug)] #[command(author, version, about)] -/// Challenger searches for `opPoked` events for `ScribeOptimistic` contract. -/// Verifies poke schnorr signature and challenges it, if it's invalid. struct Cli { #[arg( short = 'a', @@ -59,7 +58,10 @@ struct Cli { #[arg(long, help = "Node HTTP RPC_URL, normally starts with https://****")] rpc_url: String, - #[arg(long, help = "Flashbot Node HTTP RPC_URL, normally starts with https://****")] + #[arg( + long, + help = "Flashbot Node HTTP RPC_URL, normally starts with https://****" + )] flashbot_rpc_url: String, #[arg( @@ -75,7 +77,11 @@ struct Cli { )] keystore_path: Option, - #[arg(long = "password", requires = "keystore_path", help = "Key raw password as text")] + #[arg( + long = "password", + requires = "keystore_path", + help = "Key raw password as text" + )] raw_password: Option, #[arg( @@ -86,7 +92,10 @@ struct Cli { )] password_file: Option, - #[arg(long, help = "If no chain_id provided binary will try to get chain_id from given RPC")] + #[arg( + long, + help = "If no chain_id provided binary will try to get chain_id from given RPC" + )] chain_id: Option, } @@ -123,7 +132,10 @@ async fn main() -> Result<()> { // Building tx signer for provider let signer = args.wallet()?.unwrap(); - info!("Using {:?} for signing transactions.", signer.default_signer().address()); + info!( + "Using {:?} for signing transactions.", + signer.default_signer().address() + ); let nonce_mananger = NonceFiller::::default(); // Create new HTTP client with retry backoff layer @@ -140,7 +152,7 @@ async fn main() -> Result<()> { .filler(nonce_mananger.clone()) // Add default signer .wallet(signer.clone()) - .on_client(client) + .on_client(client), ); // Create new HTTP client for flashbots @@ -158,7 +170,7 @@ async fn main() -> Result<()> { .filler(nonce_mananger.clone()) // Add default signer .wallet(signer.clone()) - .on_client(flashbot_client) + .on_client(flashbot_client), ); // let signer_lock = Arc::new(Mutex::new(signer)); @@ -177,7 +189,10 @@ async fn main() -> Result<()> { // Register Prometheus metrics let builder = PrometheusBuilder::new(); - let port = env::var("HTTP_PORT").unwrap_or(String::from("9090")).parse::().unwrap(); + let port = env::var("HTTP_PORT") + .unwrap_or(String::from("9090")) + .parse::() + .unwrap(); let addr = SocketAddr::from(([0, 0, 0, 0], port)); let _ = builder.with_http_listener(addr).install(); @@ -201,7 +216,7 @@ async fn main() -> Result<()> { cancel_token.clone(), provider.clone(), tx.clone(), - 30 + 30, ); // Create event distributor @@ -210,7 +225,7 @@ async fn main() -> Result<()> { cancel_token.clone(), provider.clone(), flashbot_provider.clone(), - rx + rx, ); // Run events listener process @@ -287,7 +302,7 @@ mod tests { let cli = Cli { addresses: vec![], raw_secret_key: Some( - "def90b5b5cb2d68c5cd9de7b3e6d767cbb1b8d5fd8560bd6c42cbc4a4da30b16".to_string() + "def90b5b5cb2d68c5cd9de7b3e6d767cbb1b8d5fd8560bd6c42cbc4a4da30b16".to_string(), ), chain_id: None, keystore_path: None, @@ -308,7 +323,7 @@ mod tests { let cli = Cli { addresses: vec![], raw_secret_key: Some( - "0xdef90b5b5cb2d68c5cd9de7b3e6d767cbb1b8d5fd8560bd6c42cbc4a4da30b16".to_string() + "0xdef90b5b5cb2d68c5cd9de7b3e6d767cbb1b8d5fd8560bd6c42cbc4a4da30b16".to_string(), ), chain_id: None, keystore_path: None, @@ -329,13 +344,11 @@ mod tests { #[test] fn keystore_works_with_password_in_file() { let keystore = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/keystore"); - let keystore_file = keystore.join( - "UTC--2022-12-20T10-30-43.591916000Z--ec554aeafe75601aaab43bd4621a22284db566c2" - ); + let keystore_file = keystore + .join("UTC--2022-12-20T10-30-43.591916000Z--ec554aeafe75601aaab43bd4621a22284db566c2"); - let keystore_password_file = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join( - "tests/fixtures/keystore/password" - ); + let keystore_password_file = + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/keystore/password"); let cli = Cli { addresses: vec![], @@ -359,9 +372,8 @@ mod tests { #[test] fn keystore_works_with_raw_password() { let keystore = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/keystore"); - let keystore_file = keystore.join( - "UTC--2022-12-20T10-30-43.591916000Z--ec554aeafe75601aaab43bd4621a22284db566c2" - ); + let keystore_file = keystore + .join("UTC--2022-12-20T10-30-43.591916000Z--ec554aeafe75601aaab43bd4621a22284db566c2"); let cli = Cli { addresses: vec![], @@ -392,27 +404,21 @@ mod integration_tests { use std::vec; use alloy::hex; - use alloy::network::{ Ethereum, EthereumWallet, Network, NetworkWallet }; - use alloy::node_bindings::{ Anvil, AnvilInstance }; + use alloy::network::{Ethereum, EthereumWallet, Network}; + use alloy::node_bindings::{Anvil, AnvilInstance}; use alloy::primitives::U256; - use alloy::primitives::{ Address, FixedBytes }; + use alloy::primitives::{Address, FixedBytes}; use alloy::providers::ext::AnvilApi; use alloy::providers::fillers::{ - BlobGasFiller, - CachedNonceManager, - ChainIdFiller, - FillProvider, - GasFiller, - JoinFill, - NonceFiller, - WalletFiller, + BlobGasFiller, CachedNonceManager, ChainIdFiller, FillProvider, GasFiller, JoinFill, + NonceFiller, WalletFiller, }; - use alloy::providers::{ Identity, Provider, ProviderBuilder, RootProvider }; + use alloy::providers::{Identity, Provider, ProviderBuilder, RootProvider}; use alloy::rpc::client::ClientBuilder; use alloy::signers::local::PrivateKeySigner; use alloy::signers::SignerSync; use alloy::transports::http::reqwest::Url; - use alloy::transports::http::{ Client, Http }; + use alloy::transports::http::{Client, Http}; use alloy::transports::layers::RetryBackoffLayer; use alloy::transports::Transport; use futures_util::future::join_all; @@ -420,10 +426,7 @@ mod integration_tests { use scribe::event_handler; use scribe::events_listener::Poller; use scribe_optimistic::{ - IScribe, - LibSecp256k1, - ScribeOptimisitic, - ScribeOptimisitic::ScribeOptimisiticInstance, + IScribe, LibSecp256k1, ScribeOptimisitic, ScribeOptimisitic::ScribeOptimisiticInstance, }; use tokio::task::JoinSet; use tokio_util::sync::CancellationToken; @@ -450,19 +453,19 @@ mod integration_tests { Identity, JoinFill< GasFiller, - JoinFill> - > + JoinFill>, + >, >, - WalletFiller + WalletFiller, >, - ChainIdFiller + ChainIdFiller, >, - NonceFiller + NonceFiller, >, RootProvider>, Http, - Ethereum - > + Ethereum, + >, >; const PRIVATE_KEY: &str = "d4cf162c2e26ff75095922ea108d516ff07bdd732f050e64ced632980f11320b"; @@ -480,14 +483,19 @@ mod integration_tests { let (anvil, anvil_provider, signer) = create_anvil_instances(private_key, 8545).await; // set to a low current time for now, this avoids having stale poke error later - anvil_provider.anvil_set_time(1000).await.expect("Failed to set time"); + anvil_provider + .anvil_set_time(1000) + .await + .expect("Failed to set time"); let mut deployments = vec![]; let mut scribes = vec![]; for i in 0..NUM_SCRIBE_INSTANCES { - deployments.push( - deploy_scribe(anvil_provider.clone(), signer.clone(), CHALLENGE_PERIOD) - ); + deployments.push(deploy_scribe( + anvil_provider.clone(), + signer.clone(), + CHALLENGE_PERIOD, + )); // Only deploy at most 20 in parrallel if i % 20 == 0 { scribes.append(&mut join_all(deployments).await); @@ -501,12 +509,10 @@ mod integration_tests { for i in 0..NUM_SCRIBE_INSTANCES { let scribe_optimistic = &scribes[i]; scribe_addresses.push(scribe_optimistic.address().clone()); - balance_updates.push( - anvil_provider.anvil_set_balance( - scribes[i].address().clone(), - U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap() - ) - ); + balance_updates.push(anvil_provider.anvil_set_balance( + scribes[i].address().clone(), + U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap(), + )); } for balance_update in join_all(balance_updates).await { balance_update.expect("Unable to set balance"); @@ -517,7 +523,10 @@ mod integration_tests { // TODO add challenge period variable let current_timestamp = (chrono::Utc::now().timestamp() as u64) - (CHALLENGE_PERIOD as u64) + 1; - anvil_provider.anvil_set_time(current_timestamp).await.expect("Failed to set time"); + anvil_provider + .anvil_set_time(current_timestamp) + .await + .expect("Failed to set time"); // ------------------------------------------------------------------------------------------------------------ let cancel_token: CancellationToken = CancellationToken::new(); @@ -528,8 +537,9 @@ mod integration_tests { anvil_provider .anvil_set_balance( signer.default_signer().address().clone(), - U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap() - ).await + U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap(), + ) + .await .expect("Unable to set balance"); let addresses = scribe_addresses.clone(); let cancel_token = cancel_token.clone(); @@ -545,7 +555,8 @@ mod integration_tests { // Increase current block count to move away from poller intialisation block anvil_provider - .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await + .anvil_mine(Some(U256::from(1)), Some(U256::from(1))) + .await .expect("Failed to mine"); // ------------------------------------------------------------------------------------------------------------ @@ -553,10 +564,15 @@ mod integration_tests { for i in 0..NUM_SCRIBE_INSTANCES { // Assert that the current contract balance is not 0 let balance = anvil_provider - .get_balance(scribe_addresses[i]).await + .get_balance(scribe_addresses[i]) + .await .expect("Failed to get balance"); assert_ne!(balance, U256::from(0)); - invalid_pokes.push(make_invalid_op_poke(current_timestamp, private_key, &scribes[i])); + invalid_pokes.push(make_invalid_op_poke( + current_timestamp, + private_key, + &scribes[i], + )); // Do at most 50 at the same time if i % 50 == 0 { join_all(invalid_pokes).await; @@ -567,20 +583,20 @@ mod integration_tests { // Mine at least one block to ensure log is processed anvil_provider - .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await + .anvil_mine(Some(U256::from(1)), Some(U256::from(1))) + .await .expect("Failed to mine"); - let result = join_all( - (0..NUM_SCRIBE_INSTANCES).map(|i| - poll_balance_is_zero( - &anvil_provider, - &scribe_addresses[i], - (20 + (10 * NUM_SCRIBE_INSTANCES) / 100).try_into().unwrap() - ) + let result = join_all((0..NUM_SCRIBE_INSTANCES).map(|i| { + poll_balance_is_zero( + &anvil_provider, + &scribe_addresses[i], + (20 + (10 * NUM_SCRIBE_INSTANCES) / 100).try_into().unwrap(), ) - ).await - .iter() - .all(|&result| result); + })) + .await + .iter() + .all(|&result| result); // Poll to check that the challenge started log is found, // (to ensure log hasn't been changed as its non appearance is looked for in other tests) @@ -589,7 +605,10 @@ mod integration_tests { testing_logger::validate(|captured_logs| { let mut found: bool = false; for log in captured_logs { - found |= log.body.to_lowercase().contains(&"Challenge started".to_lowercase()); + found |= log + .body + .to_lowercase() + .contains(&"Challenge started".to_lowercase()); } success.set(found); }); @@ -620,21 +639,28 @@ mod integration_tests { let (anvil, anvil_provider, signer) = create_anvil_instances(private_key, 8546).await; // Set to a low current time for now, this avoids having stale poke error later - anvil_provider.anvil_set_time(1000).await.expect("Failed to set time"); + anvil_provider + .anvil_set_time(1000) + .await + .expect("Failed to set time"); // Deploy scribe instance let scribe_optimistic = deploy_scribe(anvil_provider.clone(), signer.clone(), 300).await; anvil_provider .anvil_set_balance( scribe_optimistic.address().clone(), - U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap() - ).await + U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap(), + ) + .await .expect("Unable to set balance"); // Update current anvil time to be far from last scribe config update // Set the anvil time to be in the past to ensure the challenge period is exceeded later let current_timestamp = (chrono::Utc::now().timestamp() as u64) - 400; - anvil_provider.anvil_set_time(current_timestamp).await.expect("Failed to set time"); + anvil_provider + .anvil_set_time(current_timestamp) + .await + .expect("Failed to set time"); // ------------------------------------------------------------------------------------------------------------ let cancel_token: CancellationToken = CancellationToken::new(); @@ -645,8 +671,9 @@ mod integration_tests { anvil_provider .anvil_set_balance( signer.default_signer().address().clone(), - U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap() - ).await + U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap(), + ) + .await .expect("Unable to set balance"); let addresses = vec![scribe_optimistic.address().clone()]; let cancel_token = cancel_token.clone(); @@ -661,13 +688,15 @@ mod integration_tests { // Increase current block count to move away from poller intialisation block anvil_provider - .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await + .anvil_mine(Some(U256::from(1)), Some(U256::from(1))) + .await .expect("Failed to mine"); // ------------------------------------------------------------------------------------------------------------ // Assert that the current contract balance is not 0 let balance = anvil_provider - .get_balance(scribe_optimistic.address().clone()).await + .get_balance(scribe_optimistic.address().clone()) + .await .expect("Failed to get balance"); assert_ne!(balance, U256::from(0)); @@ -677,7 +706,8 @@ mod integration_tests { // Mine at least one block to ensure log is processed anvil_provider - .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await + .anvil_mine(Some(U256::from(1)), Some(U256::from(1))) + .await .expect("Failed to mine"); // Poll till expected logs are found @@ -689,10 +719,13 @@ mod integration_tests { let mut found: bool = false; for log in captured_logs { assert!( - !log.body.to_lowercase().contains(&"Challenge started".to_lowercase()), + !log.body + .to_lowercase() + .contains(&"Challenge started".to_lowercase()), "Challenge started log found" ); - found |= log.body + found |= log + .body .to_lowercase() .contains(&"OpPoked received outside of challenge period".to_lowercase()); } @@ -714,7 +747,7 @@ mod integration_tests { async fn create_anvil_instances( private_key: &str, - port: u16 + port: u16, ) -> (AnvilInstance, AnvilProvider, EthereumWallet) { let anvil: AnvilInstance = Anvil::new() .port(port) @@ -723,9 +756,8 @@ mod integration_tests { .try_spawn() .expect("Failed to spawn anvil"); - let signer: EthereumWallet = EthereumWallet::new( - private_key.parse::().unwrap() - ); + let signer: EthereumWallet = + EthereumWallet::new(private_key.parse::().unwrap()); let anvil_provider = Arc::new( ProviderBuilder::new() @@ -734,24 +766,30 @@ mod integration_tests { .wallet(signer.clone()) .filler(ChainIdFiller::new(Some(31337))) .filler(NonceFiller::::default()) - .on_http(anvil.endpoint_url()) + .on_http(anvil.endpoint_url()), ); // set initial balance for deployer anvil_provider .anvil_set_balance( signer.default_signer().address(), - U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap() - ).await + U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap(), + ) + .await .expect("Unable to set balance"); // configure anvil settings - anvil_provider.anvil_set_auto_mine(true).await.expect("Failed to set auto mine"); anvil_provider - .anvil_mine(Some(U256::from(3)), Some(U256::from(3))).await + .anvil_set_auto_mine(true) + .await + .expect("Failed to set auto mine"); + anvil_provider + .anvil_mine(Some(U256::from(3)), Some(U256::from(3))) + .await .expect("Failed to mine"); anvil_provider - .anvil_impersonate_account(signer.default_signer().address().clone()).await + .anvil_impersonate_account(signer.default_signer().address().clone()) + .await .expect("Failed to impersonate account"); return (anvil, anvil_provider, signer); @@ -760,7 +798,7 @@ mod integration_tests { async fn deploy_scribe, T: Clone + Transport, N: Network>( provider: P, signer: EthereumWallet, - challenge_period: u16 + challenge_period: u16, ) -> ScribeOptimisiticInstance { // deploy scribe instance let initial_authed = signer.default_signer().address(); @@ -771,18 +809,16 @@ mod integration_tests { 0123456789abcdef0123456789abcdef " ); - let scribe_optimistic = ScribeOptimisitic::deploy( - provider, - initial_authed.clone(), - FixedBytes(wat).clone() - ); + let scribe_optimistic = + ScribeOptimisitic::deploy(provider, initial_authed.clone(), FixedBytes(wat).clone()); let scribe_optimistic = scribe_optimistic.await.unwrap(); let receipt = scribe_optimistic.setBar(1); let receipt = receipt.send().await.expect("Failed to set bar"); receipt .with_timeout(Some(Duration::from_secs(15))) - .watch().await + .watch() + .await .expect("Failed to set bar"); // TODO generate the public key and v r s from private key @@ -790,23 +826,30 @@ mod integration_tests { let pub_key = LibSecp256k1::Point { x: U256::from_str_radix( "95726579611468854699048782904089382286224374897874075137780214269565012360365", - 10 - ).unwrap(), + 10, + ) + .unwrap(), y: U256::from_str_radix( "95517337328947037046967076057450300670379811052080651187456799621439597083272", - 10 - ).unwrap(), + 10, + ) + .unwrap(), }; let ecdsa_data = IScribe::ECDSAData { v: 0x1b, - r: FixedBytes(hex!("0ced9fd231ad454eaac301d6e15a56b6aaa839a55d664757e3ace927e95948ec")), - s: FixedBytes(hex!("21b2813ad85945f320d7728fbfc9b83cbbb564135e67c16db16d5f4e74392119")), + r: FixedBytes(hex!( + "0ced9fd231ad454eaac301d6e15a56b6aaa839a55d664757e3ace927e95948ec" + )), + s: FixedBytes(hex!( + "21b2813ad85945f320d7728fbfc9b83cbbb564135e67c16db16d5f4e74392119" + )), }; let receipt = scribe_optimistic.lift_0(pub_key, ecdsa_data); let receipt = receipt.send().await.expect("Failed to lift validator"); receipt .with_timeout(Some(Duration::from_secs(15))) - .watch().await + .watch() + .await .expect("Failed to lift validator"); // set challenge period @@ -814,7 +857,8 @@ mod integration_tests { let receipt = receipt.send().await.expect("Failed to lift validator"); receipt .with_timeout(Some(Duration::from_secs(15))) - .watch().await + .watch() + .await .expect("Failed to set opChallenge"); return scribe_optimistic; @@ -824,20 +868,21 @@ mod integration_tests { url: Url, signer: EthereumWallet, cancel_token: CancellationToken, - addresses: Vec
+ addresses: Vec
, ) { - let client = ClientBuilder::default().layer(RetryBackoffLayer::new(15, 200, 300)).http(url); + let client = ClientBuilder::default() + .layer(RetryBackoffLayer::new(15, 200, 300)) + .http(url); let nonce_mananger = NonceFiller::::default(); let provider = Arc::new( ProviderBuilder::new() .with_recommended_fillers() - .filler(ChainIdFiller::new(Some(31337))) .filler(nonce_mananger.clone()) .wallet(signer.clone()) - .on_client(client) + .on_client(client), ); let flashbot_provider = provider.clone(); @@ -853,7 +898,7 @@ mod integration_tests { cancel_token.clone(), provider.clone(), tx.clone(), - 1 + 1, ); // Create event distributor @@ -862,7 +907,7 @@ mod integration_tests { cancel_token.clone(), provider.clone(), flashbot_provider.clone(), - rx + rx, ); // Run events listener process @@ -892,18 +937,20 @@ mod integration_tests { async fn poll_balance_is_zero( anvil_provider: &AnvilProvider, address: &Address, - timeout: u64 + timeout: u64, ) -> bool { let start_time = chrono::Utc::now().timestamp() as u64; while (chrono::Utc::now().timestamp() as u64) < start_time + timeout { let balance = anvil_provider - .get_balance(address.clone()).await + .get_balance(address.clone()) + .await .expect("Failed to get balance"); if balance == U256::from(0) { return true; } anvil_provider - .anvil_mine(Some(U256::from(1)), Some(U256::from(1))).await + .anvil_mine(Some(U256::from(1)), Some(U256::from(1))) + .await .expect("Failed to mine"); tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; } @@ -913,22 +960,23 @@ mod integration_tests { async fn make_invalid_op_poke( age: u64, private_key: &str, - scribe_optimisitic: &ScribeOptimisiticInstance, AnvilProvider, Ethereum> + scribe_optimisitic: &ScribeOptimisiticInstance, AnvilProvider, Ethereum>, ) { let poke_data: IScribe::PokeData = IScribe::PokeData { val: 10, age: age as u32, }; let schnorr_data = IScribe::SchnorrData { - signature: FixedBytes( - hex!("0000000000000000000000000000000000000000000000000000000000000000") - ), + signature: FixedBytes(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), commitment: alloy::primitives::Address::ZERO.clone(), feedIds: hex!("00").into(), }; let op_poke_message = scribe_optimisitic .constructOpPokeMessage(poke_data.clone(), schnorr_data.clone()) - .call().await + .call() + .await .expect("Failed to read current age"); let op_poke_message = op_poke_message._0; let ecdsa_signer = private_key @@ -944,11 +992,8 @@ mod integration_tests { }; // Make the invalid poke - let receipt = scribe_optimisitic.opPoke( - poke_data.clone(), - schnorr_data.clone(), - ecdsa_data - ); + let receipt = + scribe_optimisitic.opPoke(poke_data.clone(), schnorr_data.clone(), ecdsa_data); let receipt = receipt.send().await.expect("Failed to send op poke"); receipt.watch().await.expect("Failed to watch op poke"); } diff --git a/src/wallet.rs b/src/wallet.rs index 182a817..bb92add 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -29,6 +29,7 @@ pub trait PrivateKeyWallet { fn raw_private_key(&self) -> Option; #[track_caller] + #[allow(clippy::wrong_self_convention)] fn from_private_key(&self, private_key: &str) -> Result> { debug!("Using private key from arguments"); From ebae99417b455bae09403a25409d02cd3bdfd184 Mon Sep 17 00:00:00 2001 From: Max Wickham Date: Tue, 29 Oct 2024 09:32:29 +0000 Subject: [PATCH 25/40] comment --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index be977c7..d9903ff 100644 --- a/src/main.rs +++ b/src/main.rs @@ -708,7 +708,7 @@ mod integration_tests { // TODO: test dont challenge if receive op challenged - // TODO: test flashbotnused first, fallback to normal rpc + // TODO: test flashbot used first, fallback to normal rpc // -- Helper functions -- From 9c90548fc8d76eafe493fd77d88275c012c1f305 Mon Sep 17 00:00:00 2001 From: Konstantin Zolotarev Date: Sat, 9 Nov 2024 20:50:07 +0200 Subject: [PATCH 26/40] Reworked logging + removed dangerous unwraps --- crates/scribe/src/contract.rs | 58 +++++- .../{event_handler.rs => events_handler.rs} | 175 ++++++++++++------ crates/scribe/src/events_listener.rs | 51 +++-- crates/scribe/src/lib.rs | 2 +- crates/scribe/src/metrics.rs | 10 +- src/main.rs | 10 +- 6 files changed, 221 insertions(+), 85 deletions(-) rename crates/scribe/src/{event_handler.rs => events_handler.rs} (63%) diff --git a/crates/scribe/src/contract.rs b/crates/scribe/src/contract.rs index 4184a0f..8271f0b 100644 --- a/crates/scribe/src/contract.rs +++ b/crates/scribe/src/contract.rs @@ -61,7 +61,10 @@ impl Event { /// Creates a new [Event] from a Log pub fn from_log(log: Log) -> Result { let Some(topic) = log.topic0() else { - bail!("No topic found in log for tx {:?}", log.transaction_hash) + bail!( + "Failed to convert log to event: empty topic0 for log under tx {:?}", + log.transaction_hash + ) }; // Match the event by `topic0 @@ -74,7 +77,10 @@ impl Event { let event = decode_log::(&log)?; Ok(Self::OpPokeChallengedSuccessfully(event)) } - _ => bail!("Unknown event with topic0: {:#x}", topic), + _ => bail!( + "Failed to convert log to event: Unknown topic0: {:#x}", + topic + ), } } } @@ -140,10 +146,14 @@ impl ScribeOptimisticProviderInstance { } impl ScribeOptimisticProvider for ScribeOptimisticProviderInstance { + /// Returns challenge period from ScribeOptimistic smart contract deployed to `address`. async fn get_challenge_period(&self) -> Result { Ok(self.contract.opChallengePeriod().call().await?._0) } + /// Validates given `OpPoked` schnorr signature. + /// Uses `constructPokeMessage` and `isAcceptableSchnorrSignatureNow` methods from the contract. + /// Returns true if the signature is valid. async fn is_schnorr_signature_valid(&self, op_poked: OpPoked) -> Result { log::trace!( "Contract {:?}: Validating OpPoke signature", @@ -154,39 +164,71 @@ impl ScribeOptimisticProvider for ScribeOptimisticProviderInstance { .contract .constructPokeMessage(op_poked.pokeData) .call() - .await? + .await + .wrap_err_with(|| { + format!( + "Contract {:?}: failed to construct poke message", + self.contract.address() + ) + })? ._0; let acceptable = self .contract .isAcceptableSchnorrSignatureNow(message, op_poked.schnorrData) .call() - .await? + .await + .wrap_err_with(|| { + format!( + "Contract {:?}: failed to call isAcceptableSchnorrSignatureNow() method", + self.contract.address() + ) + })? ._0; Ok(acceptable) } + /// Challenges given `OpPoked` event with given `schnorr_data`. + /// Executes `opChallenge` method on the contract and returns transaction hash if everything worked well. async fn challenge(&self, schnorr_data: SchnorrData) -> Result> { - log::info!("Contract {:?}: Challenging OpPoke", self.contract.address(),); + log::warn!( + "Contract {:?}: Challenging OpPoke with shnorr_data {:?}", + self.contract.address(), + &schnorr_data + ); + let transaction = self .contract - .opChallenge(schnorr_data) + .opChallenge(schnorr_data.clone()) // TODO: set gas limit properly .gas(200000); transaction .send() - .await? + .await + .wrap_err_with(|| { + format!( + "Contract {:?} Failed to send transaction", + self.contract.address() + ) + })? .watch() .await - .wrap_err("Failed to challenge") + .wrap_err_with(|| { + format!( + "Contract {:?} Failed to wait for challenge confirmation", + self.contract.address() + ) + }) } + /// Returns the address of the contract. fn address(&self) -> &Address { self.contract.address() } + /// Returns a new provider (clone) with the same signer. fn get_new_provider(&self) -> Arc { self.contract.provider().clone() } diff --git a/crates/scribe/src/event_handler.rs b/crates/scribe/src/events_handler.rs similarity index 63% rename from crates/scribe/src/event_handler.rs rename to crates/scribe/src/events_handler.rs index 48119c5..82e510c 100644 --- a/crates/scribe/src/event_handler.rs +++ b/crates/scribe/src/events_handler.rs @@ -20,7 +20,7 @@ use std::time::Duration; use alloy::primitives::Address; use alloy::providers::Provider; use alloy::rpc::types::BlockTransactionsKind; -use eyre::{bail, Result}; +use eyre::{bail, Context, Result}; use tokio::sync::mpsc::{Receiver, Sender}; use tokio::sync::Mutex; use tokio::task::JoinSet; @@ -32,13 +32,18 @@ use crate::contract::{ EventWithMetadata, ScribeOptimisticProvider, ScribeOptimisticProviderInstance, }; use crate::events_listener::RetryProviderWithSigner; +use crate::metrics; const CHALLENGE_POKE_DELAY_MS: u64 = 200; const FLASHBOT_CHALLENGE_RETRY_COUNT: u64 = 3; const CLASSIC_CHALLENGE_RETRY_COUNT: u64 = 3; -// Takes event logs and distributes them to the appropriate contract handler -// Each `ScribeOptimistic` instance deployed to address has its own `EventHandler` process. +/// You can think about `EventDistributor` as a router for events. +/// It listens for contract events from logs and distributes them to the appropriate `ScribeEventsProcessor` instance via channels. +/// Each `ScribeOptimistic` instance deployed to address should have its own `ScribeEventsProcessor` processor. +/// +/// `EventDistributor` is responsible for creating and managing `ScribeEventsProcessor` instances. +/// You don't need to spawn anything manually, just call `start` and it will take care of the rest. pub struct EventDistributor { addresses: Vec
, cancel: CancellationToken, @@ -66,6 +71,8 @@ impl EventDistributor { } } + /// Spawns list of `ScribeEventsProcessor` instances for each address in `addresses`, + /// creates a channel for each address and starts listening for events. pub async fn start(&mut self) -> Result<()> { let mut contract_handler_set = JoinSet::new(); // Create a contract handler for each scribe address @@ -84,13 +91,16 @@ impl EventDistributor { let _ = contract_handler.start().await; }); } + log::debug!("EventDistributor: All processors started, listening for events..."); loop { tokio::select! { _ = self.cancel.cancelled() => { + log::info!("EventDistributor: received cancel command, stopping..."); break; } event = self.rx.recv() => { + log::trace!("EventDistributor: Received event: {:?}", event); if let Some(event) = event { // Send the event to the appropriate contract handler match self.txs.get(&event.address) { @@ -99,7 +109,10 @@ impl EventDistributor { } _ => { // Should never happen - log::warn!("Received event for unknown address: {:?}", event.address); + log::warn!( + "EventDistributor: Received event from unknown address or receiver is dead: {:?}", + event + ); } } } @@ -114,6 +127,11 @@ impl EventDistributor { // Receives preparsed [crate::contract::EventWithMetadata] events for a `ScribeOptimistic` instance on address, // validates `OpPoked` events and challenges them if they are invalid and within the challenge period. +// +// `OpPoked` validation and challenge logic is launched in a separate task and is cancellable. +// When new `OpPoked` event is received, it's challenge process is started with a delay of `CHALLENGE_POKE_DELAY_MS`. +// If next received event will be `OpPokeChallengedSuccessfully`, the challenge process is cancelled (no need to spend resources on validation). +// Otherwise, the challenge process will start procecssing. struct ScribeEventsProcessor { address: Address, provider: Arc, @@ -143,23 +161,27 @@ impl ScribeEventsProcessor { } } - pub async fn start(&mut self) -> Result<()> { - // We have to fail if no challenge period is fetched - self.fetch_challenge_period().await.unwrap(); + async fn start(&mut self) -> Result<()> { + log::debug!( + "ScribeEventsProcessor[{:?}] Starting new contract handler", + self.address + ); + // We have to fail if no challenge period is fetched on start + self.refresh_challenge_period().await.unwrap(); loop { tokio::select! { // main process terminates, need to finish work and exit... _ = self.cancellation_token.cancelled() => { log::info!( - "[{:?}] Cancellation requested, stopping contract handler", + "ScribeEventsProcessor[{:?}] Cancellation requested, stopping contract handler", self.address ); return Ok(()); } // new [EventWithMetadata] received, process it... event = self.rx.recv() => { - log::debug!("[{:?}] Received event: {:?}", self.address, event); + log::trace!("ScribeEventsProcessor[{:?}] Received event: {:?}", self.address, event); if let Some(event) = event { match &event.event { @@ -167,11 +189,14 @@ impl ScribeEventsProcessor { // if not - check if event is within the challenge period, send challenge. // If `schnorr_signature` is valid, do nothing. Event::OpPoked(op_poked) => { - log::debug!("[{:?}] OpPoked received", self.address); + log::trace!( + "ScribeEventsProcessor[{:?}] OpPoked received, start processing", + self.address + ); if let Err(err) = self.process_op_poked(event.clone(), op_poked.clone()).await { log::error!( - "[{:?}] Error processing OpPoked event: {:?}", + "ScribeEventsProcessor[{:?}] Error processing OpPoked event: {:?}", self.address, err ); @@ -179,15 +204,25 @@ impl ScribeEventsProcessor { } // If the challenge is already successful, cancel the previous challenge process Event::OpPokeChallengedSuccessfully(_) => { + log::debug!( + "ScribeEventsProcessor[{:?}] OpPokeChallengedSuccessfully received, cancelling challenge process", + self.address + ); self.cancel_challenge().await; } } + } else { + log::warn!( + "ScribeEventsProcessor[{:?}] Received None event in contract handler", + self.address + ); } } } } } + // Starts the validation and challenge process for the `OpPoked` event. async fn process_op_poked( &mut self, event: EventWithMetadata, @@ -196,12 +231,15 @@ impl ScribeEventsProcessor { // Check if the poke is within the challenge period let event_timestamp = match event.log.block_timestamp { Some(timestamp) => timestamp, - None => self.get_timestamp_from_block(&event).await?, + None => self + .get_timestamp_from_block(&event) + .await + .wrap_err_with(|| format!("Failed to get timestamp from block via RPC call"))?, }; let current_timestamp = chrono::Utc::now().timestamp() as u64; log::debug!( - "[{:?}] OpPoked, event_timestamp: {:?}, current_timestamp: {:?}", + "ScribeEventsProcessor[{:?}] OpPoked, event_timestamp: {:?}, current_timestamp: {:?}", self.address, event_timestamp, current_timestamp @@ -209,12 +247,16 @@ impl ScribeEventsProcessor { if current_timestamp - event_timestamp > self.challenge_period.unwrap() { // This log is expected in tests, tests must be updated if log message is changed log::debug!( - "[{:?}] OpPoked received outside of challenge period", + "ScribeEventsProcessor[{:?}] OpPoked received outside of challenge period", self.address ); return Ok(()); } - log::debug!("Spawning challenge..."); + + log::debug!( + "ScribeEventsProcessor[{:?}] Spawning validation and challenge process...", + self.address + ); self.spawn_challenge(op_poked).await; Ok(()) @@ -224,11 +266,7 @@ impl ScribeEventsProcessor { async fn get_timestamp_from_block(&self, event: &EventWithMetadata) -> Result { // Possible block number to be missing for unconfirmed blocks if event.log.block_number.is_none() { - bail!( - "[{:?}] Block number is missing for log {:?}", - self.address, - event.log - ); + bail!("Block number is missing for log {:?}", event.log); } let block_number = event.log.block_number.unwrap(); @@ -240,25 +278,18 @@ impl ScribeEventsProcessor { let block = match block { Some(block) => block, None => { - bail!( - "[{:?}] Block with number {:?} not found", - self.address, - block_number - ); + bail!("Block with number {:?} not found", block_number); } }; Ok(block.header.timestamp) } - async fn fetch_challenge_period(&mut self) -> Result<()> { - // TODO: Add expiration time for the challenge period - if self.challenge_period.is_some() { - return Ok(()); - } + async fn refresh_challenge_period(&mut self) -> Result<()> { let period = ScribeOptimisticProviderInstance::new(self.address, self.provider.clone()) .get_challenge_period() .await?; + self.challenge_period = Some(period as u64); Ok(()) } @@ -269,7 +300,7 @@ impl ScribeEventsProcessor { // Create a new cancellation token self.cancel_challenge = Some(CancellationToken::new()); // Create a new challenger instance - let challenge_handler = Some(Arc::new(Mutex::new(ChallengeSender::new( + let challenge_handler = Some(Arc::new(Mutex::new(OpPokedValidator::new( op_poked, self.cancellation_token.clone(), // cancel_challenge garunteed to be Some @@ -278,19 +309,24 @@ impl ScribeEventsProcessor { self.provider.clone(), self.flashbot_provider.clone(), )))); - // TODO: Validate challenge period first... // Spawn the asynchronous task tokio::spawn(async move { let handler = challenge_handler.as_ref().unwrap().lock().await; let _ = handler.start().await; }); - log::debug!("[{:?}] Spawned New challenger process", self.address); + log::debug!( + "ScribeEventsProcessor[{:?}] Spawned New challenger process", + self.address + ); } async fn cancel_challenge(&mut self) { if let Some(cancel) = &self.cancel_challenge { - log::debug!("[{:?}] Cancelling existing challenge", self.address); + log::debug!( + "ScribeEventsProcessor[{:?}] Cancelling existing challenge", + self.address + ); cancel.cancel(); self.cancel_challenge = None; } @@ -300,17 +336,17 @@ impl ScribeEventsProcessor { // Handle the challenge process for a specific OpPoked event after a delay // If cancelled before end of delay or inbewteen retries stop process // First try challenge with flashbot provider, then with normal provider -struct ChallengeSender { - pub op_poked: OpPoked, - pub global_cancel: CancellationToken, - pub cancel: CancellationToken, +struct OpPokedValidator { + op_poked: OpPoked, + global_cancel: CancellationToken, + cancel: CancellationToken, address: Address, // TODO replace with ScribeOptimisticProviderInstances provider: Arc, flashbot_provider: Arc, } -impl ChallengeSender { +impl OpPokedValidator { pub fn new( op_poked: OpPoked, global_cancel: CancellationToken, @@ -331,19 +367,22 @@ impl ChallengeSender { pub async fn start(&self) -> Result<()> { // This checked for in tests, tests must be updated if log is changed - log::debug!("[{:?}] Challenge started", self.address); + log::debug!( + "OpPokedValidator[{:?}] OpPoked validation started", + self.address + ); // Perform the challenge after 200ms let mut challenge_attempts: u64 = 0; loop { tokio::select! { // Check if the challenge has been cancelled _ = self.cancel.cancelled() => { - log::debug!("[{:?}] Challenge cancelled", self.address); + log::debug!("OpPokedValidator[{:?}] Challenge cancelled", self.address); break; } // Check if the global cancel command sent _ = self.global_cancel.cancelled() => { - log::debug!("[{:?}] Global cancel command received", self.address); + log::debug!("OpPokedValidator[{:?}] Global cancel command received", self.address); break; } _ = tokio::time::sleep(Duration::from_millis(CHALLENGE_POKE_DELAY_MS)) => { @@ -354,48 +393,80 @@ impl ChallengeSender { ).is_schnorr_signature_valid(self.op_poked.clone()).await?; if is_valid { - log::debug!("[{:?}] OpPoked is valid, no need to challenge", self.address); + log::debug!("OpPokedValidator[{:?}] OpPoked is valid, no need to challenge", self.address); break; } const RETRY_RANGE_END: u64 = CLASSIC_CHALLENGE_RETRY_COUNT+FLASHBOT_CHALLENGE_RETRY_COUNT; match challenge_attempts { 0..FLASHBOT_CHALLENGE_RETRY_COUNT => { - + log::debug!("OpPokedValidator[{:?}] Attempting flashbot challenge", self.address); let contract = ScribeOptimisticProviderInstance::new( self.address, self.flashbot_provider.clone() ); + let result = contract.challenge(self.op_poked.schnorrData.clone()).await; - let result = contract.challenge( - self.op_poked.schnorrData.clone() - ).await; match result { Ok(tx_hash) => { - log::debug!("[{:?}] Flashbot transaction sent: {:?}", self.address, tx_hash); + log::info!( + "OpPokedValidator[{:?}] Flashbot transaction sent: {:?}", + self.address, + tx_hash + ); + + // Increment the challenge counter + metrics::inc_challenge_counter( + self.address, + tx_hash, + true, + ); break; } Err(e) => { - log::error!("[{:?}] Failed to send flashbot transaction: {:?}", self.address, e); + log::error!( + "OpPokedValidator[{:?}] Failed to send flashbot transaction: {:?}", + self.address, + e + ); } } } FLASHBOT_CHALLENGE_RETRY_COUNT..RETRY_RANGE_END => { + log::debug!("OpPokedValidator[{:?}] Attempting public challenge", self.address); let contract = ScribeOptimisticProviderInstance::new( self.address, self.provider.clone() ); match contract.challenge(self.op_poked.schnorrData.clone()).await { Ok(tx_hash) => { - log::debug!("[{:?}] Challenge transaction sent: {:?}", self.address, tx_hash); + log::info!( + "OpPokedValidator[{:?}] Challenge transaction sent: {:?}", + self.address, + tx_hash + ); + // Increment the challenge counter + metrics::inc_challenge_counter( + self.address, + tx_hash, + false, + ); break; } Err(e) => { - log::error!("[{:?}] Failed to send challenge transaction: {:?}", self.address, e); + log::error!( + "OpPokedValidator[{:?}] Failed to send challenge transaction: {:?}", + self.address, + e + ); } } } _ => { - log::error!("[{:?}] Challenge failed, total attempts {:?}", self.address, challenge_attempts); + log::error!( + "OpPokedValidator[{:?}] Challenge failed, total attempts {:?}", + self.address, + challenge_attempts + ); break; } } diff --git a/crates/scribe/src/events_listener.rs b/crates/scribe/src/events_listener.rs index 22cb9da..2a48d50 100644 --- a/crates/scribe/src/events_listener.rs +++ b/crates/scribe/src/events_listener.rs @@ -32,7 +32,7 @@ use alloy::{ layers::RetryBackoffService, }, }; -use eyre::{Context, Result}; +use eyre::{bail, Context, Result}; use tokio::{select, sync::mpsc::Sender}; use tokio_util::sync::CancellationToken; @@ -46,6 +46,7 @@ use crate::{ // const POLL_INTERVAL_SEC: u64 = 30; const MAX_ADDRESS_PER_REQUEST: usize = 50; +const MAX_RETRY_COUNT: u16 = 5; /// The provider type used to interact with the Ethereum network. pub type RpcRetryProvider = RetryBackoffService>; @@ -73,6 +74,11 @@ pub type RetryProviderWithSigner = FillProvider< Ethereum, >; +/// Poller is responsible for polling for new events in the Ethereum network. +/// It will poll for new events in the range `self.last_processes_block..latest_block` +/// every `self.poll_interval_seconds` seconds. +/// It will send the new events to the `tx` channel. +/// For optimization reasons, it will query for events in chunks of `MAX_ADDRESS_PER_REQUEST` addresses. #[derive(Debug, Clone)] pub struct Poller { addresses: Vec
, @@ -81,6 +87,7 @@ pub struct Poller { last_processes_block: Option, tx: Sender, poll_interval_seconds: u64, + retry_count: u16, } impl Poller { @@ -98,6 +105,7 @@ impl Poller { tx, last_processes_block: None, poll_interval_seconds, + retry_count: 0, } } @@ -116,7 +124,7 @@ impl Poller { OpPokeChallengedSuccessfully::SIGNATURE_HASH, ]); - log::trace!("[{:?}] Getting for new events", &chunk); + log::trace!("Poller: [{:?}] Getting for new events", &chunk); self.provider .get_logs(&filter) @@ -126,16 +134,16 @@ impl Poller { // Poll for new events in block range `self.last_processes_block..latest_block` async fn poll(&mut self) -> Result<()> { - log::trace!("Polling for new events"); + log::trace!("Poller: start polling for new events"); // Get latest block number - let latest_block = self.provider.get_block_number().await.unwrap(); + let latest_block = self.provider.get_block_number().await?; if self.last_processes_block.is_none() { self.last_processes_block = Some(latest_block); } // TODO remove this line if latest_block <= self.last_processes_block.unwrap_or(0) { log::warn!( - "Latest block {:?} is not greater than last processed block {:?}", + "Poller: latest block {:?} is not greater than last processed block {:?}", latest_block, self.last_processes_block ); @@ -146,34 +154,37 @@ impl Poller { let logs = self .query_logs( chunk.to_vec(), - self.last_processes_block.unwrap(), + self.last_processes_block.unwrap(), // unwrap is safe because we checked it in the beginning latest_block, ) .await; match logs { Ok(logs) => { - log::debug!("[{:?}] Received {} logs", chunk, logs.len()); + log::debug!("Poller: [{:?}] Received {} logs", chunk, logs.len()); for log in logs { match EventWithMetadata::from_log(log) { Ok(event) => { - log::debug!("[{:?}] Event received: {:?}", chunk, &event); + log::debug!("Poller: [{:?}] Event received: {:?}", chunk, &event); // Send event to the channel self.tx.send(event).await?; } Err(e) => { - log::error!("[{:?}] Failed to parse log: {:?}", chunk, e); + log::error!("Poller: [{:?}] Failed to parse log: {:?}", chunk, e); continue; } }; } } Err(e) => { - log::error!("Failed to query logs: {:?}", e); + // TODO: retry? + bail!("Poller: Failed to query logs: {:?}", e); } } } + // Reset retry count, or might be issues + self.retry_count = 0; self.last_processes_block = Some(latest_block); // Updating last scanned block metric metrics::set_last_scanned_block( @@ -186,17 +197,29 @@ impl Poller { /// Start the event listener pub async fn start(&mut self) -> Result<()> { - log::info!("Starting polling events from RPC..."); + log::info!("Poller: Starting polling events from RPC..."); loop { select! { _ = self.cancelation_token.cancelled() => { - log::info!("Challenger cancelled"); + log::info!("Poller: got cancel signal, terminating..."); return Ok(()); } _ = tokio::time::sleep(Duration::from_secs(self.poll_interval_seconds)) => { - log::info!("Executing tick for events listener..."); - self.poll().await?; + log::info!("Poller: Executing tick for events listener..."); + if let Err(err) = self.poll().await { + if self.retry_count >= MAX_RETRY_COUNT { + log::error!( + "Poller: Max {} reties reached, will not retry anymore: {:?}", + MAX_RETRY_COUNT, + err + ); + return Err(err); + } + + self.retry_count += 1; + log::error!("Poller: Failed to poll for events, will retry: {:?}", err); + } } } } diff --git a/crates/scribe/src/lib.rs b/crates/scribe/src/lib.rs index 1c191f5..712cee5 100644 --- a/crates/scribe/src/lib.rs +++ b/crates/scribe/src/lib.rs @@ -14,6 +14,6 @@ // along with this program. If not, see . pub mod contract; -pub mod event_handler; +pub mod events_handler; pub mod events_listener; pub mod metrics; diff --git a/crates/scribe/src/metrics.rs b/crates/scribe/src/metrics.rs index e7bfd60..29182d8 100644 --- a/crates/scribe/src/metrics.rs +++ b/crates/scribe/src/metrics.rs @@ -27,20 +27,20 @@ pub fn set_last_scanned_block(from: Address, block: i64) { } /// `inc_errors_counter` increments the errors counter for given `address`, `from` account -pub fn inc_errors_counter(address: Address, from: Address, error: &str) { +pub fn inc_errors_counter(address: Address, error: &str) { + // TODO: Use it... let labels = [ ("address", format!("{:?}", address)), - ("from", format!("{:?}", from)), ("error", String::from(error)), ]; counter!(ERRORS_COUNTER, &labels).increment(1); } -/// `inc_challenge_counter` increments the challenges counter for given `address`, `from` account and `tx` hash. -pub fn inc_challenge_counter(address: Address, from: Address, tx: B256) { +/// `inc_challenge_counter` increments the challenges counter for given `address` and `tx` hash. +pub fn inc_challenge_counter(address: Address, tx: B256, flashbots: bool) { let labels = [ ("address", format!("{:?}", address)), - ("from", format!("{:?}", from)), + ("flashbots", format!("{:?}", flashbots)), ("tx", format!("{:?}", tx)), ]; counter!(CHALLENGE_COUNTER, &labels).increment(1); diff --git a/src/main.rs b/src/main.rs index c97bdb8..97a55db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,7 +32,7 @@ use std::{env, panic, path::PathBuf, time::Duration}; use tokio::time::sleep; use tokio_util::sync::CancellationToken; -use scribe::event_handler; +use scribe::events_handler; mod wallet; @@ -220,7 +220,7 @@ async fn main() -> Result<()> { ); // Create event distributor - let mut event_distributor = event_handler::EventDistributor::new( + let mut event_distributor = events_handler::EventDistributor::new( addresses.clone(), cancel_token.clone(), provider.clone(), @@ -423,7 +423,7 @@ mod integration_tests { use alloy::transports::Transport; use futures_util::future::join_all; use scribe::contract::EventWithMetadata; - use scribe::event_handler; + use scribe::events_handler; use scribe::events_listener::Poller; use scribe_optimistic::{ IScribe, LibSecp256k1, ScribeOptimisitic, ScribeOptimisitic::ScribeOptimisiticInstance, @@ -608,7 +608,7 @@ mod integration_tests { found |= log .body .to_lowercase() - .contains(&"Challenge started".to_lowercase()); + .contains(&"OpPoked validation started".to_lowercase()); } success.set(found); }); @@ -902,7 +902,7 @@ mod integration_tests { ); // Create event distributor - let mut event_distributor = event_handler::EventDistributor::new( + let mut event_distributor = events_handler::EventDistributor::new( addresses.clone(), cancel_token.clone(), provider.clone(), From a9ac662d7bca767e8f2e812b3d02dceacecf281a Mon Sep 17 00:00:00 2001 From: Konstantin Zolotarev Date: Sat, 9 Nov 2024 20:52:30 +0200 Subject: [PATCH 27/40] Fix clippy --- crates/scribe/src/events_handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/scribe/src/events_handler.rs b/crates/scribe/src/events_handler.rs index 82e510c..4714af6 100644 --- a/crates/scribe/src/events_handler.rs +++ b/crates/scribe/src/events_handler.rs @@ -234,7 +234,7 @@ impl ScribeEventsProcessor { None => self .get_timestamp_from_block(&event) .await - .wrap_err_with(|| format!("Failed to get timestamp from block via RPC call"))?, + .wrap_err("Failed to get timestamp from block via RPC call")?, }; let current_timestamp = chrono::Utc::now().timestamp() as u64; From cbfb2355b571a7d3781211e4820badd3a96e4fdd Mon Sep 17 00:00:00 2001 From: Konstantin Zolotarev Date: Sat, 9 Nov 2024 21:15:23 +0200 Subject: [PATCH 28/40] Fix clippy --- src/main.rs | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/main.rs b/src/main.rs index 97a55db..bfe64d4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -506,11 +506,10 @@ mod integration_tests { let mut scribe_addresses = vec![]; let mut balance_updates = vec![]; - for i in 0..NUM_SCRIBE_INSTANCES { - let scribe_optimistic = &scribes[i]; - scribe_addresses.push(scribe_optimistic.address().clone()); + for scribe_optimistic in scribes.iter().take(NUM_SCRIBE_INSTANCES) { + scribe_addresses.push(*scribe_optimistic.address()); balance_updates.push(anvil_provider.anvil_set_balance( - scribes[i].address().clone(), + *scribe_optimistic.address(), U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap(), )); } @@ -536,7 +535,7 @@ mod integration_tests { let signer: EthereumWallet = EthereumWallet::new(PrivateKeySigner::random()); anvil_provider .anvil_set_balance( - signer.default_signer().address().clone(), + signer.default_signer().address(), U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap(), ) .await @@ -648,7 +647,7 @@ mod integration_tests { let scribe_optimistic = deploy_scribe(anvil_provider.clone(), signer.clone(), 300).await; anvil_provider .anvil_set_balance( - scribe_optimistic.address().clone(), + *scribe_optimistic.address(), U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap(), ) .await @@ -670,12 +669,12 @@ mod integration_tests { let signer: EthereumWallet = EthereumWallet::new(PrivateKeySigner::random()); anvil_provider .anvil_set_balance( - signer.default_signer().address().clone(), + signer.default_signer().address(), U256::from_str_radix("1000000000000000000000000000000000000000", 10).unwrap(), ) .await .expect("Unable to set balance"); - let addresses = vec![scribe_optimistic.address().clone()]; + let addresses = vec![*scribe_optimistic.address()]; let cancel_token = cancel_token.clone(); let url = anvil.endpoint_url(); let signer = signer.clone(); @@ -695,7 +694,7 @@ mod integration_tests { // ------------------------------------------------------------------------------------------------------------ // Assert that the current contract balance is not 0 let balance = anvil_provider - .get_balance(scribe_optimistic.address().clone()) + .get_balance(*scribe_optimistic.address()) .await .expect("Failed to get balance"); assert_ne!(balance, U256::from(0)); @@ -788,11 +787,11 @@ mod integration_tests { .await .expect("Failed to mine"); anvil_provider - .anvil_impersonate_account(signer.default_signer().address().clone()) + .anvil_impersonate_account(signer.default_signer().address()) .await .expect("Failed to impersonate account"); - return (anvil, anvil_provider, signer); + (anvil, anvil_provider, signer) } async fn deploy_scribe, T: Clone + Transport, N: Network>( @@ -810,7 +809,7 @@ mod integration_tests { " ); let scribe_optimistic = - ScribeOptimisitic::deploy(provider, initial_authed.clone(), FixedBytes(wat).clone()); + ScribeOptimisitic::deploy(provider, initial_authed, FixedBytes(wat)); let scribe_optimistic = scribe_optimistic.await.unwrap(); let receipt = scribe_optimistic.setBar(1); @@ -853,7 +852,7 @@ mod integration_tests { .expect("Failed to lift validator"); // set challenge period - let receipt = scribe_optimistic.setOpChallengePeriod(challenge_period as u16); + let receipt = scribe_optimistic.setOpChallengePeriod(challenge_period); let receipt = receipt.send().await.expect("Failed to lift validator"); receipt .with_timeout(Some(Duration::from_secs(15))) @@ -861,7 +860,7 @@ mod integration_tests { .await .expect("Failed to set opChallenge"); - return scribe_optimistic; + scribe_optimistic } async fn start_event_listener( @@ -942,7 +941,7 @@ mod integration_tests { let start_time = chrono::Utc::now().timestamp() as u64; while (chrono::Utc::now().timestamp() as u64) < start_time + timeout { let balance = anvil_provider - .get_balance(address.clone()) + .get_balance(*address) .await .expect("Failed to get balance"); if balance == U256::from(0) { @@ -954,7 +953,7 @@ mod integration_tests { .expect("Failed to mine"); tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; } - return false; + false } async fn make_invalid_op_poke( @@ -970,7 +969,7 @@ mod integration_tests { signature: FixedBytes(hex!( "0000000000000000000000000000000000000000000000000000000000000000" )), - commitment: alloy::primitives::Address::ZERO.clone(), + commitment: alloy::primitives::Address::ZERO, feedIds: hex!("00").into(), }; let op_poke_message = scribe_optimisitic From 70b8c0da7e04f846014478e7828d031c48318367 Mon Sep 17 00:00:00 2001 From: Konstantin Zolotarev Date: Sun, 10 Nov 2024 20:01:42 +0200 Subject: [PATCH 29/40] Updated logging and default levels --- crates/scribe/src/events_listener.rs | 3 ++- src/main.rs | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/scribe/src/events_listener.rs b/crates/scribe/src/events_listener.rs index 2a48d50..6c0b738 100644 --- a/crates/scribe/src/events_listener.rs +++ b/crates/scribe/src/events_listener.rs @@ -134,10 +134,11 @@ impl Poller { // Poll for new events in block range `self.last_processes_block..latest_block` async fn poll(&mut self) -> Result<()> { - log::trace!("Poller: start polling for new events"); + log::trace!("Poller: polling for new events"); // Get latest block number let latest_block = self.provider.get_block_number().await?; if self.last_processes_block.is_none() { + log::info!("Poller: First run, setting last processed block to latest block"); self.last_processes_block = Some(latest_block); } // TODO remove this line diff --git a/src/main.rs b/src/main.rs index bfe64d4..0025acf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -124,7 +124,10 @@ impl CustomWallet for Cli {} #[tokio::main] async fn main() -> Result<()> { // Setting default log level to info - env_logger::Builder::from_env(Env::default().default_filter_or("info,challenger=debug")).init(); + env_logger::Builder::from_env( + Env::default().default_filter_or("info,challenger=debug,scribe=trace"), + ) + .init(); let args = Cli::parse(); From d9d66dea0481c2a9356f901b1d3481e7f90d1b68 Mon Sep 17 00:00:00 2001 From: Konstantin Zolotarev Date: Tue, 19 Nov 2024 20:39:03 +0200 Subject: [PATCH 30/40] Anvil installation on github actions --- .github/workflows/tests.yml | 86 +++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 46e68c1..06a406f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,51 +1,53 @@ name: test on: - push: - branches: - - main - - master - pull_request: + push: + branches: + - main + - master + pull_request: env: - CARGO_TERM_COLOR: always + CARGO_TERM_COLOR: always jobs: - test: - name: tests - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - with: - cache-on-failure: true - - run: cargo test --workspace --all-targets --all-features - env: - RUSTFLAGS: -Dwarnings + test: + name: tests + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + - run: curl -L https://foundry.paradigm.xyz | bash + - run: foundryup + - run: cargo test --workspace --all-targets --all-features + env: + RUSTFLAGS: -Dwarnings - clippy: - name: clippy - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@clippy - - uses: Swatinem/rust-cache@v2 - with: - cache-on-failure: true - - run: cargo clippy --workspace --all-targets --all-features - env: - RUSTFLAGS: -Dwarnings + clippy: + name: clippy + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@clippy + - uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + - run: cargo clippy --workspace --all-targets --all-features + env: + RUSTFLAGS: -Dwarnings - fmt: - name: fmt - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@stable - with: - components: rustfmt - - run: cargo fmt --all --check \ No newline at end of file + fmt: + name: fmt + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + - run: cargo fmt --all --check From 2653e22e14159ff07940afefde3fc25f1a9e19b5 Mon Sep 17 00:00:00 2001 From: Konstantin Zolotarev Date: Tue, 19 Nov 2024 20:40:00 +0200 Subject: [PATCH 31/40] Anvil installation on github actions --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 06a406f..de6b1e3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,7 +22,7 @@ jobs: with: cache-on-failure: true - run: curl -L https://foundry.paradigm.xyz | bash - - run: foundryup + - run: source /home/runner/.bashrc && foundryup - run: cargo test --workspace --all-targets --all-features env: RUSTFLAGS: -Dwarnings From 2f3ac47c06c5be93c99dc1a7293f41529979b99e Mon Sep 17 00:00:00 2001 From: Konstantin Zolotarev Date: Tue, 19 Nov 2024 20:42:48 +0200 Subject: [PATCH 32/40] Anvil installation on github actions --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index de6b1e3..d96a753 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,6 +22,7 @@ jobs: with: cache-on-failure: true - run: curl -L https://foundry.paradigm.xyz | bash + - run: cat /home/runner/.bashrc - run: source /home/runner/.bashrc && foundryup - run: cargo test --workspace --all-targets --all-features env: From 20c9984ce0fdc6589c18ba54780a44826e820680 Mon Sep 17 00:00:00 2001 From: Konstantin Zolotarev Date: Tue, 19 Nov 2024 20:47:06 +0200 Subject: [PATCH 33/40] Anvil installation on github actions --- .github/workflows/tests.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d96a753..8a0962b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,8 +22,9 @@ jobs: with: cache-on-failure: true - run: curl -L https://foundry.paradigm.xyz | bash - - run: cat /home/runner/.bashrc - - run: source /home/runner/.bashrc && foundryup + - run: cat $HOME/.bashrc + - run: source $HOME/.bashrc + - run: foundryup - run: cargo test --workspace --all-targets --all-features env: RUSTFLAGS: -Dwarnings From c22b27abbc0ef7a01afb2a9b3cc8629f1d988300 Mon Sep 17 00:00:00 2001 From: Konstantin Zolotarev Date: Tue, 19 Nov 2024 20:48:19 +0200 Subject: [PATCH 34/40] Anvil installation on github actions --- .github/workflows/tests.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8a0962b..9c54f2e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,9 +22,7 @@ jobs: with: cache-on-failure: true - run: curl -L https://foundry.paradigm.xyz | bash - - run: cat $HOME/.bashrc - - run: source $HOME/.bashrc - - run: foundryup + - run: /home/runner/.config/.foundry/bin/foundryup - run: cargo test --workspace --all-targets --all-features env: RUSTFLAGS: -Dwarnings From 397bfa093840e364990ac99a3f22acca59423fc2 Mon Sep 17 00:00:00 2001 From: Konstantin Zolotarev Date: Tue, 19 Nov 2024 20:50:29 +0200 Subject: [PATCH 35/40] Anvil installation on github actions --- .github/workflows/tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9c54f2e..d77fd00 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,6 +23,8 @@ jobs: cache-on-failure: true - run: curl -L https://foundry.paradigm.xyz | bash - run: /home/runner/.config/.foundry/bin/foundryup + - run: ls -l /home/runner/.config/.foundry/bin + - run: anvil --help - run: cargo test --workspace --all-targets --all-features env: RUSTFLAGS: -Dwarnings From c500902d87c35475a8d76515f3cdc2e6d313b005 Mon Sep 17 00:00:00 2001 From: Konstantin Zolotarev Date: Tue, 19 Nov 2024 20:52:25 +0200 Subject: [PATCH 36/40] Anvil installation on github actions --- .github/workflows/tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d77fd00..fac4fe6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -24,6 +24,8 @@ jobs: - run: curl -L https://foundry.paradigm.xyz | bash - run: /home/runner/.config/.foundry/bin/foundryup - run: ls -l /home/runner/.config/.foundry/bin + # - run: /home/runner/.config/.foundry/bin/anvil --help + - run: export PATH="/home/runner/.config/.foundry/bin:$PATH" - run: anvil --help - run: cargo test --workspace --all-targets --all-features env: From 82bf95591b1617131f84fc56802aff292b404cec Mon Sep 17 00:00:00 2001 From: Konstantin Zolotarev Date: Tue, 19 Nov 2024 20:59:30 +0200 Subject: [PATCH 37/40] Anvil installation on github actions --- .github/workflows/tests.yml | 4 ++-- src/main.rs | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fac4fe6..0f53867 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -25,10 +25,10 @@ jobs: - run: /home/runner/.config/.foundry/bin/foundryup - run: ls -l /home/runner/.config/.foundry/bin # - run: /home/runner/.config/.foundry/bin/anvil --help - - run: export PATH="/home/runner/.config/.foundry/bin:$PATH" - - run: anvil --help + - run: /home/runner/.config/.foundry/bin/anvil --help - run: cargo test --workspace --all-targets --all-features env: + ANVIL_BIN: /home/runner/.config/.foundry/bin/anvil RUSTFLAGS: -Dwarnings clippy: diff --git a/src/main.rs b/src/main.rs index 0025acf..408bc0d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -747,11 +747,18 @@ mod integration_tests { // -- Helper functions -- + // Search for anvil binary in the target directory if `ANVIL_BIN` is set. + // Otherwise it will try get `anvil` from system. + fn get_anvil() -> Anvil { + let anvil_bin = std::env::var("ANVIL_BIN").unwrap_or_else(|_| "anvil".to_string()); + Anvil::at(anvil_bin) + } + async fn create_anvil_instances( private_key: &str, port: u16, ) -> (AnvilInstance, AnvilProvider, EthereumWallet) { - let anvil: AnvilInstance = Anvil::new() + let anvil: AnvilInstance = get_anvil() .port(port) .chain_id(31337) .block_time_f64(0.1) From 9e311dbd37969ad33d7ac5e5b5aeb449696c502b Mon Sep 17 00:00:00 2001 From: Konstantin Zolotarev Date: Sat, 23 Nov 2024 20:46:56 +0200 Subject: [PATCH 38/40] Fixed prometheus metrics + updated challenge logic to validate challenge only once --- crates/scribe/src/events_handler.rs | 155 +++++++++++++++------------ crates/scribe/src/events_listener.rs | 6 +- crates/scribe/src/metrics.rs | 5 +- 3 files changed, 92 insertions(+), 74 deletions(-) diff --git a/crates/scribe/src/events_handler.rs b/crates/scribe/src/events_handler.rs index 4714af6..db4ad89 100644 --- a/crates/scribe/src/events_handler.rs +++ b/crates/scribe/src/events_handler.rs @@ -17,7 +17,7 @@ use std::collections::HashMap; use std::sync::Arc; use std::time::Duration; -use alloy::primitives::Address; +use alloy::primitives::{Address, FixedBytes}; use alloy::providers::Provider; use alloy::rpc::types::BlockTransactionsKind; use eyre::{bail, Context, Result}; @@ -100,7 +100,6 @@ impl EventDistributor { break; } event = self.rx.recv() => { - log::trace!("EventDistributor: Received event: {:?}", event); if let Some(event) = event { // Send the event to the appropriate contract handler match self.txs.get(&event.address) { @@ -181,8 +180,6 @@ impl ScribeEventsProcessor { } // new [EventWithMetadata] received, process it... event = self.rx.recv() => { - log::trace!("ScribeEventsProcessor[{:?}] Received event: {:?}", self.address, event); - if let Some(event) = event { match &event.event { // For `OpPoked` events, check if `schnorr_signature` is valid, @@ -371,8 +368,6 @@ impl OpPokedValidator { "OpPokedValidator[{:?}] OpPoked validation started", self.address ); - // Perform the challenge after 200ms - let mut challenge_attempts: u64 = 0; loop { tokio::select! { // Check if the challenge has been cancelled @@ -397,83 +392,103 @@ impl OpPokedValidator { break; } - const RETRY_RANGE_END: u64 = CLASSIC_CHALLENGE_RETRY_COUNT+FLASHBOT_CHALLENGE_RETRY_COUNT; - match challenge_attempts { - 0..FLASHBOT_CHALLENGE_RETRY_COUNT => { - log::debug!("OpPokedValidator[{:?}] Attempting flashbot challenge", self.address); - let contract = ScribeOptimisticProviderInstance::new( - self.address, self.flashbot_provider.clone() - ); + self.challenge().await?; + break; + } + } + } + Ok(()) + } - let result = contract.challenge(self.op_poked.schnorrData.clone()).await; + // Perform the challenge process, first with flashbot provider, then with normal provider. + async fn challenge(&self) -> Result> { + // Perform the challenge after 200ms + let mut challenge_attempts: u64 = 0; + const RETRY_RANGE_END: u64 = CLASSIC_CHALLENGE_RETRY_COUNT + FLASHBOT_CHALLENGE_RETRY_COUNT; - match result { - Ok(tx_hash) => { - log::info!( - "OpPokedValidator[{:?}] Flashbot transaction sent: {:?}", - self.address, - tx_hash - ); + log::info!( + "OpPokedValidator[{:?}] Challending data: {:?}", + self.address, + self.op_poked + ); - // Increment the challenge counter - metrics::inc_challenge_counter( - self.address, - tx_hash, - true, - ); - break; - } - Err(e) => { - log::error!( - "OpPokedValidator[{:?}] Failed to send flashbot transaction: {:?}", - self.address, - e - ); - } - } + loop { + match challenge_attempts { + 0..FLASHBOT_CHALLENGE_RETRY_COUNT => { + log::debug!( + "OpPokedValidator[{:?}] Attempting flashbot challenge", + self.address + ); + let contract = ScribeOptimisticProviderInstance::new( + self.address, + self.flashbot_provider.clone(), + ); + + let result = contract.challenge(self.op_poked.schnorrData.clone()).await; + + match result { + Ok(tx_hash) => { + log::info!( + "OpPokedValidator[{:?}] Flashbot transaction sent: {:?}", + self.address, + tx_hash + ); + + // Increment the challenge counter + metrics::inc_challenge_counter(self.address, true); + return Ok(tx_hash); } - FLASHBOT_CHALLENGE_RETRY_COUNT..RETRY_RANGE_END => { - log::debug!("OpPokedValidator[{:?}] Attempting public challenge", self.address); - let contract = ScribeOptimisticProviderInstance::new( - self.address, self.provider.clone() + Err(e) => { + log::error!( + "OpPokedValidator[{:?}] Failed to send flashbot transaction: {:?}", + self.address, + e ); - match contract.challenge(self.op_poked.schnorrData.clone()).await { - Ok(tx_hash) => { - log::info!( - "OpPokedValidator[{:?}] Challenge transaction sent: {:?}", - self.address, - tx_hash - ); - // Increment the challenge counter - metrics::inc_challenge_counter( - self.address, - tx_hash, - false, - ); - break; - } - Err(e) => { - log::error!( - "OpPokedValidator[{:?}] Failed to send challenge transaction: {:?}", - self.address, - e - ); - } - } } - _ => { + } + } + FLASHBOT_CHALLENGE_RETRY_COUNT..RETRY_RANGE_END => { + log::debug!( + "OpPokedValidator[{:?}] Attempting public challenge", + self.address + ); + let contract = + ScribeOptimisticProviderInstance::new(self.address, self.provider.clone()); + match contract.challenge(self.op_poked.schnorrData.clone()).await { + Ok(tx_hash) => { + log::info!( + "OpPokedValidator[{:?}] Challenge transaction sent: {:?}", + self.address, + tx_hash + ); + // Increment the challenge counter + metrics::inc_challenge_counter(self.address, false); + return Ok(tx_hash); + } + Err(e) => { log::error!( - "OpPokedValidator[{:?}] Challenge failed, total attempts {:?}", + "OpPokedValidator[{:?}] Failed to send challenge transaction: {:?}", self.address, - challenge_attempts + e ); - break; } } - challenge_attempts += 1; + } + _ => { + log::error!( + "OpPokedValidator[{:?}] Challenge failed, total attempts {:?}", + self.address, + challenge_attempts + ); + break; } } + challenge_attempts += 1; } - Ok(()) + + bail!(format!( + "OpPokedValidator[{:?}] Challenge failed, total attempts {:?}", + self.address, challenge_attempts + )) } } diff --git a/crates/scribe/src/events_listener.rs b/crates/scribe/src/events_listener.rs index 6c0b738..f7d72a5 100644 --- a/crates/scribe/src/events_listener.rs +++ b/crates/scribe/src/events_listener.rs @@ -166,7 +166,11 @@ impl Poller { for log in logs { match EventWithMetadata::from_log(log) { Ok(event) => { - log::debug!("Poller: [{:?}] Event received: {:?}", chunk, &event); + log::debug!( + "Poller: [{:?}] Event received for address {:?} processing", + chunk, + &event.address + ); // Send event to the channel self.tx.send(event).await?; } diff --git a/crates/scribe/src/metrics.rs b/crates/scribe/src/metrics.rs index 29182d8..f2a7ce9 100644 --- a/crates/scribe/src/metrics.rs +++ b/crates/scribe/src/metrics.rs @@ -13,7 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -use alloy::primitives::{Address, B256}; +use alloy::primitives::Address; use metrics::{counter, describe_counter, describe_gauge, gauge}; const LAST_SCANNED_BLOCK_GAUGE: &str = "challenger_last_scanned_block"; @@ -37,11 +37,10 @@ pub fn inc_errors_counter(address: Address, error: &str) { } /// `inc_challenge_counter` increments the challenges counter for given `address` and `tx` hash. -pub fn inc_challenge_counter(address: Address, tx: B256, flashbots: bool) { +pub fn inc_challenge_counter(address: Address, flashbots: bool) { let labels = [ ("address", format!("{:?}", address)), ("flashbots", format!("{:?}", flashbots)), - ("tx", format!("{:?}", tx)), ]; counter!(CHALLENGE_COUNTER, &labels).increment(1); } From 42194f3bd0352838776ed9d99678a4ef5c613505 Mon Sep 17 00:00:00 2001 From: Konstantin Zolotarev Date: Sat, 23 Nov 2024 21:11:09 +0200 Subject: [PATCH 39/40] Fixed clippy & logs --- crates/scribe/src/events_handler.rs | 58 ++++++++++++++--------------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/crates/scribe/src/events_handler.rs b/crates/scribe/src/events_handler.rs index db4ad89..51fdd5e 100644 --- a/crates/scribe/src/events_handler.rs +++ b/crates/scribe/src/events_handler.rs @@ -368,36 +368,34 @@ impl OpPokedValidator { "OpPokedValidator[{:?}] OpPoked validation started", self.address ); - loop { - tokio::select! { - // Check if the challenge has been cancelled - _ = self.cancel.cancelled() => { - log::debug!("OpPokedValidator[{:?}] Challenge cancelled", self.address); - break; - } - // Check if the global cancel command sent - _ = self.global_cancel.cancelled() => { - log::debug!("OpPokedValidator[{:?}] Global cancel command received", self.address); - break; + tokio::select! { + // Check if the challenge has been cancelled + _ = self.cancel.cancelled() => { + log::debug!("OpPokedValidator[{:?}] Challenge cancelled", self.address); + Ok(()) + } + // Check if the global cancel command sent + _ = self.global_cancel.cancelled() => { + log::debug!("OpPokedValidator[{:?}] Global cancel command received", self.address); + Ok(()) + } + _ = tokio::time::sleep(Duration::from_millis(CHALLENGE_POKE_DELAY_MS)) => { + // Verify that the OpPoked is valid + let is_valid = ScribeOptimisticProviderInstance::new( + self.address, + self.provider.clone() + ).is_schnorr_signature_valid(self.op_poked.clone()).await?; + + if is_valid { + log::debug!("OpPokedValidator[{:?}] OpPoked is valid, no need to challenge", self.address); + return Ok(()); } - _ = tokio::time::sleep(Duration::from_millis(CHALLENGE_POKE_DELAY_MS)) => { - // Verify that the OpPoked is valid - let is_valid = ScribeOptimisticProviderInstance::new( - self.address, - self.provider.clone() - ).is_schnorr_signature_valid(self.op_poked.clone()).await?; - - if is_valid { - log::debug!("OpPokedValidator[{:?}] OpPoked is valid, no need to challenge", self.address); - break; - } - self.challenge().await?; - break; - } + // Perform the challenge process + self.challenge().await?; + Ok(()) } } - Ok(()) } // Perform the challenge process, first with flashbot provider, then with normal provider. @@ -429,7 +427,7 @@ impl OpPokedValidator { match result { Ok(tx_hash) => { log::info!( - "OpPokedValidator[{:?}] Flashbot transaction sent: {:?}", + "OpPokedValidator[{:?}] Flashbot transaction sent via flashbots RPC: {:?}", self.address, tx_hash ); @@ -440,7 +438,7 @@ impl OpPokedValidator { } Err(e) => { log::error!( - "OpPokedValidator[{:?}] Failed to send flashbot transaction: {:?}", + "OpPokedValidator[{:?}] Failed to send challenge transaction via flashbots: {:?}", self.address, e ); @@ -457,7 +455,7 @@ impl OpPokedValidator { match contract.challenge(self.op_poked.schnorrData.clone()).await { Ok(tx_hash) => { log::info!( - "OpPokedValidator[{:?}] Challenge transaction sent: {:?}", + "OpPokedValidator[{:?}] Challenge transaction sent via public RPC: {:?}", self.address, tx_hash ); @@ -467,7 +465,7 @@ impl OpPokedValidator { } Err(e) => { log::error!( - "OpPokedValidator[{:?}] Failed to send challenge transaction: {:?}", + "OpPokedValidator[{:?}] Failed to send challenge transaction via public RPC: {:?}", self.address, e ); From 147f04ebba24395db2fc8e25100ea5243ca1290a Mon Sep 17 00:00:00 2001 From: Konstantin Zolotarev Date: Sat, 23 Nov 2024 21:37:19 +0200 Subject: [PATCH 40/40] Fixed typos in project --- .github/workflows/tests.yml | 11 + .gitignore | 3 +- Cargo.lock | 850 +++++++++++++++++----------- crates/scribe/src/events_handler.rs | 4 +- src/main.rs | 42 +- typos.toml | 5 + 6 files changed, 566 insertions(+), 349 deletions(-) create mode 100644 typos.toml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0f53867..6968b1f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -55,3 +55,14 @@ jobs: with: components: rustfmt - run: cargo fmt --all --check + + typos-check: + name: TyposCheck + timeout-minutes: 5 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: crate-ci/typos@v1.22.7 + with: + config: ./typos.toml + isolated: true diff --git a/.gitignore b/.gitignore index 057cc7d..3677c68 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /target .DS_Store .vscode -.idea \ No newline at end of file +.idea +LOCAL.md diff --git a/Cargo.lock b/Cargo.lock index a6adde1..2544352 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] @@ -51,9 +51,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" [[package]] name = "alloy" @@ -83,10 +83,11 @@ dependencies = [ [[package]] name = "alloy-chains" -version = "0.1.32" +version = "0.1.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf770dad29577cd3580f3dd09005799224a912b8cdfdd6dc04d030d42b3df4e" +checksum = "18c5c520273946ecf715c0010b4e3503d7eba9893cd9ce6b7fff5654c4a3c470" dependencies = [ + "alloy-primitives", "num_enum", "strum", ] @@ -164,21 +165,22 @@ dependencies = [ [[package]] name = "alloy-core" -version = "0.8.3" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b095eb0533144b4497e84a9cc3e44a5c2e3754a3983c0376a55a2f9183a53e" +checksum = "9c8316d83e590f4163b221b8180008f302bda5cf5451202855cdd323e588849c" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", "alloy-primitives", + "alloy-rlp", "alloy-sol-types", ] [[package]] name = "alloy-dyn-abi" -version = "0.8.7" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f95d76a38cae906fd394a5afb0736aaceee5432efe76addfd71048e623e208af" +checksum = "ef2364c782a245cf8725ea6dbfca5f530162702b5d685992ea03ce64529136cc" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -263,9 +265,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.8.7" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c66eec1acdd96b39b995b8f5ee5239bc0c871d62c527ae1ac9fd1d7fecd455" +checksum = "b84c506bf264110fa7e90d9924f742f40ef53c6572ea56a0b0bd714a567ed389" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -387,9 +389,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.7" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb848c43f6b06ae3de2e4a67496cbbabd78ae87db0f1248934f15d76192c6a" +checksum = "9fce5dbd6a4f118eecc4719eaa9c7ffc31c315e6c5ccde3642db927802312425" dependencies = [ "alloy-rlp", "bytes", @@ -397,7 +399,7 @@ dependencies = [ "const-hex", "derive_more", "foldhash", - "hashbrown 0.15.0", + "hashbrown 0.15.1", "hex-literal", "indexmap", "itoa", @@ -499,15 +501,15 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower 0.5.1", + "tower", "tracing", ] [[package]] name = "alloy-rlp" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26154390b1d205a4a7ac7352aa2eb4f81f391399d4e2f546fb81a2f8bb383f62" +checksum = "da0822426598f95e45dd1ea32a738dac057529a709ee645fcc516ffa4cbde08f" dependencies = [ "alloy-rlp-derive", "arrayvec", @@ -516,13 +518,13 @@ dependencies = [ [[package]] name = "alloy-rlp-derive" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d0f2d905ebd295e7effec65e5f6868d153936130ae718352771de3e7d03c75c" +checksum = "2b09cae092c27b6f1bde952653a22708691802e57bfef4a2973b80bea21efd3f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -545,7 +547,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower 0.5.1", + "tower", "tracing", "url", ] @@ -566,7 +568,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower 0.5.1", + "tower", "tracing", ] @@ -716,23 +718,23 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.8.7" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "661c516eb1fa3294cc7f2fb8955b3b609d639c282ac81a4eedb14d3046db503a" +checksum = "9343289b4a7461ed8bab8618504c995c049c082b70c7332efd7b32125633dc05" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] name = "alloy-sol-macro-expander" -version = "0.8.7" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecbabb8fc3d75a0c2cea5215be22e7a267e3efde835b0f2a8922f5e3f5d47683" +checksum = "4222d70bec485ceccc5d8fd4f2909edd65b5d5e43d4aca0b5dcee65d519ae98f" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -742,16 +744,16 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", "syn-solidity", "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "0.8.7" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16517f2af03064485150d89746b8ffdcdbc9b6eeb3d536fb66efd7c2846fbc75" +checksum = "2e17f2677369571b976e51ea1430eb41c3690d344fef567b840bfc0b01b6f83a" dependencies = [ "alloy-json-abi", "const-hex", @@ -760,15 +762,15 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.77", + "syn 2.0.89", "syn-solidity", ] [[package]] name = "alloy-sol-type-parser" -version = "0.8.7" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c07ebb0c1674ff8cbb08378d7c2e0e27919d2a2dae07ad3bca26174deda8d389" +checksum = "aa64d80ae58ffaafdff9d5d84f58d03775f66c84433916dc9a64ed16af5755da" dependencies = [ "serde", "winnow", @@ -776,9 +778,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "0.8.7" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e448d879903624863f608c552d10efb0e0905ddbee98b0049412799911eb062" +checksum = "6520d427d4a8eb7aa803d852d7a52ceb0c519e784c292f64bb339e636918cf27" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -801,7 +803,7 @@ dependencies = [ "serde_json", "thiserror", "tokio", - "tower 0.5.1", + "tower", "tracing", "url", ] @@ -820,7 +822,7 @@ dependencies = [ "serde_json", "thiserror", "tokio", - "tower 0.5.1", + "tower", "tracing", "url", ] @@ -835,7 +837,7 @@ dependencies = [ "alloy-transport 0.3.6", "reqwest", "serde_json", - "tower 0.5.1", + "tower", "tracing", "url", ] @@ -904,9 +906,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -919,36 +921,36 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1083,9 +1085,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "async-stream" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ "async-stream-impl", "futures-core", @@ -1094,24 +1096,24 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] name = "async-trait" -version = "0.1.82" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -1139,14 +1141,14 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" @@ -1195,22 +1197,20 @@ checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" [[package]] name = "bindgen" -version = "0.69.4" +version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" dependencies = [ "bitflags", "cexpr", "clang-sys", - "itertools 0.12.1", - "lazy_static", - "lazycell", + "itertools 0.13.0", "proc-macro2", "quote", "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -1287,9 +1287,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" dependencies = [ "serde", ] @@ -1311,9 +1311,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.21" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" +checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" dependencies = [ "shlex", ] @@ -1354,7 +1354,7 @@ dependencies = [ "testing_logger", "tokio", "tokio-util", - "tower 0.5.1", + "tower", "warp", ] @@ -1395,9 +1395,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.17" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" +checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" dependencies = [ "clap_builder", "clap_derive", @@ -1405,9 +1405,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.17" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" +checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" dependencies = [ "anstream", "anstyle", @@ -1417,33 +1417,33 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "const-hex" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8a24a26d37e1ffd45343323dc9fe6654ceea44c12f2fcb3d7ac29e610bc6" +checksum = "487981fa1af147182687064d0a2c336586d337a606595ced9ffb0c685c250c73" dependencies = [ "cfg-if", "cpufeatures", @@ -1476,9 +1476,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] @@ -1593,7 +1593,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", "unicode-xid", ] @@ -1618,6 +1618,17 @@ dependencies = [ "subtle", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "doctest-file" version = "1.0.0" @@ -1671,9 +1682,9 @@ dependencies = [ [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] @@ -1751,9 +1762,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" [[package]] name = "fastrlp" @@ -1832,9 +1843,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -1863,9 +1874,9 @@ checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -1886,7 +1897,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -1949,9 +1960,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.31.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" @@ -1991,9 +2002,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" dependencies = [ "atomic-waker", "bytes", @@ -2021,10 +2032,12 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" dependencies = [ + "allocator-api2", + "equivalent", "foldhash", "serde", ] @@ -2147,9 +2160,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -2165,9 +2178,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.30" +version = "0.14.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" dependencies = [ "bytes", "futures-channel", @@ -2189,14 +2202,14 @@ dependencies = [ [[package]] name = "hyper" -version = "1.4.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.6", + "h2 0.4.7", "http 1.1.0", "http-body 1.0.1", "httparse", @@ -2216,7 +2229,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.4.1", + "hyper 1.5.1", "hyper-util", "native-tls", "tokio", @@ -2226,20 +2239,19 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.8" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-channel", "futures-util", "http 1.1.0", "http-body 1.0.1", - "hyper 1.4.1", + "hyper 1.5.1", "pin-project-lite", "socket2", "tokio", - "tower 0.4.13", "tower-service", "tracing", ] @@ -2267,14 +2279,143 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "icu_normalizer", + "icu_properties", ] [[package]] @@ -2288,13 +2429,13 @@ dependencies = [ [[package]] name = "impl-trait-for-tuples" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.89", ] [[package]] @@ -2305,12 +2446,12 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.1", "serde", ] @@ -2340,9 +2481,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.10.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" [[package]] name = "is_terminal_polyfill" @@ -2359,15 +2500,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.13.0" @@ -2379,24 +2511,24 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "540654e97a3f4470a492cd30ff187bc95d89557a903a2bbf112e2fae98104ef2" [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" dependencies = [ "wasm-bindgen", ] [[package]] name = "k256" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if", "ecdsa", @@ -2430,17 +2562,11 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "libc" -version = "0.2.158" +version = "0.2.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" [[package]] name = "libloading" @@ -2454,15 +2580,15 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "libproc" -version = "0.14.8" +version = "0.14.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae9ea4b75e1a81675429dafe43441df1caea70081e82246a8cccf514884a88bb" +checksum = "e78a09b56be5adbcad5aa1197371688dc6bb249a26da3bca2011ee2fb987ebfb" dependencies = [ "bindgen", "errno", @@ -2475,6 +2601,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + [[package]] name = "lock_api" version = "0.4.12" @@ -2493,11 +2625,11 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lru" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.15.1", ] [[package]] @@ -2533,7 +2665,7 @@ checksum = "5d58e362dc7206e9456ddbcdbd53c71ba441020e62104703075a69151e38d85f" dependencies = [ "base64 0.22.1", "http-body-util", - "hyper 1.4.1", + "hyper 1.5.1", "hyper-tls", "hyper-util", "indexmap", @@ -2720,29 +2852,29 @@ checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] name = "object" -version = "0.36.4" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "openssl" -version = "0.10.66" +version = "0.10.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" dependencies = [ "bitflags", "cfg-if", @@ -2761,7 +2893,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -2772,9 +2904,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.103" +version = "0.9.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" dependencies = [ "cc", "libc", @@ -2784,28 +2916,29 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.6.12" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" +checksum = "8be4817d39f3272f69c59fe05d0535ae6456c2dc2fa1ba02910296c7e0a5c590" dependencies = [ "arrayvec", "bitvec", "byte-slice-cast", "impl-trait-for-tuples", "parity-scale-codec-derive", + "rustversion", "serde", ] [[package]] name = "parity-scale-codec-derive" -version = "3.6.12" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" +checksum = "8781a75c6205af67215f382092b6e0a4ff3734798523e69073d4bcd294ec767b" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.89", ] [[package]] @@ -2854,9 +2987,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.13" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdbef9d1d47087a895abd220ed25eb4ad973a5e26f6a4367b038c25e28dfc2d9" +checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" dependencies = [ "memchr", "thiserror", @@ -2875,29 +3008,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -2917,15 +3050,15 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "portable-atomic" -version = "1.7.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" [[package]] name = "ppv-lite86" @@ -2975,14 +3108,14 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -3108,9 +3241,9 @@ dependencies = [ [[package]] name = "raw-cpuid" -version = "11.1.0" +version = "11.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb9ee317cfe3fbd54b36a511efc1edd42e216903c9cd575e686dd68a2ba90d8d" +checksum = "1ab240315c661615f2ee9f0f2cd32d5a7343a84d5ebcccb99d46e6637565e7b0" dependencies = [ "bitflags", ] @@ -3123,18 +3256,18 @@ checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" [[package]] name = "redox_syscall" -version = "0.5.4" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.10.6" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -3144,9 +3277,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -3155,15 +3288,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.7" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" dependencies = [ "base64 0.22.1", "bytes", @@ -3172,7 +3305,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.1", "http-body-util", - "hyper 1.4.1", + "hyper 1.5.1", "hyper-tls", "hyper-util", "ipnet", @@ -3187,7 +3320,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 1.0.1", + "sync_wrapper 1.0.2", "tokio", "tokio-native-tls", "tower-service", @@ -3337,9 +3470,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" dependencies = [ "bitflags", "errno", @@ -3350,9 +3483,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.13" +version = "0.23.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" +checksum = "9c9cc1d47e243d655ace55ed38201c19ae02c148ae56412ab8750e8f0166ab7f" dependencies = [ "once_cell", "ring", @@ -3364,19 +3497,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64 0.22.1", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" [[package]] name = "rustls-webpki" @@ -3391,9 +3523,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "rusty-fork" @@ -3424,9 +3556,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.24" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ "windows-sys 0.59.0", ] @@ -3455,7 +3587,7 @@ dependencies = [ "once_cell", "tokio", "tokio-util", - "tower 0.5.1", + "tower", ] [[package]] @@ -3499,9 +3631,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.1" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" dependencies = [ "core-foundation-sys", "libc", @@ -3524,9 +3656,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "semver-parser" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" dependencies = [ "pest", ] @@ -3539,29 +3671,29 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -3695,6 +3827,12 @@ dependencies = [ "der", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -3726,7 +3864,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -3748,9 +3886,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" dependencies = [ "proc-macro2", "quote", @@ -3759,14 +3897,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.8.7" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20e7b52ad118b2153644eea95c6fc740b6c1555b2344fdab763fc9de4075f665" +checksum = "f76fe0a3e1476bdaa0775b9aec5b869ed9520c2b2fedfe9c6df3618f8ea6290b" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -3777,13 +3915,24 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "sync_wrapper" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ "futures-core", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "tap" version = "1.0.1" @@ -3792,9 +3941,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.12.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", "fastrand", @@ -3814,22 +3963,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -3851,25 +4000,20 @@ dependencies = [ ] [[package]] -name = "tinyvec" -version = "1.8.0" +name = "tinystr" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ - "tinyvec_macros", + "displaydoc", + "zerovec", ] -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" -version = "1.40.0" +version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" dependencies = [ "backtrace", "bytes", @@ -3891,7 +4035,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -3976,30 +4120,15 @@ checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.22.21" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "toml_datetime", "winnow", ] -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "pin-project", - "pin-project-lite", - "tokio", - "tower-layer", - "tower-service", -] - [[package]] name = "tower" version = "0.5.1" @@ -4047,7 +4176,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -4112,9 +4241,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "uint" @@ -4136,33 +4265,15 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.15" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" [[package]] name = "unicode-ident" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" - -[[package]] -name = "unicode-normalization" -version = "0.1.24" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" -dependencies = [ - "tinyvec", -] +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-xid" @@ -4178,9 +4289,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", @@ -4193,6 +4304,18 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -4256,7 +4379,7 @@ dependencies = [ "futures-util", "headers", "http 0.2.12", - "hyper 0.14.30", + "hyper 0.14.31", "log", "mime", "mime_guess", @@ -4282,9 +4405,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", "once_cell", @@ -4293,24 +4416,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.43" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" dependencies = [ "cfg-if", "js-sys", @@ -4320,9 +4443,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4330,28 +4453,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "web-sys" -version = "0.3.70" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" dependencies = [ "js-sys", "wasm-bindgen", @@ -4359,9 +4482,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.6" +version = "0.26.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" dependencies = [ "rustls-pki-types", ] @@ -4612,13 +4735,25 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "ws_stream_wasm" version = "0.7.4" @@ -4647,6 +4782,30 @@ dependencies = [ "tap", ] +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -4665,7 +4824,28 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", + "synstructure", ] [[package]] @@ -4685,5 +4865,27 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", ] diff --git a/crates/scribe/src/events_handler.rs b/crates/scribe/src/events_handler.rs index 51fdd5e..6908548 100644 --- a/crates/scribe/src/events_handler.rs +++ b/crates/scribe/src/events_handler.rs @@ -300,7 +300,7 @@ impl ScribeEventsProcessor { let challenge_handler = Some(Arc::new(Mutex::new(OpPokedValidator::new( op_poked, self.cancellation_token.clone(), - // cancel_challenge garunteed to be Some + // cancel_challenge guaranteed to be Some self.cancel_challenge.as_ref().unwrap().clone(), self.address, self.provider.clone(), @@ -331,7 +331,7 @@ impl ScribeEventsProcessor { } // Handle the challenge process for a specific OpPoked event after a delay -// If cancelled before end of delay or inbewteen retries stop process +// If cancelled before end of delay or inbetween retries stop process // First try challenge with flashbot provider, then with normal provider struct OpPokedValidator { op_poked: OpPoked, diff --git a/src/main.rs b/src/main.rs index 408bc0d..1275e59 100644 --- a/src/main.rs +++ b/src/main.rs @@ -139,7 +139,7 @@ async fn main() -> Result<()> { "Using {:?} for signing transactions.", signer.default_signer().address() ); - let nonce_mananger = NonceFiller::::default(); + let nonce_manager = NonceFiller::::default(); // Create new HTTP client with retry backoff layer let client = ClientBuilder::default() @@ -152,7 +152,7 @@ async fn main() -> Result<()> { .with_recommended_fillers() // Add chain id request from rpc .filler(ChainIdFiller::new(args.chain_id)) - .filler(nonce_mananger.clone()) + .filler(nonce_manager.clone()) // Add default signer .wallet(signer.clone()) .on_client(client), @@ -170,7 +170,7 @@ async fn main() -> Result<()> { .with_recommended_fillers() // Add chain id request from rpc .filler(ChainIdFiller::new(args.chain_id)) - .filler(nonce_mananger.clone()) + .filler(nonce_manager.clone()) // Add default signer .wallet(signer.clone()) .on_client(flashbot_client), @@ -429,12 +429,12 @@ mod integration_tests { use scribe::events_handler; use scribe::events_listener::Poller; use scribe_optimistic::{ - IScribe, LibSecp256k1, ScribeOptimisitic, ScribeOptimisitic::ScribeOptimisiticInstance, + IScribe, LibSecp256k1, ScribeOptimistic, ScribeOptimistic::ScribeOptimisticInstance, }; use tokio::task::JoinSet; use tokio_util::sync::CancellationToken; - // In a seperate module due to problems with autoformatter + // In a separate module due to problems with autoformatter #[rustfmt::skip] mod scribe_optimistic { use alloy::sol; @@ -442,7 +442,7 @@ mod integration_tests { #[allow(missing_docs)] #[sol(rpc)] #[derive(Debug)] - ScribeOptimisitic, + ScribeOptimistic, "tests/fixtures/bytecode/ScribeOptimistic.json" ); } @@ -477,7 +477,7 @@ mod integration_tests { #[tokio::test] async fn challenge_contract() { - // Test an invalid poke on multiple scribe instances in parrallel are successfully challenged + // Test an invalid poke on multiple scribe instances in parallel are successfully challenged const NUM_SCRIBE_INSTANCES: usize = 100; const CHALLENGE_PERIOD: u16 = 1000; @@ -499,7 +499,7 @@ mod integration_tests { signer.clone(), CHALLENGE_PERIOD, )); - // Only deploy at most 20 in parrallel + // Only deploy at most 20 in parallel if i % 20 == 0 { scribes.append(&mut join_all(deployments).await); deployments = vec![]; @@ -521,7 +521,7 @@ mod integration_tests { } // Update current anvil time to be far from last scribe config update - // The more scribe instances the firther back in time a the anvil block timestamp doesn't stay in sync with chrono + // The more scribe instances the further back in time a the anvil block timestamp doesn't stay in sync with chrono // TODO add challenge period variable let current_timestamp = (chrono::Utc::now().timestamp() as u64) - (CHALLENGE_PERIOD as u64) + 1; @@ -555,7 +555,7 @@ mod integration_tests { // Let first poll occur on poller tokio::time::sleep(tokio::time::Duration::from_millis(1200)).await; - // Increase current block count to move away from poller intialisation block + // Increase current block count to move away from poller initialisation block anvil_provider .anvil_mine(Some(U256::from(1)), Some(U256::from(1))) .await @@ -637,7 +637,7 @@ mod integration_tests { // Test an invalid poke on multiple scribe instances is successfully challenged // ------------------------------------------------------------------------------------------------------------ let private_key = PRIVATE_KEY; - // Use a new port for each test to avoid conflicts if tests run in parrallel + // Use a new port for each test to avoid conflicts if tests run in parallel let (anvil, anvil_provider, signer) = create_anvil_instances(private_key, 8546).await; // Set to a low current time for now, this avoids having stale poke error later @@ -688,7 +688,7 @@ mod integration_tests { // Let first poll occur on poller tokio::time::sleep(tokio::time::Duration::from_millis(1200)).await; - // Increase current block count to move away from poller intialisation block + // Increase current block count to move away from poller initialisation block anvil_provider .anvil_mine(Some(U256::from(1)), Some(U256::from(1))) .await @@ -808,7 +808,7 @@ mod integration_tests { provider: P, signer: EthereumWallet, challenge_period: u16, - ) -> ScribeOptimisiticInstance { + ) -> ScribeOptimisticInstance { // deploy scribe instance let initial_authed = signer.default_signer().address(); // let private_key = signer. @@ -818,8 +818,7 @@ mod integration_tests { 0123456789abcdef0123456789abcdef " ); - let scribe_optimistic = - ScribeOptimisitic::deploy(provider, initial_authed, FixedBytes(wat)); + let scribe_optimistic = ScribeOptimistic::deploy(provider, initial_authed, FixedBytes(wat)); let scribe_optimistic = scribe_optimistic.await.unwrap(); let receipt = scribe_optimistic.setBar(1); @@ -883,13 +882,13 @@ mod integration_tests { .layer(RetryBackoffLayer::new(15, 200, 300)) .http(url); - let nonce_mananger = NonceFiller::::default(); + let nonce_manager = NonceFiller::::default(); let provider = Arc::new( ProviderBuilder::new() .with_recommended_fillers() .filler(ChainIdFiller::new(Some(31337))) - .filler(nonce_mananger.clone()) + .filler(nonce_manager.clone()) .wallet(signer.clone()) .on_client(client), ); @@ -899,7 +898,7 @@ mod integration_tests { let mut set: JoinSet<()> = JoinSet::new(); let (tx, rx) = tokio::sync::mpsc::channel::(100); - // let addresses = vec![scribe_optimisitic.address().clone()]; + // let addresses = vec![scribe_optimistic.address().clone()]; // Create events listener let mut poller = Poller::new( @@ -969,7 +968,7 @@ mod integration_tests { async fn make_invalid_op_poke( age: u64, private_key: &str, - scribe_optimisitic: &ScribeOptimisiticInstance, AnvilProvider, Ethereum>, + scribe_optimistic: &ScribeOptimisticInstance, AnvilProvider, Ethereum>, ) { let poke_data: IScribe::PokeData = IScribe::PokeData { val: 10, @@ -982,7 +981,7 @@ mod integration_tests { commitment: alloy::primitives::Address::ZERO, feedIds: hex!("00").into(), }; - let op_poke_message = scribe_optimisitic + let op_poke_message = scribe_optimistic .constructOpPokeMessage(poke_data.clone(), schnorr_data.clone()) .call() .await @@ -1001,8 +1000,7 @@ mod integration_tests { }; // Make the invalid poke - let receipt = - scribe_optimisitic.opPoke(poke_data.clone(), schnorr_data.clone(), ecdsa_data); + let receipt = scribe_optimistic.opPoke(poke_data.clone(), schnorr_data.clone(), ecdsa_data); let receipt = receipt.send().await.expect("Failed to send op poke"); receipt.watch().await.expect("Failed to watch op poke"); } diff --git a/typos.toml b/typos.toml new file mode 100644 index 0000000..3a73864 --- /dev/null +++ b/typos.toml @@ -0,0 +1,5 @@ +[default] +extend-ignore-identifiers-re = ["Datas"] + +[files] +extend-exclude = ["CHANGELOG.md", "tests"]