From fe5c40577e69999648e7fbc02087c6cafa96dfa0 Mon Sep 17 00:00:00 2001 From: nhtyy Date: Tue, 18 Feb 2025 13:28:16 -0800 Subject: [PATCH 01/18] feat: multi-chain operator --- Cargo.lock | 602 ++++++++++++++++---- Cargo.toml | 4 +- primitives/Cargo.toml | 4 +- primitives/src/header_range.rs | 4 +- primitives/src/justification.rs | 2 +- primitives/src/lib.rs | 22 +- primitives/src/merkle.rs | 2 +- primitives/src/rotate.rs | 4 +- primitives/src/types.rs | 4 +- program/Cargo.toml | 2 +- script/Cargo.toml | 8 +- script/bin/costs.rs | 25 +- script/bin/operator.rs | 950 ++++++++++++++++++++++---------- script/src/relay.rs | 15 +- services/Cargo.toml | 2 +- services/bin/indexer.rs | 18 +- services/src/input.rs | 6 +- 17 files changed, 1212 insertions(+), 462 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9f154b6..5eb0d18 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -78,7 +78,7 @@ dependencies = [ "getrandom 0.2.15", "once_cell", "version_check", - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -98,23 +98,23 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy" -version = "0.8.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7e1758e5d759c0114140152ae72032eafcfdd7b599e995ebbc8eeafa2b4c977" +checksum = "6d2cc5aeb8dfa1e451a49fac87bc4b86c5de40ebea153ed88e83eb92b8151e74" dependencies = [ - "alloy-consensus", + "alloy-consensus 0.11.1", "alloy-contract", "alloy-core", - "alloy-eips", + "alloy-eips 0.11.1", "alloy-genesis", - "alloy-network", + "alloy-network 0.11.1", "alloy-provider", "alloy-pubsub", "alloy-rpc-client", "alloy-rpc-types", - "alloy-serde", - "alloy-signer", - "alloy-signer-local", + "alloy-serde 0.11.1", + "alloy-signer 0.11.1", + "alloy-signer-local 0.11.1", "alloy-transport", "alloy-transport-http", "alloy-transport-ipc", @@ -137,10 +137,27 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a205d0cbb7bfdf9f4fd4b0ec842bc4c5f926e8c14ec3072d3fd75dd363baf1e0" dependencies = [ - "alloy-eips", + "alloy-eips 0.8.1", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 0.8.1", + "alloy-trie", + "auto_impl", + "c-kzg", + "derive_more 1.0.0", + "serde", +] + +[[package]] +name = "alloy-consensus" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e32ef5c74bbeb1733c37f4ac7f866f8c8af208b7b4265e21af609dcac5bd5e" +dependencies = [ + "alloy-eips 0.11.1", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.11.1", "alloy-trie", "auto_impl", "c-kzg", @@ -155,28 +172,42 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993c34090a3f281cb746fd1604520cf21f8407ffbeb006aaa34c0556bffa718e" dependencies = [ - "alloy-consensus", - "alloy-eips", + "alloy-consensus 0.8.1", + "alloy-eips 0.8.1", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 0.8.1", + "serde", +] + +[[package]] +name = "alloy-consensus-any" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa13b7b1e1e3fedc42f0728103bfa3b4d566d3d42b606db449504d88dbdbdcf" +dependencies = [ + "alloy-consensus 0.11.1", + "alloy-eips 0.11.1", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.11.1", "serde", ] [[package]] name = "alloy-contract" -version = "0.8.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aec7945dff98ba68489aa6da455bf66f6c0fee8157df06747fbae7cb03c368e2" +checksum = "ee6180fb232becdea70fad57c63b6967f01f74ab9595671b870f504116dd29de" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", - "alloy-network", - "alloy-network-primitives", + "alloy-network 0.11.1", + "alloy-network-primitives 0.11.1", "alloy-primitives", "alloy-provider", "alloy-pubsub", - "alloy-rpc-types-eth", + "alloy-rpc-types-eth 0.11.1", "alloy-sol-types", "alloy-transport", "futures", @@ -214,6 +245,18 @@ dependencies = [ "winnow 0.6.18", ] +[[package]] +name = "alloy-eip2124" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "675264c957689f0fd75f5993a73123c2cc3b5c235a38f5b9037fe6c826bfb2c0" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "crc", + "thiserror 2.0.7", +] + [[package]] name = "alloy-eip2930" version = "0.1.0" @@ -230,6 +273,18 @@ name = "alloy-eip7702" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c986539255fb839d1533c128e190e557e52ff652c9ef62939e233a81dd93f7e" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "derive_more 1.0.0", + "serde", +] + +[[package]] +name = "alloy-eip7702" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cabf647eb4650c91a9d38cb6f972bb320009e7e9d61765fb688a86f1563b33e8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -245,10 +300,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1d9907c29ce622946759bf4fd3418166bfeae76c1c544b8081c7be3acd9b4be" dependencies = [ "alloy-eip2930", - "alloy-eip7702", + "alloy-eip7702 0.4.2", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.8.1", + "c-kzg", + "derive_more 1.0.0", + "once_cell", + "serde", + "sha2 0.10.8", +] + +[[package]] +name = "alloy-eips" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5591581ca2ab0b3e7226a4047f9a1bfcf431da1d0cce3752fda609fea3c27e37" +dependencies = [ + "alloy-eip2124", + "alloy-eip2930", + "alloy-eip7702 0.5.0", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 0.11.1", + "auto_impl", "c-kzg", "derive_more 1.0.0", "once_cell", @@ -258,12 +333,13 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "0.8.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f13f7405a8eb8021258994ed1beab490c3e509ebbe2c18e1c24ae10749d56b" +checksum = "0cded3a2d4bd7173f696458c5d4c98c18a628dfcc9f194385e80a486e412e2e0" dependencies = [ + "alloy-eips 0.11.1", "alloy-primitives", - "alloy-serde", + "alloy-serde 0.11.1", "alloy-trie", "serde", ] @@ -294,22 +370,61 @@ dependencies = [ "tracing", ] +[[package]] +name = "alloy-json-rpc" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "762414662d793d7aaa36ee3af6928b6be23227df1681ce9c039f6f11daadef64" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "serde", + "serde_json", + "thiserror 2.0.7", + "tracing", +] + [[package]] name = "alloy-network" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99051f82f77159d5bee06108f33cffee02849e2861fc500bf74213aa2ae8a26e" dependencies = [ - "alloy-consensus", - "alloy-consensus-any", - "alloy-eips", - "alloy-json-rpc", - "alloy-network-primitives", + "alloy-consensus 0.8.1", + "alloy-consensus-any 0.8.1", + "alloy-eips 0.8.1", + "alloy-json-rpc 0.8.1", + "alloy-network-primitives 0.8.1", "alloy-primitives", - "alloy-rpc-types-any", - "alloy-rpc-types-eth", - "alloy-serde", - "alloy-signer", + "alloy-rpc-types-any 0.8.1", + "alloy-rpc-types-eth 0.8.1", + "alloy-serde 0.8.1", + "alloy-signer 0.8.1", + "alloy-sol-types", + "async-trait", + "auto_impl", + "futures-utils-wasm", + "serde", + "serde_json", + "thiserror 2.0.7", +] + +[[package]] +name = "alloy-network" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be03f2ebc00cf88bd06d3c6caf387dceaa9c7e6b268216779fa68a9bf8ab4e6" +dependencies = [ + "alloy-consensus 0.11.1", + "alloy-consensus-any 0.11.1", + "alloy-eips 0.11.1", + "alloy-json-rpc 0.11.1", + "alloy-network-primitives 0.11.1", + "alloy-primitives", + "alloy-rpc-types-any 0.11.1", + "alloy-rpc-types-eth 0.11.1", + "alloy-serde 0.11.1", + "alloy-signer 0.11.1", "alloy-sol-types", "async-trait", "auto_impl", @@ -325,10 +440,23 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2aff127863f8279921397be8af0ac3f05a8757d5c4c972b491c278518fa07c7" dependencies = [ - "alloy-consensus", - "alloy-eips", + "alloy-consensus 0.8.1", + "alloy-eips 0.8.1", + "alloy-primitives", + "alloy-serde 0.8.1", + "serde", +] + +[[package]] +name = "alloy-network-primitives" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a00ce618ae2f78369918be0c20f620336381502c83b6ed62c2f7b2db27698b0" +dependencies = [ + "alloy-consensus 0.11.1", + "alloy-eips 0.11.1", "alloy-primitives", - "alloy-serde", + "alloy-serde 0.11.1", "serde", ] @@ -362,20 +490,25 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "0.8.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0280a4f68e0cefde9449ee989a248230efbe3f95255299d2a7a92009e154629d" +checksum = "cbe0a2acff0c4bd1669c71251ce10fc455cbffa1b4d0a817d5ea4ba7e5bb3db7" dependencies = [ "alloy-chains", - "alloy-consensus", - "alloy-eips", - "alloy-json-rpc", - "alloy-network", - "alloy-network-primitives", + "alloy-consensus 0.11.1", + "alloy-eips 0.11.1", + "alloy-json-rpc 0.11.1", + "alloy-network 0.11.1", + "alloy-network-primitives 0.11.1", "alloy-primitives", "alloy-pubsub", "alloy-rpc-client", - "alloy-rpc-types-eth", + "alloy-rpc-types-anvil", + "alloy-rpc-types-debug", + "alloy-rpc-types-eth 0.11.1", + "alloy-rpc-types-trace", + "alloy-rpc-types-txpool", + "alloy-sol-types", "alloy-transport", "alloy-transport-http", "alloy-transport-ipc", @@ -386,11 +519,10 @@ dependencies = [ "dashmap", "futures", "futures-utils-wasm", - "lru", + "lru 0.13.0", "parking_lot", "pin-project", "reqwest 0.12.5", - "schnellru", "serde", "serde_json", "thiserror 2.0.7", @@ -402,11 +534,11 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "0.8.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9475dc1a835bd8bb77275b6bccf8e177e7e669ba81277ce6bea0016ce994fafe" +checksum = "de3a68996f193f542f9e29c88dfa8ed1369d6ee04fa764c1bf23dc11b2f9e4a2" dependencies = [ - "alloy-json-rpc", + "alloy-json-rpc 0.11.1", "alloy-primitives", "alloy-transport", "bimap", @@ -443,11 +575,11 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "0.8.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fc8b0f68619cfab3a2e15dca7b80ab266f78430bb4353dec546528e04b7449" +checksum = "b37cc3c7883dc41be1b01460127ad7930466d0a4bb6ba15a02ee34d2745e2d7c" dependencies = [ - "alloy-json-rpc", + "alloy-json-rpc 0.11.1", "alloy-primitives", "alloy-pubsub", "alloy-transport", @@ -469,14 +601,29 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "0.8.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "986f23fe42ac95832901a24b93c20f7ed2b9644394c02b86222801230da60041" +checksum = "6f18e68a3882f372e045ddc89eb455469347767d17878ca492cfbac81e71a111" dependencies = [ "alloy-primitives", + "alloy-rpc-types-anvil", "alloy-rpc-types-engine", - "alloy-rpc-types-eth", - "alloy-serde", + "alloy-rpc-types-eth 0.11.1", + "alloy-rpc-types-trace", + "alloy-rpc-types-txpool", + "alloy-serde 0.11.1", + "serde", +] + +[[package]] +name = "alloy-rpc-types-anvil" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d06300df4a87d960add35909240fc72da355dd2ac926fa6999f9efafbdc5a7" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth 0.11.1", + "alloy-serde 0.11.1", "serde", ] @@ -486,22 +633,43 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57e3aa433d3657b42e98e257ee6fa201f5c853245648a33da8fbb7497a5008bf" dependencies = [ - "alloy-consensus-any", - "alloy-rpc-types-eth", - "alloy-serde", + "alloy-consensus-any 0.8.1", + "alloy-rpc-types-eth 0.8.1", + "alloy-serde 0.8.1", +] + +[[package]] +name = "alloy-rpc-types-any" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "318ae46dd12456df42527c3b94c1ae9001e1ceb707f7afe2c7807ac4e49ebad9" +dependencies = [ + "alloy-consensus-any 0.11.1", + "alloy-rpc-types-eth 0.11.1", + "alloy-serde 0.11.1", +] + +[[package]] +name = "alloy-rpc-types-debug" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2834b7012054cb2f90ee9893b7cc97702edca340ec1ef386c30c42e55e6cd691" +dependencies = [ + "alloy-primitives", + "serde", ] [[package]] name = "alloy-rpc-types-engine" -version = "0.8.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30814f8b9ac10219fb77fe42c277a0ffa1c369fbc3961f14d159f51fb221966e" +checksum = "e83dde9fcf1ccb9b815cc0c89bba26bbbbaae5150a53ae624ed0fc63cb3676c1" dependencies = [ - "alloy-consensus", - "alloy-eips", + "alloy-consensus 0.11.1", + "alloy-eips 0.11.1", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.11.1", "derive_more 1.0.0", "serde", "strum", @@ -513,13 +681,13 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0643cc497a71941f526454fe4fecb47e9307d3a7b6c05f70718a0341643bcc79" dependencies = [ - "alloy-consensus", - "alloy-consensus-any", - "alloy-eips", - "alloy-network-primitives", + "alloy-consensus 0.8.1", + "alloy-consensus-any 0.8.1", + "alloy-eips 0.8.1", + "alloy-network-primitives 0.8.1", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.8.1", "alloy-sol-types", "derive_more 1.0.0", "itertools 0.13.0", @@ -527,6 +695,52 @@ dependencies = [ "serde_json", ] +[[package]] +name = "alloy-rpc-types-eth" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b4dbee4d82f8a22dde18c28257bed759afeae7ba73da4a1479a039fd1445d04" +dependencies = [ + "alloy-consensus 0.11.1", + "alloy-consensus-any 0.11.1", + "alloy-eips 0.11.1", + "alloy-network-primitives 0.11.1", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 0.11.1", + "alloy-sol-types", + "itertools 0.13.0", + "serde", + "serde_json", + "thiserror 2.0.7", +] + +[[package]] +name = "alloy-rpc-types-trace" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd951155515fa452a2ca4b5434d4b3ab742bcd3d1d1b9a91704bcef5b8d2604" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth 0.11.1", + "alloy-serde 0.11.1", + "serde", + "serde_json", + "thiserror 2.0.7", +] + +[[package]] +name = "alloy-rpc-types-txpool" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21d8dd5bd94993eda3d56a8c4c0d693548183a35462523ffc4385c0b020d3b0c" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth 0.11.1", + "alloy-serde 0.11.1", + "serde", +] + [[package]] name = "alloy-serde" version = "0.8.1" @@ -538,6 +752,17 @@ dependencies = [ "serde_json", ] +[[package]] +name = "alloy-serde" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8732058f5ca28c1d53d241e8504620b997ef670315d7c8afab856b3e3b80d945" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", +] + [[package]] name = "alloy-signer" version = "0.8.1" @@ -552,16 +777,47 @@ dependencies = [ "thiserror 2.0.7", ] +[[package]] +name = "alloy-signer" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f96b3526fdd779a4bd0f37319cfb4172db52a7ac24cdbb8804b72091c18e1701" +dependencies = [ + "alloy-primitives", + "async-trait", + "auto_impl", + "either", + "elliptic-curve", + "k256", + "thiserror 2.0.7", +] + [[package]] name = "alloy-signer-local" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f08ec1bfa433f9e9f7c5af05af07e5cf86d27d93170de76b760e63b925f1c9c" dependencies = [ - "alloy-consensus", - "alloy-network", + "alloy-consensus 0.8.1", + "alloy-network 0.8.1", "alloy-primitives", - "alloy-signer", + "alloy-signer 0.8.1", + "async-trait", + "k256", + "rand 0.8.5", + "thiserror 2.0.7", +] + +[[package]] +name = "alloy-signer-local" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe8f78cd6b7501c7e813a1eb4a087b72d23af51f5bb66d4e948dc840bdd207d8" +dependencies = [ + "alloy-consensus 0.11.1", + "alloy-network 0.11.1", + "alloy-primitives", + "alloy-signer 0.11.1", "async-trait", "k256", "rand 0.8.5", @@ -643,13 +899,12 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "0.8.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf656f983e14812df65b5aee37e7b37535f68a848295e6ed736b2054a405cb7" +checksum = "5a8d762eadce3e9b65eac09879430c6f4fce3736cac3cac123f9b1bf435ddd13" dependencies = [ - "alloy-json-rpc", + "alloy-json-rpc 0.11.1", "base64 0.22.1", - "futures-util", "futures-utils-wasm", "serde", "serde_json", @@ -663,11 +918,11 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "0.8.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec938d51a47b7953b1c0fd8ddeb89a29eb113cd4908dfc4e01c7893b252d669f" +checksum = "20819c4cb978fb39ce6ac31991ba90f386d595f922f42ef888b4a18be190713e" dependencies = [ - "alloy-json-rpc", + "alloy-json-rpc 0.11.1", "alloy-transport", "reqwest 0.12.5", "serde_json", @@ -678,17 +933,18 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "0.8.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9df0d2e1b24dd029641bd21ef783491c42af87b162968be94f0443c1eb72c8e0" +checksum = "5e88304aa8b796204e5e2500dfe235933ed692745e3effd94c3733643db6d218" dependencies = [ - "alloy-json-rpc", + "alloy-json-rpc 0.11.1", "alloy-pubsub", "alloy-transport", "bytes", "futures", "interprocess", "pin-project", + "serde", "serde_json", "tokio", "tokio-util", @@ -697,9 +953,9 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "0.8.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fabdf2d18c0c87b6cfcf6a067f1d5a7db378f103faeb16130d6d174c73d006b" +checksum = "b9653ea9aa06d0e02fcbe2f04f1c47f35a85c378ccefa98e54ae85210bc8bbfa" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -2122,8 +2378,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" dependencies = [ "bitcoin_hashes", - "rand 0.7.3", - "rand_core 0.5.1", + "rand 0.8.5", + "rand_core 0.6.4", "serde", "unicode-normalization", ] @@ -2333,9 +2589,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" dependencies = [ "serde", ] @@ -2722,6 +2978,21 @@ dependencies = [ "serde", ] +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crc32fast" version = "1.4.2" @@ -4131,6 +4402,18 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets 0.52.6", +] + [[package]] name = "getrandom_or_panic" version = "0.0.3" @@ -4357,6 +4640,8 @@ version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ + "allocator-api2", + "equivalent", "foldhash", "serde", ] @@ -5391,6 +5676,15 @@ dependencies = [ "hashbrown 0.14.5", ] +[[package]] +name = "lru" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465" +dependencies = [ + "hashbrown 0.15.2", +] + [[package]] name = "mach" version = "0.3.2" @@ -6513,7 +6807,7 @@ version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -6802,6 +7096,17 @@ dependencies = [ "serde", ] +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.1", + "zerocopy 0.8.18", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -6822,6 +7127,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.1", +] + [[package]] name = "rand_core" version = "0.5.1" @@ -6840,6 +7155,16 @@ dependencies = [ "getrandom 0.2.15", ] +[[package]] +name = "rand_core" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a88e0da7a2c97baa202165137c158d0a2e824ac465d13d81046727b34cb247d3" +dependencies = [ + "getrandom 0.3.1", + "zerocopy 0.8.18", +] + [[package]] name = "rand_hc" version = "0.2.0" @@ -7857,7 +8182,7 @@ dependencies = [ name = "services" version = "0.1.0" dependencies = [ - "alloy-primitives", + "alloy", "anyhow", "avail-subxt 0.5.0 (git+https://github.com/availproject/avail.git?tag=v2.2.5.1)", "aws-config", @@ -8127,7 +8452,7 @@ dependencies = [ "hex", "itertools 0.12.1", "log", - "lru", + "lru 0.12.4", "no-std-net", "parking_lot", "pin-project", @@ -9016,7 +9341,7 @@ dependencies = [ "parity-scale-codec", "tracing", "tracing-core", - "tracing-subscriber 0.3.18", + "tracing-subscriber 0.3.19", ] [[package]] @@ -9293,7 +9618,7 @@ dependencies = [ "thiserror 1.0.65", "tracing", "tracing-forest", - "tracing-subscriber 0.3.18", + "tracing-subscriber 0.3.19", "typenum", "web-time", ] @@ -9390,7 +9715,7 @@ dependencies = [ "eyre", "hex", "itertools 0.13.0", - "lru", + "lru 0.12.4", "num-bigint 0.4.6", "p3-baby-bear", "p3-bn254-fr", @@ -9416,7 +9741,7 @@ dependencies = [ "thiserror 1.0.65", "tracing", "tracing-appender", - "tracing-subscriber 0.3.18", + "tracing-subscriber 0.3.19", ] [[package]] @@ -9561,8 +9886,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6e2dc9d893b032f84d3177fb53e7e248ea9043624de5d4ff473c5521ba2b966" dependencies = [ "alloy-primitives", - "alloy-signer", - "alloy-signer-local", + "alloy-signer 0.8.1", + "alloy-signer-local 0.8.1", "alloy-sol-types", "anyhow", "async-trait", @@ -9641,8 +9966,7 @@ dependencies = [ name = "sp1-vector-primitives" version = "0.1.0" dependencies = [ - "alloy-primitives", - "alloy-sol-types", + "alloy", "avail-subxt 0.5.0 (git+https://github.com/availproject/avail.git?tag=v2.2.2.0-rc1)", "blake2", "ed25519-consensus", @@ -9651,6 +9975,7 @@ dependencies = [ "primitive-types", "serde", "sha2 0.10.8", + "tokio", ] [[package]] @@ -9673,10 +9998,8 @@ dependencies = [ "clap 4.5.13", "csv", "dotenv", - "env_logger", "futures", "hex", - "log", "rand 0.8.5", "reqwest 0.11.27", "serde", @@ -9686,6 +10009,8 @@ dependencies = [ "sp1-sdk", "sp1-vector-primitives", "tokio", + "tracing", + "tracing-subscriber 0.3.19", ] [[package]] @@ -10353,9 +10678,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.24.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" +checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" dependencies = [ "futures-util", "log", @@ -10529,9 +10854,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -10548,14 +10873,14 @@ dependencies = [ "crossbeam-channel", "thiserror 1.0.65", "time", - "tracing-subscriber 0.3.18", + "tracing-subscriber 0.3.19", ] [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", @@ -10564,9 +10889,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -10582,7 +10907,7 @@ dependencies = [ "smallvec 1.13.2", "thiserror 1.0.65", "tracing", - "tracing-subscriber 0.3.18", + "tracing-subscriber 0.3.19", ] [[package]] @@ -10641,9 +10966,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers 0.1.0", "nu-ansi-term", @@ -10694,21 +11019,20 @@ checksum = "f4f195fd851901624eee5a58c4bb2b4f06399148fcd0ed336e6f1cb60a9881df" [[package]] name = "tungstenite" -version = "0.24.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" +checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" dependencies = [ - "byteorder", "bytes", "data-encoding", "http 1.1.0", "httparse", "log", - "rand 0.8.5", + "rand 0.9.0", "rustls 0.23.12", "rustls-pki-types", "sha1", - "thiserror 1.0.65", + "thiserror 2.0.7", "utf-8", ] @@ -10742,7 +11066,7 @@ checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", "digest 0.10.7", - "rand 0.7.3", + "rand 0.8.5", "static_assertions", ] @@ -10976,6 +11300,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" version = "0.2.92" @@ -11588,6 +11921,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags 2.6.0", +] + [[package]] name = "ws_stream_wasm" version = "0.7.4" @@ -11647,7 +11989,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79386d31a42a4996e3336b0919ddb90f81112af416270cff95b5f5af22b839c2" +dependencies = [ + "zerocopy-derive 0.8.18", ] [[package]] @@ -11661,6 +12012,17 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "zerocopy-derive" +version = "0.8.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76331675d372f91bf8d17e13afbd5fe639200b73d01f0fc748bb059f9cca2db7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "zeroize" version = "1.8.1" diff --git a/Cargo.toml b/Cargo.toml index 05f0290..1439923 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,9 +15,7 @@ sha2 = { version = "0.10.8", default-features = false } blake2 = "0.10.6" # Alloy -alloy-primitives = { version = "0.8.0", features = ["serde"] } -alloy = { version = "0.8.0", features = ["full"] } -alloy-sol-types = { version = "0.8.0" } +alloy = { version = "0.11.1", features = ["full"] } # Common anyhow = "1.0.68" diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 5666830..5316eba 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -8,10 +8,10 @@ serde.workspace = true sha2.workspace = true ed25519-consensus.workspace = true codec.workspace = true -alloy-primitives.workspace = true -alloy-sol-types.workspace = true +alloy.workspace = true blake2.workspace = true itertools.workspace = true +tokio.workspace = true [dev-dependencies] primitive-types = "0.12.2" diff --git a/primitives/src/header_range.rs b/primitives/src/header_range.rs index 1afd14c..8ee7937 100644 --- a/primitives/src/header_range.rs +++ b/primitives/src/header_range.rs @@ -1,5 +1,5 @@ -use alloy_primitives::B256; -use alloy_sol_types::SolType; +use alloy::primitives::B256; +use alloy::sol_types::SolType; use crate::consts::HEADER_OUTPUTS_LENGTH; use crate::merkle::get_merkle_root_commitments; diff --git a/primitives/src/justification.rs b/primitives/src/justification.rs index 9e8a6f9..3fc5e0c 100644 --- a/primitives/src/justification.rs +++ b/primitives/src/justification.rs @@ -3,7 +3,7 @@ use codec::Encode; use ed25519_consensus::{Signature, VerificationKey}; use std::collections::HashMap; -use alloy_primitives::B256; +use alloy::primitives::B256; /// Verify that a Ed25519 signature is valid. Panics if the signature is not valid. fn verify_signature(pubkey_bytes: [u8; 32], signed_message: &[u8], signature: [u8; 64]) { diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 7214b86..85f891c 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -1,4 +1,4 @@ -use alloy_primitives::B256; +use alloy::primitives::B256; use blake2::{ digest::{Update, VariableOutput}, Blake2bVar, @@ -120,3 +120,23 @@ mod tests { assert_eq!(extracted_hash, hash, "Hashes don't match") } } + +pub use timeout::Timeout; + +mod timeout { + use std::future::Future; + use std::time::Duration; + use tokio::time::{timeout, Timeout as TimeoutFuture}; + + pub trait Timeout: Sized { + fn timeout(self, duration: Duration) -> TimeoutFuture; + } + + impl Timeout for T { + fn timeout(self, duration: Duration) -> TimeoutFuture { + timeout(duration, self) + } + } +} + +mod maybe_signer {} diff --git a/primitives/src/merkle.rs b/primitives/src/merkle.rs index 52a0bda..79a5217 100644 --- a/primitives/src/merkle.rs +++ b/primitives/src/merkle.rs @@ -1,7 +1,7 @@ use sha2::{Digest, Sha256}; use crate::types::DecodedHeaderData; -use alloy_primitives::B256; +use alloy::primitives::B256; // Computes the simple Merkle root of the leaves. If the number of leaves is not a power of 2, pad // with empty 32 byte arrays till the next power of 2. diff --git a/primitives/src/rotate.rs b/primitives/src/rotate.rs index a8321f4..be67979 100644 --- a/primitives/src/rotate.rs +++ b/primitives/src/rotate.rs @@ -2,8 +2,8 @@ use crate::{ compute_authority_set_commitment, consts::ROTATE_OUTPUTS_LENGTH, decode_scale_compact_int, types::RotateInputs, types::RotateOutputs, verify_encoded_validators, verify_justification, }; -use alloy_primitives::B256; -use alloy_sol_types::SolType; +use alloy::primitives::B256; +use alloy::sol_types::SolType; /// Verify the justification from the current authority set on the epoch end header and return the new /// authority set commitment. diff --git a/primitives/src/types.rs b/primitives/src/types.rs index 3ddda4e..1396998 100644 --- a/primitives/src/types.rs +++ b/primitives/src/types.rs @@ -1,5 +1,5 @@ -use alloy_primitives::{B256, B512}; -use alloy_sol_types::sol; +use alloy::primitives::{B256, B512}; +use alloy::sol; use serde::{Deserialize, Serialize}; diff --git a/program/Cargo.toml b/program/Cargo.toml index 5098253..270f523 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -5,5 +5,5 @@ edition = "2021" [dependencies] sp1-vector-primitives.workspace = true -alloy-sol-types.workspace = true +alloy-sol-types = { version = "0.8" } sp1-zkvm.workspace = true diff --git a/script/Cargo.toml b/script/Cargo.toml index ba08151..5a06d6a 100644 --- a/script/Cargo.toml +++ b/script/Cargo.toml @@ -27,9 +27,7 @@ path = "bin/costs.rs" sp1-vector-primitives = { workspace = true } services = { workspace = true } sp1-sdk = { workspace = true } -env_logger = { workspace = true } hex = { workspace = true } -log = { workspace = true } tokio = { workspace = true } dotenv = { workspace = true } @@ -47,5 +45,9 @@ csv = "1.3.1" rand = "0.8.5" sp1-build = { workspace = true } +# Logging +tracing = "0.1.41" +tracing-subscriber = "0.3.19" + [build-dependencies] -sp1-build = { workspace = true } \ No newline at end of file +sp1-build = { workspace = true } diff --git a/script/bin/costs.rs b/script/bin/costs.rs index 71e5319..65a2dea 100644 --- a/script/bin/costs.rs +++ b/script/bin/costs.rs @@ -1,14 +1,13 @@ +use alloy::consensus::BlockHeader; use alloy::eips::BlockId; use alloy::network::primitives::HeaderResponse; use alloy::rpc::types::{BlockTransactionsKind, Filter}; use alloy::sol; use alloy::sol_types::SolEvent; use alloy::{ - consensus::BlockHeader, - network::BlockResponse, + network::{BlockResponse, Network}, primitives::{Address, B256}, - providers::{Network, Provider, ProviderBuilder}, - transports::Transport, + providers::{Provider, ProviderBuilder}, }; use anyhow::Result; use chrono::{TimeZone, Utc}; @@ -20,6 +19,8 @@ use std::collections::HashMap; use std::str::FromStr; use std::{env, fs}; +use tracing_subscriber::EnvFilter; + #[derive(Parser, Debug, Clone)] #[command(about = "Get transaction costs for an address in a given month")] pub struct CostScriptArgs { @@ -140,7 +141,7 @@ async fn get_receipts_for_chain( .map(|receipt| RelayTransaction { chain_id, tx_hash: receipt.transaction_hash, - tx_fee_wei: receipt.gas_used * receipt.effective_gas_price, + tx_fee_wei: receipt.gas_used as u128 * receipt.effective_gas_price, from: receipt.from, to: receipt.to.unwrap_or_default(), }) @@ -151,7 +152,11 @@ async fn get_receipts_for_chain( async fn main() -> Result<()> { env::set_var("RUST_LOG", "info"); dotenv::dotenv().ok(); - env_logger::init(); + tracing_subscriber::fmt::fmt() + .with_env_filter( + EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::from_env("info")), + ) + .init(); let args = CostScriptArgs::parse(); @@ -210,13 +215,9 @@ async fn main() -> Result<()> { } /// Finds the block at the provided timestamp, using the provided provider. -async fn find_block_by_timestamp( - provider: &P, - target_timestamp: u64, -) -> Result<(B256, u64)> +async fn find_block_by_timestamp(provider: &P, target_timestamp: u64) -> Result<(B256, u64)> where - P: Provider, - T: Transport + Clone, + P: Provider, N: Network, { let latest_block = provider diff --git a/script/bin/operator.rs b/script/bin/operator.rs index cb49655..10e017a 100644 --- a/script/bin/operator.rs +++ b/script/bin/operator.rs @@ -1,34 +1,56 @@ -use std::cmp::min; use std::env; +use std::sync::Arc; use std::time::Duration; +use std::{cmp::min, collections::HashMap}; +use alloy::network::{EthereumWallet, ReceiptResponse, TransactionBuilder}; +use alloy::signers::local::PrivateKeySigner; use alloy::{ - network::EthereumWallet, + network::Network, primitives::{Address, B256}, - providers::ProviderBuilder, - signers::local::PrivateKeySigner, + providers::{Provider, ProviderBuilder}, sol, }; -use reqwest::Url; +use futures::future::{join_all, try_join_all}; -use anyhow::Result; -use log::{error, info}; +use anyhow::{Context, Result}; use services::input::{HeaderRangeRequestData, RpcDataFetcher}; +use sp1_sdk::NetworkProver; use sp1_sdk::{ network::FulfillmentStrategy, HashableKey, Prover, ProverClient, SP1ProofWithPublicValues, SP1ProvingKey, SP1Stdin, SP1VerifyingKey, }; + +use tracing::{debug, error, info}; +use tracing_subscriber::EnvFilter; + use sp1_vector_primitives::types::ProofType; +use sp1_vector_primitives::Timeout; use sp1_vectorx_script::relay::{self}; use sp1_vectorx_script::SP1_VECTOR_ELF; +//////////////////////////////////////////////////////////// +// Constants +//////////////////////////////////////////////////////////// + // If the SP1 proof takes too long to respond, time out. const PROOF_TIMEOUT_SECS: u64 = 60 * 30; +// If the RPC takes too long to respond, time out. +const RPC_TIMEOUT_SECS: u64 = 60 * 2; + // Wait for 3 required confirmations with a timeout of 60 seconds. const NUM_CONFIRMATIONS: u64 = 3; + +// If the relay takes too long to respond, time out. const RELAY_TIMEOUT_SECONDS: u64 = 60; +const NUM_RELAY_RETRIES: u32 = 3; + +//////////////////////////////////////////////////////////// +// Type Definitions +//////////////////////////////////////////////////////////// + sol! { #[allow(missing_docs)] #[sol(rpc)] @@ -45,13 +67,17 @@ sol! { function commitHeaderRange(bytes calldata proof, bytes calldata publicValues) external; } } -struct VectorXOperator { - pk: SP1ProvingKey, + +type SP1VectorInstance = SP1Vector::SP1VectorInstance<(), P, N>; + +struct VectorXOperator { + pk: Arc, vk: SP1VerifyingKey, - contract_address: Address, - rpc_url: Url, - chain_id: u64, use_kms_relayer: bool, + tree_size: u32, + fetcher: RpcDataFetcher, + prover: NetworkProver, + contracts: HashMap>, } #[derive(Debug)] @@ -62,116 +88,101 @@ struct HeaderRangeContractData { next_authority_set_hash_exists: bool, } -const NUM_RELAY_RETRIES: u32 = 3; - #[derive(Debug)] struct RotateContractData { current_block: u32, next_authority_set_hash_exists: bool, } -impl VectorXOperator { - async fn new() -> Self { +//////////////////////////////////////////////////////////// +// Constructor +//////////////////////////////////////////////////////////// + +impl VectorXOperator +where + P: Provider, + N: Network, +{ + async fn new(use_kms_relayer: bool) -> Self { dotenv::dotenv().ok(); - let client = ProverClient::builder().mock().build(); - let (pk, vk) = client.setup(SP1_VECTOR_ELF); - let use_kms_relayer: bool = env::var("USE_KMS_RELAYER") - .unwrap_or("false".to_string()) - .parse() - .unwrap(); - let chain_id: u64 = env::var("CHAIN_ID") - .expect("CHAIN_ID not set") - .parse() - .unwrap(); - let rpc_url = env::var("RPC_URL") - .expect("RPC_URL not set") - .parse() - .unwrap(); - - let contract_address = env::var("CONTRACT_ADDRESS") - .expect("CONTRACT_ADDRESS not set") - .parse() - .unwrap(); + let prover = ProverClient::builder().network().build(); + let (pk, vk) = prover.setup(SP1_VECTOR_ELF); Self { - pk, + fetcher: RpcDataFetcher::new().await, + pk: Arc::new(pk), vk, - rpc_url, - chain_id, - contract_address, use_kms_relayer, + prover, + contracts: HashMap::new(), + tree_size: 0, } } - async fn request_header_range( - &self, - header_range_request: HeaderRangeRequestData, - ) -> Result { - let mut stdin: SP1Stdin = SP1Stdin::new(); - - let fetcher = RpcDataFetcher::new().await; + /// Register a new chain with the operator. + /// + /// This function will panic if the tree size doesnt match as expected, or it fails to get the chain id. + async fn with_chain(mut self, provider: P, address: Address) -> Self { + let contract = SP1VectorInstance::new(address, provider); - let proof_type = ProofType::HeaderRangeProof; - let provider = ProviderBuilder::new().on_http(self.rpc_url.clone()); - // Fetch the header range commitment tree size from the contract. - let contract = SP1Vector::new(self.contract_address, provider.clone()); - let output = contract + let tree_size = contract .headerRangeCommitmentTreeSize() .call() .await - .unwrap(); - let header_range_inputs = fetcher - .get_header_range_inputs( - header_range_request, - Some(output.headerRangeCommitmentTreeSize), - ) - .await; - - stdin.write(&proof_type); - stdin.write(&header_range_inputs); - - info!( - "Requesting header range proof from block {} to block {}.", - header_range_request.trusted_block, header_range_request.target_block - ); + .expect("Failed to get tree size") + .headerRangeCommitmentTreeSize; - // If the SP1_PROVER environment variable is set to "mock", use the mock prover. - if let Ok(prover_type) = env::var("SP1_PROVER") { - if prover_type == "mock" { - let prover_client = ProverClient::builder().mock().build(); - let proof = prover_client.prove(&self.pk, &stdin).plonk().run()?; - return Ok(proof); - } + let chain_id = contract + .provider() + .get_chain_id() + .await + .expect("Failed to get chain id"); + + // Register the first tree size. + if self.tree_size == 0 { + self.tree_size = tree_size; + } else if self.tree_size != tree_size { + panic!( + "Tree size mismatch! Expected {}, got {} for chain id {}", + self.tree_size, tree_size, chain_id + ); } - let prover_client = ProverClient::builder().network().build(); - prover_client - .prove(&self.pk, &stdin) - .strategy(FulfillmentStrategy::Reserved) - .skip_simulation(true) - .plonk() - .timeout(Duration::from_secs(PROOF_TIMEOUT_SECS)) - .run() + self.contracts.insert(chain_id, contract); + + self } +} - async fn request_rotate( +//////////////////////////////////////////////////////////// +// Block Utilities +//////////////////////////////////////////////////////////// + +impl VectorXOperator +where + P: Provider, + N: Network, +{ + async fn request_header_range( &self, - current_authority_set_id: u64, + tree_size: u32, + header_range_request: HeaderRangeRequestData, ) -> Result { - let fetcher = RpcDataFetcher::new().await; - let mut stdin: SP1Stdin = SP1Stdin::new(); - let proof_type = ProofType::RotateProof; - let rotate_input = fetcher.get_rotate_inputs(current_authority_set_id).await; + let proof_type = ProofType::HeaderRangeProof; + let header_range_inputs = self + .fetcher + .get_header_range_inputs(header_range_request, Some(tree_size)) + .await; stdin.write(&proof_type); - stdin.write(&rotate_input); + stdin.write(&header_range_inputs); info!( - "Requesting rotate proof to add authority set {}.", - current_authority_set_id + 1 + "Requesting header range proof from block {} to block {}.", + header_range_request.trusted_block, header_range_request.target_block ); // If the SP1_PROVER environment variable is set to "mock", use the mock prover. @@ -183,8 +194,7 @@ impl VectorXOperator { } } - let prover_client = ProverClient::builder().network().build(); - prover_client + self.prover .prove(&self.pk, &stdin) .strategy(FulfillmentStrategy::Reserved) .skip_simulation(true) @@ -193,45 +203,26 @@ impl VectorXOperator { .run() } - // Determine if a rotate is needed and request the proof if so. Returns Option. - async fn find_rotate(&self) -> Result> { - let rotate_contract_data = self.get_contract_data_for_rotate().await?; - - let fetcher = RpcDataFetcher::new().await; - let head = fetcher.get_head().await; - let head_block = head.number; - let head_authority_set_id = fetcher.get_authority_set_id(head_block - 1).await; - - // The current authority set id is the authority set id of the block before the current block. - let current_authority_set_id = fetcher - .get_authority_set_id(rotate_contract_data.current_block - 1) - .await; - - if current_authority_set_id < head_authority_set_id - && !rotate_contract_data.next_authority_set_hash_exists - { - return Ok(Some(current_authority_set_id)); - } - Ok(None) - } - // Ideally, post a header range update every ideal_block_interval blocks. Returns Option<(latest_block, block_to_step_to)>. async fn find_header_range( &self, + chain_id: u64, ideal_block_interval: u32, ) -> Result> { - let header_range_contract_data = self.get_contract_data_for_header_range().await?; - - let fetcher = RpcDataFetcher::new().await; + let header_range_contract_data = self.get_contract_data_for_header_range(chain_id).await?; // The current authority set id is the authority set id of the block before the current block. - let current_authority_set_id = fetcher + let current_authority_set_id = self + .fetcher .get_authority_set_id(header_range_contract_data.vectorx_latest_block - 1) .await; info!("current_authority_set_id: {}", current_authority_set_id); // Get the last justified block by the current authority set id. - let last_justified_block = fetcher.last_justified_block(current_authority_set_id).await; + let last_justified_block = self + .fetcher + .last_justified_block(current_authority_set_id) + .await; // If this is the last justified block, check for header range with next authority set. let mut request_authority_set_id = current_authority_set_id; @@ -275,11 +266,14 @@ impl VectorXOperator { } // Current block, step_range_max and whether next authority set hash exists. - async fn get_contract_data_for_header_range(&self) -> Result { - let fetcher = RpcDataFetcher::new().await; - - let provider = ProviderBuilder::new().on_http(self.rpc_url.clone()); - let contract = SP1Vector::new(self.contract_address, provider); + async fn get_contract_data_for_header_range( + &self, + chain_id: u64, + ) -> Result { + let contract = self + .contracts + .get(&chain_id) + .expect("No contract for chain id"); let vectorx_latest_block = contract.latestBlock().call().await?.latestBlock; let header_range_commitment_tree_size = contract @@ -288,10 +282,12 @@ impl VectorXOperator { .await? .headerRangeCommitmentTreeSize; - let avail_current_block = fetcher.get_head().await.number; + let avail_current_block = self.fetcher.get_head().await.number; - let vectorx_current_authority_set_id = - fetcher.get_authority_set_id(vectorx_latest_block - 1).await; + let vectorx_current_authority_set_id = self + .fetcher + .get_authority_set_id(vectorx_latest_block - 1) + .await; let next_authority_set_id = vectorx_current_authority_set_id + 1; let next_authority_set_hash = contract @@ -308,37 +304,6 @@ impl VectorXOperator { }) } - // Current block and whether next authority set hash exists. - async fn get_contract_data_for_rotate(&self) -> Result { - let provider = ProviderBuilder::new().on_http(self.rpc_url.clone()); - let contract = SP1Vector::new(self.contract_address, provider); - - // Fetch the current block from the contract - let vectorx_latest_block = contract.latestBlock().call().await?.latestBlock; - - // Fetch the current authority set id from the contract - let vectorx_latest_authority_set_id = contract - .latestAuthoritySetId() - .call() - .await? - .latestAuthoritySetId; - - // Check if the next authority set id exists in the contract - let next_authority_set_id = vectorx_latest_authority_set_id + 1; - let next_authority_set_hash = contract - .authoritySetIdToHash(next_authority_set_id) - .call() - .await? - ._0; - let next_authority_set_hash_exists = next_authority_set_hash != B256::ZERO; - - // Return the fetched data - Ok(RotateContractData { - current_block: vectorx_latest_block, - next_authority_set_hash_exists, - }) - } - // The logic for finding the block to step to is as follows: // 1. If the current epoch in the contract is not the latest epoch, step to the last justified block // of the epoch. @@ -410,107 +375,405 @@ impl VectorXOperator { Some(block_to_step_to) } +} - /// Relay a header range proof to the SP1 SP1Vector contract. - async fn relay_header_range(&self, proof: SP1ProofWithPublicValues) -> Result { - if self.use_kms_relayer { - let provider = ProviderBuilder::new().on_http(self.rpc_url.clone()); - let contract = SP1Vector::new(self.contract_address, provider); - let proof_bytes = proof.bytes().clone().into(); - let public_values = proof.public_values.to_vec().into(); - let commit_header_range = contract.commitHeaderRange(proof_bytes, public_values); - relay::relay_with_kms( - &relay::KMSRelayRequest { - chain_id: self.chain_id, - address: self.contract_address.to_checksum(None), - calldata: commit_header_range.calldata().to_string(), - platform_request: false, - }, - NUM_RELAY_RETRIES, - ) - .await - } else { - let private_key = env::var("PRIVATE_KEY").expect("PRIVATE_KEY not set"); - let signer: PrivateKeySigner = - private_key.parse().expect("Failed to parse private key"); - let wallet = EthereumWallet::from(signer); - let provider = ProviderBuilder::new() - .with_recommended_fillers() - .wallet(wallet) - .on_http(self.rpc_url.clone()); - let contract = SP1Vector::new(self.contract_address, provider); +//////////////////////////////////////////////////////////// +// Rotate Utilities +//////////////////////////////////////////////////////////// - let receipt = contract - .commitHeaderRange(proof.bytes().into(), proof.public_values.to_vec().into()) - .send() - .await? - .with_required_confirmations(NUM_CONFIRMATIONS) - .with_timeout(Some(Duration::from_secs(RELAY_TIMEOUT_SECONDS))) - .get_receipt() - .await?; +impl VectorXOperator +where + P: Provider, + N: Network, +{ + // Current block and whether next authority set hash exists. + async fn get_contract_data_for_rotate(&self, chain_id: u64) -> Result { + let contract = self + .contracts + .get(&chain_id) + .expect("No contract for chain id"); - log::debug!("Receipt: {:?}", receipt); + // Fetch the current block from the contract + let vectorx_latest_block = contract.latestBlock().call().await?.latestBlock; - // If status is false, it reverted. - if !receipt.status() { - return Err(anyhow::anyhow!("Transaction reverted!")); + // Fetch the current authority set id from the contract + let vectorx_latest_authority_set_id = contract + .latestAuthoritySetId() + .call() + .await? + .latestAuthoritySetId; + + // Check if the next authority set id exists in the contract + let next_authority_set_id = vectorx_latest_authority_set_id + 1; + let next_authority_set_hash = contract + .authoritySetIdToHash(next_authority_set_id) + .call() + .await? + ._0; + let next_authority_set_hash_exists = next_authority_set_hash != B256::ZERO; + + // Return the fetched data + Ok(RotateContractData { + current_block: vectorx_latest_block, + next_authority_set_hash_exists, + }) + } + + async fn request_rotate( + &self, + current_authority_set_id: u64, + ) -> Result { + let fetcher = RpcDataFetcher::new().await; + + let mut stdin: SP1Stdin = SP1Stdin::new(); + + let proof_type = ProofType::RotateProof; + let rotate_input = fetcher.get_rotate_inputs(current_authority_set_id).await; + + stdin.write(&proof_type); + stdin.write(&rotate_input); + + info!( + "Requesting rotate proof to add authority set {}.", + current_authority_set_id + 1 + ); + + // If the SP1_PROVER environment variable is set to "mock", use the mock prover. + if let Ok(prover_type) = env::var("SP1_PROVER") { + if prover_type == "mock" { + let prover_client = ProverClient::builder().mock().build(); + let proof = prover_client.prove(&self.pk, &stdin).plonk().run()?; + return Ok(proof); } + } + + self.prover + .prove(&self.pk, &stdin) + .strategy(FulfillmentStrategy::Reserved) + .skip_simulation(true) + .plonk() + .timeout(Duration::from_secs(PROOF_TIMEOUT_SECS)) + .run() + } + + // Determine if a rotate is needed and request the proof if so. Returns Option. + async fn find_rotate(&self, chain_id: u64) -> Result> { + let rotate_contract_data = self.get_contract_data_for_rotate(chain_id).await?; + + // Get the current block and authority set id from the Avail chain. + let fetcher = RpcDataFetcher::new().await; + let head = fetcher.get_head().await; + let head_block = head.number; + let head_authority_set_id = fetcher.get_authority_set_id(head_block - 1).await; + + // The current authority set id is the authority set id of the block before the current block. + let current_authority_set_id = fetcher + .get_authority_set_id(rotate_contract_data.current_block - 1) + .await; - Ok(receipt.transaction_hash) + if current_authority_set_id < head_authority_set_id + && !rotate_contract_data.next_authority_set_hash_exists + { + return Ok(Some(current_authority_set_id)); } + Ok(None) } +} + +//////////////////////////////////////////////////////////// +// Control Flow & SP1 +//////////////////////////////////////////////////////////// + +impl VectorXOperator +where + P: Provider, + N: Network, +{ + /// Create and relay a header range proof for each chain. + /// + /// If any step of this function fails, it will return a generic error indicating a failure. + async fn handle_header_range(&self) -> Result<()> { + let block_interval = get_block_update_interval(); + + // NOTE: Fails fast if any of the futures fail. + let header_range_datas = + try_join_all(self.contracts.keys().copied().map(|id| async move { + Result::<_, anyhow::Error>::Ok(( + id, + self.find_header_range(id, block_interval).await?, + )) + })) + .timeout(Duration::from_secs(RPC_TIMEOUT_SECS)) + .await??; - /// Relay a rotate proof to the SP1 SP1Vector contract. - async fn relay_rotate(&self, proof: SP1ProofWithPublicValues) -> Result { + // Batch the chains with the same header range request data. + let mut header_range_data_to_chain_id: HashMap<_, Vec> = HashMap::new(); + header_range_datas + .into_iter() + .filter(|(_, header_range_data)| header_range_data.is_some()) + .for_each(|(id, header_range_data)| { + header_range_data_to_chain_id + .entry(header_range_data.unwrap()) + .or_default() + .push(id); + }); + + debug!( + "header_range_data_to_chain_id: {:?}", + header_range_data_to_chain_id + ); + + // Create a single proof for all the chain with the same header range request data, then relay to each chain. + let results = join_all(header_range_data_to_chain_id.into_iter().map( + |(header_range_data, chain_ids)| async move { + let proof = self + .request_header_range(self.tree_size, header_range_data) + .await?; + + info!( + "Created header range proof for chain {:?} of {:?}", + chain_ids, header_range_data + ); + + // All contract instances will produce the same calldata. + // And the `chain_ids` vector should have non-zero length. + let contract = self + .contracts + .get(&chain_ids[0]) + .expect("No contract for chain id"); + + let tx = contract + .commitHeaderRange(proof.bytes().into(), proof.public_values.to_vec().into()) + .into_transaction_request(); + + // Relay the transaction to all chains. + let tx_hash_futs: Vec<_> = chain_ids + .into_iter() + .map(|chain_id| { + // `send_transaction` takes ownership of the transaction. + let tx = tx.clone(); + + async move { + Result::<_, anyhow::Error>::Ok(( + chain_id, + self.relay_tx(chain_id, tx) + .timeout(Duration::from_secs(RELAY_TIMEOUT_SECONDS)) + .await + .context(format!( + "Relaying proof for chain {chain_id} failed" + ))??, + )) + } + }) + .collect(); + + Result::<_, anyhow::Error>::Ok(join_all(tx_hash_futs).await) + }, + )) + .await; + + // Check if any of the futures failed. + // There are two cases where a future can fail here: + // - Creating the rotate proof failed. + // - Relaying the transaction failed. + // + // In either case we want to log it and indicate the failure to the caller. + let mut has_errors = false; + for batch_result in results { + if let Err(e) = batch_result { + has_errors = true; + error!("Error creating rotate proof: {:?}", e); + } else { + for relay_result in batch_result.unwrap() { + if let Ok((chain_id, tx_hash)) = relay_result { + info!( + "Posted next authority set on chain {}\nTransaction hash: {}", + chain_id, tx_hash + ); + } else { + has_errors = true; + error!( + "Error relaying rotate proof! {:?}", + relay_result.unwrap_err() + ); + } + } + } + } + + if has_errors { + return Err(anyhow::anyhow!("Error during `handle_header_range`!")); + } + + Ok(()) + } + + /// Create and relay proof for each chain of an authority set rotation. + /// + /// If any step of this function fails, it will return a generic error indicating a failure. + async fn handle_rotate(&self) -> Result<()> { + let next_authority_set_ids = self.contracts.keys().copied().map(|id| async move { + Result::<_, anyhow::Error>::Ok((id, self.find_rotate(id).await?)) + }); + + // NOTE: Fails fast if any of the futures fail. + let next_authority_set_ids = try_join_all(next_authority_set_ids) + .timeout(Duration::from_secs(RPC_TIMEOUT_SECS)) + .await??; + + // "Batch" the chains by the next authority set id. + let mut next_authority_set_to_chain_ids_map: HashMap> = + HashMap::with_capacity(next_authority_set_ids.len()); + + // Populate the map with the next authority set ids. + next_authority_set_ids + .into_iter() + .filter(|(_, next_authority_set_id)| next_authority_set_id.is_some()) + .for_each(|(chain_id, next_authority_set_id)| { + next_authority_set_to_chain_ids_map + .entry(next_authority_set_id.unwrap()) + .or_default() + .push(chain_id); + }); + + debug!( + "next_authority_set_to_chain_ids_map: {:?}", + next_authority_set_to_chain_ids_map + ); + + // Create and relay a proof for each back to all the chains concurrently. + let results = join_all(next_authority_set_to_chain_ids_map.into_iter().map( + |(next_auth_id, chain_ids)| async move { + let proof = self.request_rotate(next_auth_id).await.context(format!( + "Failed to request rotate proof for chains {:?}", + chain_ids + ))?; + + info!( + "Created rotate proof for authority set {} on chains {:?}", + next_auth_id, chain_ids + ); + + // All contract instances will produce the same calldata. + // We should have at least one chain id in the vector. + let contract = self + .contracts + .get(&chain_ids[0]) + .expect("No contract for chain id"); + + let tx = contract + .rotate(proof.bytes().into(), proof.public_values.to_vec().into()) + .into_transaction_request(); + + // Relay the transaction to all chains. + let tx_hash_futs: Vec<_> = chain_ids + .into_iter() + .map(|chain_id| { + // `send_transaction` takes ownership of the transaction. + let tx = tx.clone(); + + async move { + Result::<_, anyhow::Error>::Ok(( + chain_id, + self.relay_tx(chain_id, tx) + .timeout(Duration::from_secs(RELAY_TIMEOUT_SECONDS)) + .await + .context(format!( + "Relaying proof for chain {chain_id} failed" + ))??, + )) + } + }) + .collect(); + + Result::<_, anyhow::Error>::Ok(join_all(tx_hash_futs).await) + }, + )) + .await; + + // Check if any of the futures failed. + // There are two cases where a future can fail here: + // - Creating the rotate proof failed. + // - Relaying the transaction failed. + // + // In either case we want to log it and indicate the failure to the caller. + let mut has_errors = false; + for batch_result in results { + if let Err(e) = batch_result { + has_errors = true; + error!("Error creating rotate proof: {:?}", e); + } else { + for relay_result in batch_result.unwrap() { + if let Ok((chain_id, tx_hash)) = relay_result { + info!( + "Posted next authority set on chain {}\nTransaction hash: {}", + chain_id, tx_hash + ); + } else { + has_errors = true; + error!( + "Error relaying rotate proof! {:?}", + relay_result.unwrap_err() + ); + } + } + } + } + + if has_errors { + Err(anyhow::anyhow!("Error during `handle_rotate`!")) + } else { + Ok(()) + } + } + + /// Relay a transaction to a chain. + /// + /// NOTE: Assumes the provider has a wallet. + async fn relay_tx(&self, chain_id: u64, tx: N::TransactionRequest) -> Result { if self.use_kms_relayer { - let provider = ProviderBuilder::new().on_http(self.rpc_url.clone()); - let contract = SP1Vector::new(self.contract_address, provider); - let proof_bytes = proof.bytes().clone().into(); - let public_values = proof.public_values.to_vec().into(); - let rotate = contract.rotate(proof_bytes, public_values); relay::relay_with_kms( &relay::KMSRelayRequest { - chain_id: self.chain_id, - address: self.contract_address.to_checksum(None), - calldata: rotate.calldata().to_string(), + chain_id, + address: tx.to().expect("Transaction has no to address").to_string(), + calldata: tx.input().expect("Transaction has no input").to_string(), platform_request: false, }, NUM_RELAY_RETRIES, ) .await } else { - let private_key = env::var("PRIVATE_KEY").expect("PRIVATE_KEY not set"); - let signer: PrivateKeySigner = - private_key.parse().expect("Failed to parse private key"); - let wallet = EthereumWallet::from(signer); - let provider = ProviderBuilder::new() - .with_recommended_fillers() - .wallet(wallet) - .on_http(self.rpc_url.clone()); - let contract = SP1Vector::new(self.contract_address, provider); + let contract = self + .contracts + .get(&chain_id) + .expect("No contract for chain id"); + let receipt = contract - .rotate(proof.bytes().into(), proof.public_values.to_vec().into()) - .send() + .provider() + .send_transaction(tx) .await? .with_required_confirmations(NUM_CONFIRMATIONS) .with_timeout(Some(Duration::from_secs(RELAY_TIMEOUT_SECONDS))) .get_receipt() .await?; - // If status is false, it reverted. if !receipt.status() { return Err(anyhow::anyhow!("Transaction reverted!")); } - Ok(receipt.transaction_hash) + Ok(receipt.transaction_hash()) } } - /// Check the verifying key in the contract matches the verifying key in the prover. - async fn check_vkey(&self) -> Result<()> { + /// Check the verifying key in the contract matches the + /// verifying key in the prover for the given `chain_id`. + async fn check_vkey(&self, chain_id: u64) -> Result<()> { // Check that the verifying key in the contract matches the verifying key in the prover. - let provider = ProviderBuilder::new().on_http(self.rpc_url.clone()); - let contract = SP1Vector::new(self.contract_address, provider); + let contract = self + .contracts + .get(&chain_id) + .expect("No contract for chain id"); + let verifying_key = contract .vectorXProgramVkey() .call() @@ -528,68 +791,55 @@ impl VectorXOperator { Ok(()) } - async fn run(&self) -> Result<()> { - loop { - info!("Starting loop!"); - let loop_interval_mins = get_loop_interval_mins(); - let block_interval = get_block_update_interval(); - - // Check if there is a rotate available for the next authority set. - // Note: There is a timeout here in case the Avail RPC fails to respond. Once there is - // an easy way to configure the timeout on Avail RPC requests, this should be removed. - let current_authority_set_id = - tokio::time::timeout(tokio::time::Duration::from_secs(60), self.find_rotate()) - .await??; - - info!( - "Current authority set id: {}", - current_authority_set_id.unwrap_or(0) - ); + /// Run a single iteration of the operator. + /// + /// If any step of this function fails, it will return a generic error indicating a failure. + async fn run_once(&self) -> Result<()> { + debug!("Starting operator, run_once"); + let mut has_errors = false; - // Request a rotate for the next authority set id. - if let Some(current_authority_set_id) = current_authority_set_id { - let proof = self.request_rotate(current_authority_set_id).await?; - let tx_hash = self.relay_rotate(proof).await?; - info!( - "Added authority set {}\nTransaction hash: {}", - current_authority_set_id + 1, - tx_hash - ); - } + // NOTE: Fails fast if any of the futures fail. + try_join_all(self.contracts.keys().copied().map(|id| self.check_vkey(id))).await?; - info!("On the way for header range!"); + if let Err(e) = self.handle_rotate().await { + has_errors = true; + error!("Error during `handle_rotate`: {:?}", e); + } - // Check if there is a header range request available. - // Note: There is a timeout here in case the Avail RPC fails to respond. Once there is - // an easy way to configure the timeout on Avail RPC requests, this should be removed. - let header_range_request = tokio::time::timeout( - tokio::time::Duration::from_secs(60), - self.find_header_range(block_interval), - ) - .await??; + if let Err(e) = self.handle_header_range().await { + has_errors = true; + error!("Error during `handle_header_range`: {:?}", e); + } - info!("header_range_request: {:?}", header_range_request); + if has_errors { + // By this point, any known errors have been logged. + return Err(anyhow::anyhow!("")); + } - if let Some(header_range_request) = header_range_request { - // Request the header range proof to block_to_step_to. - let proof = self.request_header_range(header_range_request).await; - match proof { - Ok(proof) => { - let tx_hash = self.relay_header_range(proof).await?; - info!( - "Posted data commitment from block {} to block {}\nTransaction hash: {}", - header_range_request.trusted_block, header_range_request.target_block, tx_hash - ); - } - Err(e) => { - error!("Header range proof generation failed: {}", e); + Ok(()) + } + + // Run the operator, indefinitely. + async fn run(self) { + let loop_interval = Duration::from_secs(get_loop_interval_mins() * 60); + let error_interval = Duration::from_secs(10); + + loop { + tokio::select! { + res = self.run_once() => { + if let Err(e) = res { + error!("Error during `run_once`: {:?}", e); + // Sleep for less time if theres an error. + tokio::time::sleep(error_interval).await; } - }; + }, + _ = tokio::time::sleep(loop_interval) => { + // If this branch is hit, its effectiely a timeout. + continue; + } } - // Sleep for N minutes. - info!("Sleeping for {} minutes.", loop_interval_mins); - tokio::time::sleep(tokio::time::Duration::from_secs(60 * loop_interval_mins)).await; + tokio::time::sleep(loop_interval).await; } } } @@ -621,15 +871,143 @@ fn get_block_update_interval() -> u32 { #[tokio::main] async fn main() { dotenv::dotenv().ok(); - env_logger::init(); + tracing_subscriber::fmt::fmt() + .with_env_filter( + EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::from_env("info")), + ) + .init(); + + let use_kms_relayer = env::var("USE_KMS_RELAYER") + .map(|v| v.parse().unwrap()) + .unwrap_or(false); + + let maybe_private_key: Option = env::var("PRIVATE_KEY") + .ok() + .map(|s| s.parse().expect("Failed to parse PRIVATE_KEY")); + + if !use_kms_relayer && maybe_private_key.is_none() { + panic!("PRIVATE_KEY must be set if USE_KMS_RELAYER is false"); + } + + let config = config::ChainConfig::fetch().expect("Failed to fetch chain config"); + + let signer = maybe_signer::MaybeWallet::new(maybe_private_key.map(EthereumWallet::new)); + + let mut operator = VectorXOperator::new(use_kms_relayer).await; + + for c in config { + let provider = ProviderBuilder::new() + .wallet(signer.clone()) + .on_http(c.rpc_url.parse().expect("Failed to parse RPC URL")); + + operator = operator.with_chain(provider, c.vector_address).await; + } + + operator.run().await +} + +/// Implement a signer that may or may not actually be set. +/// +/// This is useful to dynamically choose to use the KMS relayer in the operator, +/// without having to change the actual provider type, since the provider is generic over a signer. +mod maybe_signer { + use alloy::{ + consensus::{TxEnvelope, TypedTransaction}, + network::{Network, NetworkWallet}, + primitives::Address, + }; + + /// A signer than panics if called and not set. + #[derive(Clone, Debug)] + pub struct MaybeWallet(Option); + + impl MaybeWallet { + pub fn new(signer: Option) -> Self { + Self(signer) + } + } + + impl NetworkWallet for MaybeWallet + where + W: NetworkWallet, + N: Network, + { + fn default_signer_address(&self) -> Address { + self.0 + .as_ref() + .expect("No signer set") + .default_signer_address() + } + + fn has_signer_for(&self, address: &Address) -> bool { + self.0 + .as_ref() + .expect("No signer set") + .has_signer_for(address) + } + + fn signer_addresses(&self) -> impl Iterator { + self.0.as_ref().expect("No signer set").signer_addresses() + } + + #[doc(alias = "sign_tx_from")] + async fn sign_transaction_from( + &self, + sender: Address, + tx: TypedTransaction, + ) -> alloy::signers::Result { + self.0 + .as_ref() + .expect("No signer set") + .sign_transaction_from(sender, tx) + .await + } + } +} + +mod config { + use alloy::primitives::Address; + use anyhow::{Context, Result}; + use std::env; + + #[derive(Debug, serde::Deserialize)] + pub struct ChainConfig { + pub rpc_url: String, + pub vector_address: Address, + } + + impl ChainConfig { + /// Tries to read from the `CHAINS_PATH` environment variable, then the default path (`../chains.json`). + /// + /// If neither are set, it will try to use [`Self::from_env`]. + pub fn fetch() -> Result> { + const DEFAULT_PATH: &str = "chains.json"; + + let path = env::var("CHAINS_PATH").unwrap_or(DEFAULT_PATH.to_string()); + + Self::from_file(&path).or_else(|_| { + tracing::info!("No chains file found, trying env."); + Self::from_env().map(|c| vec![c]) + }) + } + + /// Tries to read from the `CONTRACT_ADDRESS` and `RPC_URL` environment variables. + pub fn from_env() -> Result { + let address = env::var("CONTRACT_ADDRESS").context("CONTRACT_ADDRESS not set")?; + let rpc_url = env::var("RPC_URL").context("RPC_URL not set")?; + + Ok(Self { + rpc_url, + vector_address: address.parse()?, + }) + } - let operator = VectorXOperator::new().await; + pub fn from_file(path: &str) -> Result> { + tracing::debug!("Reading chains from file: {}", path); - operator.check_vkey().await.unwrap(); + let file = std::fs::read_to_string(path)?; - loop { - if let Err(e) = operator.run().await { - error!("Error running operator: {}", e); + Ok(serde_json::from_str(&file)?) } } } diff --git a/script/src/relay.rs b/script/src/relay.rs index 2c42287..102dca7 100644 --- a/script/src/relay.rs +++ b/script/src/relay.rs @@ -2,13 +2,14 @@ use std::env; use std::str::FromStr; use std::time::Duration; +use alloy::network::Network; use alloy::primitives::B256; -use alloy::providers::{Provider, RootProvider}; -use alloy::transports::http::{Client, Http}; +use alloy::providers::Provider; +use alloy::transports::http::Client; use anyhow::Result; -use log::info; use serde::{Deserialize, Serialize}; use serde_json::json; +use tracing::info; /// Get the gas limit associated with the chain id. Note: These values have been found through /// trial and error and can be configured. @@ -22,7 +23,11 @@ pub fn get_gas_limit(chain_id: u64) -> u64 { /// Get the gas fee cap associated with the chain id, using the provider to get the gas price. Note: /// These values have been found through trial and error and can be configured. -pub async fn get_fee_cap(chain_id: u64, provider: &RootProvider>) -> u128 { +pub async fn get_fee_cap(chain_id: u64, provider: &P) -> u128 +where + P: Provider, + N: Network, +{ // Base percentage multiplier for the gas fee. let mut multiplier = 20; @@ -82,7 +87,7 @@ pub async fn relay_with_kms(args: &KMSRelayRequest, num_retries: u32) -> Result< let error_message = response .message .expect("KMS request always returns a message"); - log::warn!("KMS relay attempt {} failed: {}", attempt, error_message); + tracing::warn!("KMS relay attempt {} failed: {}", attempt, error_message); if attempt == num_retries { return Err(anyhow::anyhow!( "Failed to relay transaction: {}", diff --git a/services/Cargo.toml b/services/Cargo.toml index 68db1df..0ca0c5c 100644 --- a/services/Cargo.toml +++ b/services/Cargo.toml @@ -22,7 +22,7 @@ avail-subxt = { workspace = true } subxt = { workspace = true } sp-core = { workspace = true } codec = { workspace = true } -alloy-primitives = { workspace = true } +alloy = { workspace = true } anyhow = { workspace = true } futures = { workspace = true } diff --git a/services/bin/indexer.rs b/services/bin/indexer.rs index d45b65e..63dd11b 100644 --- a/services/bin/indexer.rs +++ b/services/bin/indexer.rs @@ -10,7 +10,7 @@ use services::types::{Commit, GrandpaJustification}; use sp_core::bytes; use subxt::backend::rpc::RpcSubscription; -use timeout::Timeout; +use sp1_vector_primitives::Timeout; /// The justification type that the Avail Subxt client returns for justifications. Needs a custom /// deserializer, so we can't use the equivalent `GrandpaJustification` type. @@ -140,19 +140,3 @@ pub async fn main() { listen_for_justifications().await; } - -mod timeout { - use std::future::Future; - use std::time::Duration; - use tokio::time::{timeout, Timeout as TimeoutFuture}; - - pub trait Timeout: Sized { - fn timeout(self, duration: Duration) -> TimeoutFuture; - } - - impl Timeout for T { - fn timeout(self, duration: Duration) -> TimeoutFuture { - timeout(duration, self) - } - } -} diff --git a/services/src/input.rs b/services/src/input.rs index 422ca79..4a45bdf 100644 --- a/services/src/input.rs +++ b/services/src/input.rs @@ -12,7 +12,7 @@ use std::env; use subxt::backend::rpc::RpcSubscription; use crate::types::{EncodedFinalityProof, FinalityProof, GrandpaJustification}; -use alloy_primitives::{B256, B512}; +use alloy::primitives::{B256, B512}; use avail_subxt::avail_client::AvailClient; use avail_subxt::config::substrate::DigestItem; use avail_subxt::primitives::Header; @@ -31,7 +31,7 @@ pub struct RpcDataFetcher { } /// Data for the header range request. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct HeaderRangeRequestData { pub trusted_block: u32, pub target_block: u32, @@ -118,7 +118,7 @@ impl RpcDataFetcher { let trusted_header = self .get_header(header_range_request_data.trusted_block) .await; - let trusted_header_hash: alloy_primitives::FixedBytes<32> = + let trusted_header_hash: alloy::primitives::FixedBytes<32> = B256::from_slice(&trusted_header.hash().0); let num_headers = From 3a3c56030ccfef63ab44b8e9f373cc6a9bd57bcd Mon Sep 17 00:00:00 2001 From: nhtyy Date: Tue, 18 Feb 2025 13:53:06 -0800 Subject: [PATCH 02/18] fix: use alloy-{primitives, sol-types} to avoid network deps --- .gitignore | 5 ++++- Cargo.lock | 28 +++++++++++----------------- primitives/Cargo.toml | 5 +++-- primitives/src/header_range.rs | 4 ++-- primitives/src/justification.rs | 2 +- primitives/src/lib.rs | 2 +- primitives/src/merkle.rs | 2 +- primitives/src/rotate.rs | 4 ++-- primitives/src/types.rs | 4 ++-- 9 files changed, 27 insertions(+), 29 deletions(-) diff --git a/.gitignore b/.gitignore index f8e49b4..a057dbd 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,7 @@ pgo-data.profdata /etc/secrets -**/filtered_transactions/** \ No newline at end of file +**/filtered_transactions/** + +!chains.example.json +chains**.json \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 5eb0d18..bf00250 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -462,9 +462,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.15" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6259a506ab13e1d658796c31e6e39d2e2ee89243bcc505ddc613b35732e0a430" +checksum = "478bedf4d24e71ea48428d1bc278553bd7c6ae07c30ca063beb0b09fe58a9e74" dependencies = [ "alloy-rlp", "bytes", @@ -473,7 +473,6 @@ dependencies = [ "derive_more 1.0.0", "foldhash", "hashbrown 0.15.2", - "hex-literal", "indexmap 2.7.0", "itoa", "k256", @@ -482,7 +481,7 @@ dependencies = [ "proptest", "rand 0.8.5", "ruint", - "rustc-hash 2.0.0", + "rustc-hash 2.1.1", "serde", "sha3", "tiny-keccak", @@ -3995,9 +3994,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" [[package]] name = "foreign-types" @@ -4697,12 +4696,6 @@ dependencies = [ "serde", ] -[[package]] -name = "hex-literal" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" - [[package]] name = "hmac" version = "0.8.1" @@ -7018,7 +7011,7 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 2.0.0", + "rustc-hash 2.1.1", "rustls 0.23.12", "socket2", "thiserror 1.0.65", @@ -7035,7 +7028,7 @@ dependencies = [ "bytes", "rand 0.8.5", "ring 0.17.8", - "rustc-hash 2.0.0", + "rustc-hash 2.1.1", "rustls 0.23.12", "slab", "thiserror 1.0.65", @@ -7517,9 +7510,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustc-hex" @@ -9966,7 +9959,8 @@ dependencies = [ name = "sp1-vector-primitives" version = "0.1.0" dependencies = [ - "alloy", + "alloy-primitives", + "alloy-sol-types", "avail-subxt 0.5.0 (git+https://github.com/availproject/avail.git?tag=v2.2.2.0-rc1)", "blake2", "ed25519-consensus", diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 5316eba..1540d6c 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -8,11 +8,12 @@ serde.workspace = true sha2.workspace = true ed25519-consensus.workspace = true codec.workspace = true -alloy.workspace = true blake2.workspace = true itertools.workspace = true tokio.workspace = true +alloy-primitives = "0.8" +alloy-sol-types = { version = "0.8" } [dev-dependencies] primitive-types = "0.12.2" -avail-subxt = { git = "https://github.com/availproject/avail.git", tag = "v2.2.2.0-rc1" } \ No newline at end of file +avail-subxt = { git = "https://github.com/availproject/avail.git", tag = "v2.2.2.0-rc1" } diff --git a/primitives/src/header_range.rs b/primitives/src/header_range.rs index 8ee7937..1afd14c 100644 --- a/primitives/src/header_range.rs +++ b/primitives/src/header_range.rs @@ -1,5 +1,5 @@ -use alloy::primitives::B256; -use alloy::sol_types::SolType; +use alloy_primitives::B256; +use alloy_sol_types::SolType; use crate::consts::HEADER_OUTPUTS_LENGTH; use crate::merkle::get_merkle_root_commitments; diff --git a/primitives/src/justification.rs b/primitives/src/justification.rs index 3fc5e0c..9e8a6f9 100644 --- a/primitives/src/justification.rs +++ b/primitives/src/justification.rs @@ -3,7 +3,7 @@ use codec::Encode; use ed25519_consensus::{Signature, VerificationKey}; use std::collections::HashMap; -use alloy::primitives::B256; +use alloy_primitives::B256; /// Verify that a Ed25519 signature is valid. Panics if the signature is not valid. fn verify_signature(pubkey_bytes: [u8; 32], signed_message: &[u8], signature: [u8; 64]) { diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 85f891c..cda143d 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -1,4 +1,4 @@ -use alloy::primitives::B256; +use alloy_primitives::B256; use blake2::{ digest::{Update, VariableOutput}, Blake2bVar, diff --git a/primitives/src/merkle.rs b/primitives/src/merkle.rs index 79a5217..52a0bda 100644 --- a/primitives/src/merkle.rs +++ b/primitives/src/merkle.rs @@ -1,7 +1,7 @@ use sha2::{Digest, Sha256}; use crate::types::DecodedHeaderData; -use alloy::primitives::B256; +use alloy_primitives::B256; // Computes the simple Merkle root of the leaves. If the number of leaves is not a power of 2, pad // with empty 32 byte arrays till the next power of 2. diff --git a/primitives/src/rotate.rs b/primitives/src/rotate.rs index be67979..a8321f4 100644 --- a/primitives/src/rotate.rs +++ b/primitives/src/rotate.rs @@ -2,8 +2,8 @@ use crate::{ compute_authority_set_commitment, consts::ROTATE_OUTPUTS_LENGTH, decode_scale_compact_int, types::RotateInputs, types::RotateOutputs, verify_encoded_validators, verify_justification, }; -use alloy::primitives::B256; -use alloy::sol_types::SolType; +use alloy_primitives::B256; +use alloy_sol_types::SolType; /// Verify the justification from the current authority set on the epoch end header and return the new /// authority set commitment. diff --git a/primitives/src/types.rs b/primitives/src/types.rs index 1396998..3ddda4e 100644 --- a/primitives/src/types.rs +++ b/primitives/src/types.rs @@ -1,5 +1,5 @@ -use alloy::primitives::{B256, B512}; -use alloy::sol; +use alloy_primitives::{B256, B512}; +use alloy_sol_types::sol; use serde::{Deserialize, Serialize}; From 955b66fd4f831e393e166e52934b2511c9a87ec2 Mon Sep 17 00:00:00 2001 From: nhtyy Date: Tue, 18 Feb 2025 13:54:43 -0800 Subject: [PATCH 03/18] chore: VectorXOperator -> SP1VectorOperator --- script/bin/operator.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/script/bin/operator.rs b/script/bin/operator.rs index 10e017a..5e75a4c 100644 --- a/script/bin/operator.rs +++ b/script/bin/operator.rs @@ -70,7 +70,7 @@ sol! { type SP1VectorInstance = SP1Vector::SP1VectorInstance<(), P, N>; -struct VectorXOperator { +struct SP1VectorOperator { pk: Arc, vk: SP1VerifyingKey, use_kms_relayer: bool, @@ -98,7 +98,7 @@ struct RotateContractData { // Constructor //////////////////////////////////////////////////////////// -impl VectorXOperator +impl SP1VectorOperator where P: Provider, N: Network, @@ -159,7 +159,7 @@ where // Block Utilities //////////////////////////////////////////////////////////// -impl VectorXOperator +impl SP1VectorOperator where P: Provider, N: Network, @@ -381,7 +381,7 @@ where // Rotate Utilities //////////////////////////////////////////////////////////// -impl VectorXOperator +impl SP1VectorOperator where P: Provider, N: Network, @@ -484,7 +484,7 @@ where // Control Flow & SP1 //////////////////////////////////////////////////////////// -impl VectorXOperator +impl SP1VectorOperator where P: Provider, N: Network, @@ -890,10 +890,11 @@ async fn main() { } let config = config::ChainConfig::fetch().expect("Failed to fetch chain config"); + debug!("config: {:?}", config); let signer = maybe_signer::MaybeWallet::new(maybe_private_key.map(EthereumWallet::new)); - let mut operator = VectorXOperator::new(use_kms_relayer).await; + let mut operator = SP1VectorOperator::new(use_kms_relayer).await; for c in config { let provider = ProviderBuilder::new() From a8b339542b9fd2155f8f66a90e24a7cdf965bafa Mon Sep 17 00:00:00 2001 From: nhtyy Date: Tue, 18 Feb 2025 15:41:13 -0800 Subject: [PATCH 04/18] fix: conncurency lock, instrument --- Cargo.lock | 2 +- Cargo.toml | 4 ++++ script/Cargo.toml | 4 ++-- script/bin/operator.rs | 27 +++++++++++++++++++++------ services/Cargo.toml | 2 +- services/bin/indexer.rs | 2 +- services/src/aws.rs | 2 +- services/src/input.rs | 15 +++++++++++++++ 8 files changed, 46 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bf00250..bea738e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8184,7 +8184,6 @@ dependencies = [ "env_logger", "futures", "hex", - "log", "parity-scale-codec", "reqwest 0.11.27", "serde", @@ -8194,6 +8193,7 @@ dependencies = [ "subxt", "test-case", "tokio", + "tracing", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 1439923..9c6ddcb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,10 @@ sp1-vectorx-program = { path = "program" } services = { path = "services" } sp1-vector-primitives = { path = "primitives" } +# logging +tracing = "0.1.41" +tracing-subscriber = "0.3.19" + [profile.release] opt-level = 3 diff --git a/script/Cargo.toml b/script/Cargo.toml index 5a06d6a..0cf7e22 100644 --- a/script/Cargo.toml +++ b/script/Cargo.toml @@ -46,8 +46,8 @@ rand = "0.8.5" sp1-build = { workspace = true } # Logging -tracing = "0.1.41" -tracing-subscriber = "0.3.19" +tracing.workspace = true +tracing-subscriber.workspace = true [build-dependencies] sp1-build = { workspace = true } diff --git a/script/bin/operator.rs b/script/bin/operator.rs index 5e75a4c..be5fbca 100644 --- a/script/bin/operator.rs +++ b/script/bin/operator.rs @@ -21,7 +21,7 @@ use sp1_sdk::{ SP1ProvingKey, SP1Stdin, SP1VerifyingKey, }; -use tracing::{debug, error, info}; +use tracing::{debug, error, info, instrument}; use tracing_subscriber::EnvFilter; use sp1_vector_primitives::types::ProofType; @@ -204,6 +204,7 @@ where } // Ideally, post a header range update every ideal_block_interval blocks. Returns Option<(latest_block, block_to_step_to)>. + #[instrument(skip(self, ideal_block_interval))] async fn find_header_range( &self, chain_id: u64, @@ -457,19 +458,26 @@ where } // Determine if a rotate is needed and request the proof if so. Returns Option. + #[instrument(skip(self))] async fn find_rotate(&self, chain_id: u64) -> Result> { + debug!("finding rotate for chain {}", chain_id); + let rotate_contract_data = self.get_contract_data_for_rotate(chain_id).await?; + debug!("rotate_contract_data: {:?}", rotate_contract_data); // Get the current block and authority set id from the Avail chain. - let fetcher = RpcDataFetcher::new().await; - let head = fetcher.get_head().await; - let head_block = head.number; - let head_authority_set_id = fetcher.get_authority_set_id(head_block - 1).await; + let head_block = self.fetcher.get_head().await.number; + debug!("head_block: {}", head_block); + + let head_authority_set_id = self.fetcher.get_authority_set_id(head_block - 1).await; + debug!("head_authority_set_id: {}", head_authority_set_id); // The current authority set id is the authority set id of the block before the current block. - let current_authority_set_id = fetcher + let current_authority_set_id = self + .fetcher .get_authority_set_id(rotate_contract_data.current_block - 1) .await; + debug!("current_authority_set_id: {}", current_authority_set_id); if current_authority_set_id < head_authority_set_id && !rotate_contract_data.next_authority_set_hash_exists @@ -612,6 +620,8 @@ where /// /// If any step of this function fails, it will return a generic error indicating a failure. async fn handle_rotate(&self) -> Result<()> { + debug!("Enter handle rotate"); + let next_authority_set_ids = self.contracts.keys().copied().map(|id| async move { Result::<_, anyhow::Error>::Ok((id, self.find_rotate(id).await?)) }); @@ -730,7 +740,10 @@ where /// Relay a transaction to a chain. /// /// NOTE: Assumes the provider has a wallet. + #[instrument(skip(self, tx))] async fn relay_tx(&self, chain_id: u64, tx: N::TransactionRequest) -> Result { + debug!("Relaying transaction to chain {}", chain_id); + if self.use_kms_relayer { relay::relay_with_kms( &relay::KMSRelayRequest { @@ -768,6 +781,8 @@ where /// Check the verifying key in the contract matches the /// verifying key in the prover for the given `chain_id`. async fn check_vkey(&self, chain_id: u64) -> Result<()> { + debug!("Checking verifying key for chain {}", chain_id); + // Check that the verifying key in the contract matches the verifying key in the prover. let contract = self .contracts diff --git a/services/Cargo.toml b/services/Cargo.toml index 0ca0c5c..3f15d19 100644 --- a/services/Cargo.toml +++ b/services/Cargo.toml @@ -11,7 +11,7 @@ path = "bin/indexer.rs" sp1-vector-primitives = { workspace = true } env_logger = { workspace = true } hex = { workspace = true } -log = { workspace = true } +tracing = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } tokio = { workspace = true } diff --git a/services/bin/indexer.rs b/services/bin/indexer.rs index 63dd11b..7447fc3 100644 --- a/services/bin/indexer.rs +++ b/services/bin/indexer.rs @@ -1,7 +1,6 @@ use avail_subxt::primitives::Header; use avail_subxt::RpcParams; use codec::Decode; -use log::{debug, error, info}; use serde::de::Error; use serde::Deserialize; use services::aws::AWSClient; @@ -9,6 +8,7 @@ use services::input::RpcDataFetcher; use services::types::{Commit, GrandpaJustification}; use sp_core::bytes; use subxt::backend::rpc::RpcSubscription; +use tracing::{debug, error, info}; use sp1_vector_primitives::Timeout; diff --git a/services/src/aws.rs b/services/src/aws.rs index 80a7f70..6ce672c 100644 --- a/services/src/aws.rs +++ b/services/src/aws.rs @@ -2,9 +2,9 @@ use aws_sdk_dynamodb::types::AttributeValue; use aws_sdk_dynamodb::Client; use anyhow::Result; -use log::info; use serde_json::{from_str, to_string}; use std::collections::HashMap; +use tracing::info; use crate::types::GrandpaJustification; diff --git a/services/src/input.rs b/services/src/input.rs index 4a45bdf..a3c843a 100644 --- a/services/src/input.rs +++ b/services/src/input.rs @@ -22,6 +22,10 @@ use futures::future::join_all; use sp_core::ed25519; use subxt::config::Header as SubxtHeader; +/// In order to avoid errors from the RPC client, tasks should coordinate via this mutex to coordinate +/// large amounts of concurrent requests. +static CONCURRENCY_MUTEX: tokio::sync::Mutex<()> = tokio::sync::Mutex::const_new(()); + /// An RPC data fetcher for fetching data for VectorX. The vectorx_query_url is only necessary when /// querying justifications. pub struct RpcDataFetcher { @@ -118,6 +122,7 @@ impl RpcDataFetcher { let trusted_header = self .get_header(header_range_request_data.trusted_block) .await; + let trusted_header_hash: alloy::primitives::FixedBytes<32> = B256::from_slice(&trusted_header.hash().0); @@ -136,6 +141,12 @@ impl RpcDataFetcher { merkle_tree_size = get_merkle_tree_size(num_headers); } + tracing::debug!( + "Getting block headers range from {} to {}", + header_range_request_data.trusted_block, + header_range_request_data.target_block + ); + let headers = self .get_block_headers_range( header_range_request_data.trusted_block, @@ -229,6 +240,10 @@ impl RpcDataFetcher { // Fetch the headers in batches of MAX_CONCURRENT_WS_REQUESTS. The WS connection will error if there // are too many concurrent requests with Rpc(ClientError(MaxSlotsExceeded)). const MAX_CONCURRENT_WS_REQUESTS: usize = 200; + + // Take the guard to coordinate concurrent requests. + let _guard = CONCURRENCY_MUTEX.lock().await; + let mut headers = Vec::new(); let mut curr_block = start_block_number; while curr_block <= end_block_number { From 1cf96ba479f37300c83ae6dc0456459f17771be8 Mon Sep 17 00:00:00 2001 From: nhtyy Date: Tue, 18 Feb 2025 15:43:48 -0800 Subject: [PATCH 05/18] fix: add explicit loop timeout --- script/bin/operator.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/script/bin/operator.rs b/script/bin/operator.rs index be5fbca..8831f9b 100644 --- a/script/bin/operator.rs +++ b/script/bin/operator.rs @@ -36,6 +36,9 @@ use sp1_vectorx_script::SP1_VECTOR_ELF; // If the SP1 proof takes too long to respond, time out. const PROOF_TIMEOUT_SECS: u64 = 60 * 30; +// If the operator takes too long to run, time out. +const LOOP_TIMEOUT_MINS: u64 = 30; + // If the RPC takes too long to respond, time out. const RPC_TIMEOUT_SECS: u64 = 60 * 2; @@ -45,6 +48,7 @@ const NUM_CONFIRMATIONS: u64 = 3; // If the relay takes too long to respond, time out. const RELAY_TIMEOUT_SECONDS: u64 = 60; +// The number of times to retry a relay transaction. const NUM_RELAY_RETRIES: u32 = 3; //////////////////////////////////////////////////////////// @@ -848,12 +852,18 @@ where tokio::time::sleep(error_interval).await; } }, - _ = tokio::time::sleep(loop_interval) => { + _ = tokio::time::sleep(Duration::from_secs(LOOP_TIMEOUT_MINS * 60)) => { // If this branch is hit, its effectiely a timeout. continue; } } + tracing::info!( + "Operator ran successfully, sleeping for {} seconds", + loop_interval.as_secs() + ); + + // Sleep for the loop interval. tokio::time::sleep(loop_interval).await; } } From 5aa3ac9fda81c974f6db33141ccabb1370cc0361 Mon Sep 17 00:00:00 2001 From: nhtyy Date: Tue, 18 Feb 2025 15:48:44 -0800 Subject: [PATCH 06/18] fix: newtork prover run -> run_async --- script/bin/operator.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/script/bin/operator.rs b/script/bin/operator.rs index 8831f9b..b22e51d 100644 --- a/script/bin/operator.rs +++ b/script/bin/operator.rs @@ -204,7 +204,8 @@ where .skip_simulation(true) .plonk() .timeout(Duration::from_secs(PROOF_TIMEOUT_SECS)) - .run() + .run_async() + .await } // Ideally, post a header range update every ideal_block_interval blocks. Returns Option<(latest_block, block_to_step_to)>. @@ -458,7 +459,8 @@ where .skip_simulation(true) .plonk() .timeout(Duration::from_secs(PROOF_TIMEOUT_SECS)) - .run() + .run_async() + .await } // Determine if a rotate is needed and request the proof if so. Returns Option. From 518880e17f1c896d28ec109ba18fd57c745e1922 Mon Sep 17 00:00:00 2001 From: nhtyy Date: Tue, 18 Feb 2025 16:01:58 -0800 Subject: [PATCH 07/18] chore: signer mode --- script/bin/operator.rs | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/script/bin/operator.rs b/script/bin/operator.rs index b22e51d..7e6c377 100644 --- a/script/bin/operator.rs +++ b/script/bin/operator.rs @@ -29,6 +29,8 @@ use sp1_vector_primitives::Timeout; use sp1_vectorx_script::relay::{self}; use sp1_vectorx_script::SP1_VECTOR_ELF; +use config::{ChainConfig, SignerMode}; + //////////////////////////////////////////////////////////// // Constants //////////////////////////////////////////////////////////// @@ -77,7 +79,7 @@ type SP1VectorInstance = SP1Vector::SP1VectorInstance<(), P, N>; struct SP1VectorOperator { pk: Arc, vk: SP1VerifyingKey, - use_kms_relayer: bool, + signer_mode: SignerMode, tree_size: u32, fetcher: RpcDataFetcher, prover: NetworkProver, @@ -107,7 +109,7 @@ where P: Provider, N: Network, { - async fn new(use_kms_relayer: bool) -> Self { + async fn new(signer_mode: SignerMode) -> Self { dotenv::dotenv().ok(); let prover = ProverClient::builder().network().build(); @@ -117,7 +119,7 @@ where fetcher: RpcDataFetcher::new().await, pk: Arc::new(pk), vk, - use_kms_relayer, + signer_mode, prover, contracts: HashMap::new(), tree_size: 0, @@ -750,7 +752,7 @@ where async fn relay_tx(&self, chain_id: u64, tx: N::TransactionRequest) -> Result { debug!("Relaying transaction to chain {}", chain_id); - if self.use_kms_relayer { + if matches!(self.signer_mode, SignerMode::KMS) { relay::relay_with_kms( &relay::KMSRelayRequest { chain_id, @@ -855,7 +857,6 @@ where } }, _ = tokio::time::sleep(Duration::from_secs(LOOP_TIMEOUT_MINS * 60)) => { - // If this branch is hit, its effectiely a timeout. continue; } } @@ -904,24 +905,24 @@ async fn main() { ) .init(); - let use_kms_relayer = env::var("USE_KMS_RELAYER") + let signer_mode = env::var("SIGNER_MODE") .map(|v| v.parse().unwrap()) - .unwrap_or(false); + .unwrap_or(SignerMode::Local); let maybe_private_key: Option = env::var("PRIVATE_KEY") .ok() .map(|s| s.parse().expect("Failed to parse PRIVATE_KEY")); - if !use_kms_relayer && maybe_private_key.is_none() { + if matches!(signer_mode, SignerMode::Local) && maybe_private_key.is_none() { panic!("PRIVATE_KEY must be set if USE_KMS_RELAYER is false"); } - let config = config::ChainConfig::fetch().expect("Failed to fetch chain config"); + let config = ChainConfig::fetch().expect("Failed to fetch chain config"); debug!("config: {:?}", config); let signer = maybe_signer::MaybeWallet::new(maybe_private_key.map(EthereumWallet::new)); - let mut operator = SP1VectorOperator::new(use_kms_relayer).await; + let mut operator = SP1VectorOperator::new(signer_mode).await; for c in config { let provider = ProviderBuilder::new() @@ -996,7 +997,25 @@ mod maybe_signer { mod config { use alloy::primitives::Address; use anyhow::{Context, Result}; - use std::env; + use std::{env, str::FromStr}; + + #[derive(Debug)] + pub enum SignerMode { + KMS, + Local, + } + + impl FromStr for SignerMode { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "kms" => Self::KMS, + "local" => Self::Local, + _ => return Err(anyhow::anyhow!("Invalid signer mode: {}", s)), + }) + } + } #[derive(Debug, serde::Deserialize)] pub struct ChainConfig { From b5189efeab69b3c486d572e8840412d8398ebe5e Mon Sep 17 00:00:00 2001 From: nhtyy Date: Tue, 18 Feb 2025 16:03:19 -0800 Subject: [PATCH 08/18] fix: third person tense --- script/bin/operator.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/script/bin/operator.rs b/script/bin/operator.rs index 7e6c377..0c599ad 100644 --- a/script/bin/operator.rs +++ b/script/bin/operator.rs @@ -593,7 +593,7 @@ where // - Creating the rotate proof failed. // - Relaying the transaction failed. // - // In either case we want to log it and indicate the failure to the caller. + // In either case log it and indicate the failure to the caller. let mut has_errors = false; for batch_result in results { if let Err(e) = batch_result { @@ -609,7 +609,7 @@ where } else { has_errors = true; error!( - "Error relaying rotate proof! {:?}", + "Error relaying rotate proof: {:?}", relay_result.unwrap_err() ); } @@ -673,7 +673,7 @@ where ); // All contract instances will produce the same calldata. - // We should have at least one chain id in the vector. + // The vector should have non-zero length. let contract = self .contracts .get(&chain_ids[0]) @@ -714,7 +714,7 @@ where // - Creating the rotate proof failed. // - Relaying the transaction failed. // - // In either case we want to log it and indicate the failure to the caller. + // In either case log it and indicate the failure to the caller. let mut has_errors = false; for batch_result in results { if let Err(e) = batch_result { @@ -730,7 +730,7 @@ where } else { has_errors = true; error!( - "Error relaying rotate proof! {:?}", + "Error relaying rotate proof: {:?}", relay_result.unwrap_err() ); } @@ -745,7 +745,7 @@ where } } - /// Relay a transaction to a chain. + /// Relay a transaction to the given chain id. /// /// NOTE: Assumes the provider has a wallet. #[instrument(skip(self, tx))] From 36eb3aaf72d37e24bef0b751dff92f6e54a02402 Mon Sep 17 00:00:00 2001 From: nhtyy Date: Tue, 18 Feb 2025 16:05:17 -0800 Subject: [PATCH 09/18] fix: include chains.json example --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index a057dbd..8bdafc3 100644 --- a/.gitignore +++ b/.gitignore @@ -32,5 +32,5 @@ pgo-data.profdata **/filtered_transactions/** -!chains.example.json -chains**.json \ No newline at end of file +chains*.json +!chains.example.json \ No newline at end of file From 1a1e2a3905072c10cb1ef37959422ff9d4549ce5 Mon Sep 17 00:00:00 2001 From: nhtyy Date: Tue, 18 Feb 2025 16:07:40 -0800 Subject: [PATCH 10/18] fix: rm tokio from primitives --- Cargo.lock | 1 - chains.example.json | 6 ++++++ primitives/Cargo.toml | 1 - primitives/src/lib.rs | 20 -------------------- script/bin/operator.rs | 2 +- services/bin/indexer.rs | 2 +- services/src/lib.rs | 18 ++++++++++++++++++ 7 files changed, 26 insertions(+), 24 deletions(-) create mode 100644 chains.example.json diff --git a/Cargo.lock b/Cargo.lock index bea738e..f23faea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9969,7 +9969,6 @@ dependencies = [ "primitive-types", "serde", "sha2 0.10.8", - "tokio", ] [[package]] diff --git a/chains.example.json b/chains.example.json new file mode 100644 index 0000000..6024d2e --- /dev/null +++ b/chains.example.json @@ -0,0 +1,6 @@ +[ + { + "rpc_url": "https://rpc.ankr.com/eth", + "vector_address": "0x0000000000000000000000000000000000000000" + } +] \ No newline at end of file diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 1540d6c..dcdc40e 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -10,7 +10,6 @@ ed25519-consensus.workspace = true codec.workspace = true blake2.workspace = true itertools.workspace = true -tokio.workspace = true alloy-primitives = "0.8" alloy-sol-types = { version = "0.8" } diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index cda143d..7214b86 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -120,23 +120,3 @@ mod tests { assert_eq!(extracted_hash, hash, "Hashes don't match") } } - -pub use timeout::Timeout; - -mod timeout { - use std::future::Future; - use std::time::Duration; - use tokio::time::{timeout, Timeout as TimeoutFuture}; - - pub trait Timeout: Sized { - fn timeout(self, duration: Duration) -> TimeoutFuture; - } - - impl Timeout for T { - fn timeout(self, duration: Duration) -> TimeoutFuture { - timeout(duration, self) - } - } -} - -mod maybe_signer {} diff --git a/script/bin/operator.rs b/script/bin/operator.rs index 0c599ad..6a3634a 100644 --- a/script/bin/operator.rs +++ b/script/bin/operator.rs @@ -24,8 +24,8 @@ use sp1_sdk::{ use tracing::{debug, error, info, instrument}; use tracing_subscriber::EnvFilter; +use services::Timeout; use sp1_vector_primitives::types::ProofType; -use sp1_vector_primitives::Timeout; use sp1_vectorx_script::relay::{self}; use sp1_vectorx_script::SP1_VECTOR_ELF; diff --git a/services/bin/indexer.rs b/services/bin/indexer.rs index 7447fc3..76d3282 100644 --- a/services/bin/indexer.rs +++ b/services/bin/indexer.rs @@ -10,7 +10,7 @@ use sp_core::bytes; use subxt::backend::rpc::RpcSubscription; use tracing::{debug, error, info}; -use sp1_vector_primitives::Timeout; +use services::Timeout; /// The justification type that the Avail Subxt client returns for justifications. Needs a custom /// deserializer, so we can't use the equivalent `GrandpaJustification` type. diff --git a/services/src/lib.rs b/services/src/lib.rs index c2645bb..44065ef 100644 --- a/services/src/lib.rs +++ b/services/src/lib.rs @@ -1,3 +1,21 @@ pub mod aws; pub mod input; pub mod types; + +pub use timeout::Timeout; + +mod timeout { + use std::future::Future; + use std::time::Duration; + use tokio::time::{timeout, Timeout as TimeoutFuture}; + + pub trait Timeout: Sized { + fn timeout(self, duration: Duration) -> TimeoutFuture; + } + + impl Timeout for T { + fn timeout(self, duration: Duration) -> TimeoutFuture { + timeout(duration, self) + } + } +} From 98ec81ac2409059729bab1bee9ce57adbcf98cb1 Mon Sep 17 00:00:00 2001 From: nhtyy Date: Tue, 18 Feb 2025 16:13:08 -0800 Subject: [PATCH 11/18] chore: match in async blocks --- script/bin/operator.rs | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/script/bin/operator.rs b/script/bin/operator.rs index 6a3634a..82c373f 100644 --- a/script/bin/operator.rs +++ b/script/bin/operator.rs @@ -570,15 +570,14 @@ where let tx = tx.clone(); async move { - Result::<_, anyhow::Error>::Ok(( - chain_id, - self.relay_tx(chain_id, tx) - .timeout(Duration::from_secs(RELAY_TIMEOUT_SECONDS)) - .await - .context(format!( - "Relaying proof for chain {chain_id} failed" - ))??, - )) + match self + .relay_tx(chain_id, tx) + .await + .context(format!("Relaying proof for chain {chain_id} failed")) + { + Ok(tx_hash) => Ok((chain_id, tx_hash)), + Err(e) => Err(e), + } } }) .collect(); @@ -691,15 +690,14 @@ where let tx = tx.clone(); async move { - Result::<_, anyhow::Error>::Ok(( - chain_id, - self.relay_tx(chain_id, tx) - .timeout(Duration::from_secs(RELAY_TIMEOUT_SECONDS)) - .await - .context(format!( - "Relaying proof for chain {chain_id} failed" - ))??, - )) + match self + .relay_tx(chain_id, tx) + .await + .context(format!("Relaying proof for chain {chain_id} failed")) + { + Ok(tx_hash) => Ok((chain_id, tx_hash)), + Err(e) => Err(e), + } } }) .collect(); From af80b864b832829324b23fc9f3d21234c97dd774 Mon Sep 17 00:00:00 2001 From: nhtyy Date: Tue, 18 Feb 2025 16:14:53 -0800 Subject: [PATCH 12/18] fix: clippy --- script/bin/operator.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/script/bin/operator.rs b/script/bin/operator.rs index 82c373f..d2aa7f2 100644 --- a/script/bin/operator.rs +++ b/script/bin/operator.rs @@ -750,7 +750,7 @@ where async fn relay_tx(&self, chain_id: u64, tx: N::TransactionRequest) -> Result { debug!("Relaying transaction to chain {}", chain_id); - if matches!(self.signer_mode, SignerMode::KMS) { + if matches!(self.signer_mode, SignerMode::Kms) { relay::relay_with_kms( &relay::KMSRelayRequest { chain_id, @@ -999,7 +999,7 @@ mod config { #[derive(Debug)] pub enum SignerMode { - KMS, + Kms, Local, } @@ -1008,7 +1008,7 @@ mod config { fn from_str(s: &str) -> Result { Ok(match s { - "kms" => Self::KMS, + "kms" => Self::Kms, "local" => Self::Local, _ => return Err(anyhow::anyhow!("Invalid signer mode: {}", s)), }) From 9dd2b183e824d8b107d45740be6c3243cb4d29c2 Mon Sep 17 00:00:00 2001 From: nhtyy Date: Tue, 18 Feb 2025 16:15:28 -0800 Subject: [PATCH 13/18] rm nl --- script/bin/costs.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/script/bin/costs.rs b/script/bin/costs.rs index 65a2dea..228e809 100644 --- a/script/bin/costs.rs +++ b/script/bin/costs.rs @@ -18,7 +18,6 @@ use std::cmp::Ordering; use std::collections::HashMap; use std::str::FromStr; use std::{env, fs}; - use tracing_subscriber::EnvFilter; #[derive(Parser, Debug, Clone)] From f58b0e84ecb53cd793ffdab07c041bd4c0b1fed6 Mon Sep 17 00:00:00 2001 From: nhtyy Date: Tue, 18 Feb 2025 19:44:34 -0800 Subject: [PATCH 14/18] fix: logging --- script/bin/operator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/bin/operator.rs b/script/bin/operator.rs index d2aa7f2..c245797 100644 --- a/script/bin/operator.rs +++ b/script/bin/operator.rs @@ -602,7 +602,7 @@ where for relay_result in batch_result.unwrap() { if let Ok((chain_id, tx_hash)) = relay_result { info!( - "Posted next authority set on chain {}\nTransaction hash: {}", + "Posted next header range on chain {}\nTransaction hash: {}", chain_id, tx_hash ); } else { From f3e9a497c580f2749daf323dd5c6e950c79e5bad Mon Sep 17 00:00:00 2001 From: nhtyy Date: Tue, 18 Feb 2025 21:43:45 -0800 Subject: [PATCH 15/18] more debug logs --- script/bin/operator.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/script/bin/operator.rs b/script/bin/operator.rs index c245797..64bdede 100644 --- a/script/bin/operator.rs +++ b/script/bin/operator.rs @@ -218,6 +218,10 @@ where ideal_block_interval: u32, ) -> Result> { let header_range_contract_data = self.get_contract_data_for_header_range(chain_id).await?; + debug!( + "header_range_contract_data: {:?}", + header_range_contract_data + ); // The current authority set id is the authority set id of the block before the current block. let current_authority_set_id = self @@ -334,6 +338,7 @@ where if last_justified_block != 0 && last_justified_block <= vectorx_current_block + header_range_commitment_tree_size { + debug!("last_justified_block: {}", last_justified_block); return Some(last_justified_block); } @@ -353,6 +358,8 @@ where let mut block_to_step_to = max_valid_block_to_step_to - (max_valid_block_to_step_to % ideal_block_interval); + debug!("Block to step to: {}", block_to_step_to); + // If block_to_step_to is <= to the current block, return None. if block_to_step_to <= vectorx_current_block { return None; From f2fb5ccdb9c7127b5f3b7ee9a3426f486a747b73 Mon Sep 17 00:00:00 2001 From: nhtyy Date: Wed, 19 Feb 2025 10:43:51 -0800 Subject: [PATCH 16/18] fix: invalid to fields set when producing calldata from a single contract instance --- script/bin/operator.rs | 51 ++++++++++++++++++------------------------ services/src/input.rs | 5 +---- 2 files changed, 23 insertions(+), 33 deletions(-) diff --git a/script/bin/operator.rs b/script/bin/operator.rs index 64bdede..4d08119 100644 --- a/script/bin/operator.rs +++ b/script/bin/operator.rs @@ -255,7 +255,7 @@ where // Find the block to step to. If no block is returned, either 1) there is no block satisfying // the conditions that is available to step to or 2) something has gone wrong with the indexer. - let block_to_step_to = self + let maybe_block_to_step_to = self .find_block_to_step_to( ideal_block_interval, header_range_contract_data.header_range_commitment_tree_size, @@ -265,9 +265,9 @@ where ) .await; - info!("block_to_step_to: {:?}", block_to_step_to); + info!("maybe_block_to_step_to: {:?}", maybe_block_to_step_to); - if let Some(block_to_step_to) = block_to_step_to { + if let Some(block_to_step_to) = maybe_block_to_step_to { return Ok(Some(HeaderRangeRequestData { trusted_block: header_range_contract_data.vectorx_latest_block, target_block: block_to_step_to, @@ -558,23 +558,21 @@ where chain_ids, header_range_data ); - // All contract instances will produce the same calldata. - // And the `chain_ids` vector should have non-zero length. - let contract = self - .contracts - .get(&chain_ids[0]) - .expect("No contract for chain id"); - - let tx = contract - .commitHeaderRange(proof.bytes().into(), proof.public_values.to_vec().into()) - .into_transaction_request(); - // Relay the transaction to all chains. let tx_hash_futs: Vec<_> = chain_ids .into_iter() .map(|chain_id| { - // `send_transaction` takes ownership of the transaction. - let tx = tx.clone(); + let contract = self + .contracts + .get(&chain_id) + .expect("No contract for chain id"); + + let tx = contract + .commitHeaderRange( + proof.bytes().into(), + proof.public_values.to_vec().into(), + ) + .into_transaction_request(); async move { match self @@ -678,23 +676,18 @@ where next_auth_id, chain_ids ); - // All contract instances will produce the same calldata. - // The vector should have non-zero length. - let contract = self - .contracts - .get(&chain_ids[0]) - .expect("No contract for chain id"); - - let tx = contract - .rotate(proof.bytes().into(), proof.public_values.to_vec().into()) - .into_transaction_request(); - // Relay the transaction to all chains. let tx_hash_futs: Vec<_> = chain_ids .into_iter() .map(|chain_id| { - // `send_transaction` takes ownership of the transaction. - let tx = tx.clone(); + let contract = self + .contracts + .get(&chain_id) + .expect("No contract for chain id"); + + let tx = contract + .rotate(proof.bytes().into(), proof.public_values.to_vec().into()) + .into_transaction_request(); async move { match self diff --git a/services/src/input.rs b/services/src/input.rs index a3c843a..f96675e 100644 --- a/services/src/input.rs +++ b/services/src/input.rs @@ -256,10 +256,7 @@ impl RpcDataFetcher { .collect(); // Await all futures concurrently - let headers_batch: Vec
= join_all(header_futures) - .await - .into_iter() - .collect::>(); + let headers_batch: Vec
= join_all(header_futures).await; headers.extend_from_slice(&headers_batch); curr_block += MAX_CONCURRENT_WS_REQUESTS as u32; From 6f7d3b2fb3de00bc0334232c366e03dbea51230e Mon Sep 17 00:00:00 2001 From: nhtyy Date: Thu, 20 Feb 2025 09:44:13 -0800 Subject: [PATCH 17/18] fix: continue in loop on err --- script/bin/operator.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/script/bin/operator.rs b/script/bin/operator.rs index 4d08119..5dc2c1d 100644 --- a/script/bin/operator.rs +++ b/script/bin/operator.rs @@ -852,6 +852,7 @@ where error!("Error during `run_once`: {:?}", e); // Sleep for less time if theres an error. tokio::time::sleep(error_interval).await; + continue; } }, _ = tokio::time::sleep(Duration::from_secs(LOOP_TIMEOUT_MINS * 60)) => { From 7385367bdc8ff4c6fa61f3f6b7ed4a3a57e827bc Mon Sep 17 00:00:00 2001 From: nhtyy Date: Mon, 24 Feb 2025 09:03:30 -0800 Subject: [PATCH 18/18] fix: pr suggestions --- script/bin/operator.rs | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/script/bin/operator.rs b/script/bin/operator.rs index 5dc2c1d..9762475 100644 --- a/script/bin/operator.rs +++ b/script/bin/operator.rs @@ -77,10 +77,10 @@ sol! { type SP1VectorInstance = SP1Vector::SP1VectorInstance<(), P, N>; struct SP1VectorOperator { - pk: Arc, + pk: SP1ProvingKey, vk: SP1VerifyingKey, signer_mode: SignerMode, - tree_size: u32, + tree_size: Option, fetcher: RpcDataFetcher, prover: NetworkProver, contracts: HashMap>, @@ -117,12 +117,12 @@ where Self { fetcher: RpcDataFetcher::new().await, - pk: Arc::new(pk), + pk, vk, signer_mode, prover, contracts: HashMap::new(), - tree_size: 0, + tree_size: None, } } @@ -146,12 +146,14 @@ where .expect("Failed to get chain id"); // Register the first tree size. - if self.tree_size == 0 { - self.tree_size = tree_size; - } else if self.tree_size != tree_size { + if self.tree_size.is_none() { + self.tree_size = Some(tree_size); + } else if self.tree_size.unwrap() != tree_size { panic!( "Tree size mismatch! Expected {}, got {} for chain id {}", - self.tree_size, tree_size, chain_id + self.tree_size.unwrap(), + tree_size, + chain_id ); } @@ -265,7 +267,7 @@ where ) .await; - info!("maybe_block_to_step_to: {:?}", maybe_block_to_step_to); + info!("Target Block: {:?}", maybe_block_to_step_to); if let Some(block_to_step_to) = maybe_block_to_step_to { return Ok(Some(HeaderRangeRequestData { @@ -550,7 +552,10 @@ where let results = join_all(header_range_data_to_chain_id.into_iter().map( |(header_range_data, chain_ids)| async move { let proof = self - .request_header_range(self.tree_size, header_range_data) + .request_header_range( + self.tree_size.expect("Tree size not set"), + header_range_data, + ) .await?; info!(