From 16ee8c19c241e1c1f0b48434fe29a215a8ba3b24 Mon Sep 17 00:00:00 2001 From: clabby Date: Mon, 13 Jan 2025 13:43:59 -0500 Subject: [PATCH 1/3] feat(workspace): `kona-interop` crate --- Cargo.lock | 294 +++++++++++++++----------- Cargo.toml | 1 + README.md | 1 + crates/interop/Cargo.toml | 37 ++++ crates/interop/README.md | 8 + crates/interop/src/constants.rs | 15 ++ crates/interop/src/errors.rs | 71 +++++++ crates/interop/src/graph.rs | 344 +++++++++++++++++++++++++++++++ crates/interop/src/lib.rs | 37 ++++ crates/interop/src/message.rs | 114 ++++++++++ crates/interop/src/super_root.rs | 205 ++++++++++++++++++ crates/interop/src/test_util.rs | 197 ++++++++++++++++++ crates/interop/src/traits.rs | 33 +++ 13 files changed, 1234 insertions(+), 123 deletions(-) create mode 100644 crates/interop/Cargo.toml create mode 100644 crates/interop/README.md create mode 100644 crates/interop/src/constants.rs create mode 100644 crates/interop/src/errors.rs create mode 100644 crates/interop/src/graph.rs create mode 100644 crates/interop/src/lib.rs create mode 100644 crates/interop/src/message.rs create mode 100644 crates/interop/src/super_root.rs create mode 100644 crates/interop/src/test_util.rs create mode 100644 crates/interop/src/traits.rs diff --git a/Cargo.lock b/Cargo.lock index 1e787c00..f5a6213c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,9 +71,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy-chains" -version = "0.1.54" +version = "0.1.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38fdd69239714d7625cda1e3730773a3c1a8719d506370eb17bb0103b7c2e15" +checksum = "1e39f295f876b61a1222d937e1dd31f965e4a1acc3bba98e448dd7e84b1a4566" dependencies = [ "alloy-primitives", "num_enum", @@ -166,6 +166,18 @@ dependencies = [ "sha2", ] +[[package]] +name = "alloy-json-abi" +version = "0.8.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "731ea743b3d843bc657e120fb1d1e9cc94f5dab8107e35a82125a63e6420a102" +dependencies = [ + "alloy-primitives", + "alloy-sol-type-parser", + "serde", + "serde_json", +] + [[package]] name = "alloy-json-rpc" version = "0.9.2" @@ -176,7 +188,7 @@ dependencies = [ "alloy-sol-types", "serde", "serde_json", - "thiserror 2.0.10", + "thiserror 2.0.11", "tracing", ] @@ -202,7 +214,7 @@ dependencies = [ "futures-utils-wasm", "serde", "serde_json", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -278,7 +290,7 @@ dependencies = [ "schnellru", "serde", "serde_json", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tracing", "url", @@ -304,7 +316,7 @@ checksum = "5a833d97bf8a5f0f878daf2c8451fff7de7f9de38baa5a45d936ec718d81255a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -365,7 +377,7 @@ dependencies = [ "alloy-rpc-types-engine", "serde", "serde_with", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -411,7 +423,7 @@ dependencies = [ "itertools 0.13.0", "serde", "serde_json", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -436,7 +448,7 @@ dependencies = [ "auto_impl", "elliptic-curve", "k256", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -450,7 +462,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -466,7 +478,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", "syn-solidity", "tiny-keccak", ] @@ -482,19 +494,31 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", "syn-solidity", ] +[[package]] +name = "alloy-sol-type-parser" +version = "0.8.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1eda2711ab2e1fb517fc6e2ffa9728c9a232e296d16810810e6957b781a1b8bc" +dependencies = [ + "serde", + "winnow", +] + [[package]] name = "alloy-sol-types" version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3b478bc9c0c4737a04cd976accde4df7eba0bdc0d90ad6ff43d58bc93cf79c1" dependencies = [ + "alloy-json-abi", "alloy-primitives", "alloy-sol-macro", "const-hex", + "serde", ] [[package]] @@ -509,7 +533,7 @@ dependencies = [ "futures-utils-wasm", "serde", "serde_json", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tower", "tracing", @@ -614,6 +638,9 @@ name = "arbitrary" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +dependencies = [ + "derive_arbitrary", +] [[package]] name = "ark-ff" @@ -779,7 +806,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -790,7 +817,7 @@ checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -817,7 +844,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -865,7 +892,7 @@ version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "cexpr", "clang-sys", "itertools 0.12.1", @@ -876,7 +903,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -902,9 +929,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be" [[package]] name = "bitvec" @@ -992,7 +1019,7 @@ checksum = "523363cbe1df49b68215efdf500b103ac3b0fb4836aed6d15689a076eadb8fff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1050,9 +1077,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.7" +version = "1.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" +checksum = "c8293772165d9345bdaaa39b45b2109591e63fe5e6fbc23c6ff930a048aa310b" dependencies = [ "jobserver", "libc", @@ -1124,9 +1151,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.24" +version = "4.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9560b07a799281c7e0958b9296854d6fafd4c5f31444a7e5bb1ad6dde5ccf1bd" +checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783" dependencies = [ "clap_builder", "clap_derive", @@ -1134,9 +1161,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.24" +version = "4.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "874e0dd3eb68bf99058751ac9712f622e61e6f393a94f7128fa26e3f02f5c7cd" +checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121" dependencies = [ "anstream", "anstyle", @@ -1153,7 +1180,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1340,7 +1367,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1351,7 +1378,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1415,7 +1442,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1435,7 +1462,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", "unicode-xid", ] @@ -1468,7 +1495,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1539,7 +1566,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1559,7 +1586,7 @@ checksum = "3bf679796c0322556351f287a51b49e48f7c4986e727b5dd78c972d30e2e16cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1748,7 +1775,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -2148,7 +2175,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -2195,7 +2222,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -2295,9 +2322,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", @@ -2363,7 +2390,7 @@ dependencies = [ "serde", "serde_json", "spin", - "thiserror 2.0.10", + "thiserror 2.0.11", "tracing", ] @@ -2385,7 +2412,7 @@ dependencies = [ "proptest", "serde_json", "spin", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tracing", "tracing-subscriber", @@ -2404,7 +2431,7 @@ dependencies = [ "op-alloy-consensus", "op-alloy-genesis", "op-alloy-rpc-types-engine", - "thiserror 2.0.10", + "thiserror 2.0.11", "tracing", ] @@ -2428,7 +2455,7 @@ dependencies = [ "revm", "serde", "serde_json", - "thiserror 2.0.10", + "thiserror 2.0.11", "tracing", ] @@ -2469,6 +2496,23 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "kona-interop" +version = "0.1.0" +dependencies = [ + "alloy-consensus", + "alloy-primitives", + "alloy-rlp", + "alloy-sol-types", + "arbitrary", + "async-trait", + "op-alloy-consensus", + "rand", + "thiserror 2.0.11", + "tokio", + "tracing", +] + [[package]] name = "kona-mpt" version = "0.1.2" @@ -2487,7 +2531,7 @@ dependencies = [ "rand", "reqwest", "serde", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tracing-subscriber", ] @@ -2501,7 +2545,7 @@ dependencies = [ "async-trait", "rkyv", "serde", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tracing", ] @@ -2529,7 +2573,7 @@ dependencies = [ "serde", "serde_json", "spin", - "thiserror 2.0.10", + "thiserror 2.0.11", "tokio", "tracing", ] @@ -2542,7 +2586,7 @@ dependencies = [ "cfg-if", "kona-preimage", "linked_list_allocator", - "thiserror 2.0.10", + "thiserror 2.0.11", "tracing", ] @@ -2555,7 +2599,7 @@ dependencies = [ "kona-std-fpvm", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -2662,9 +2706,9 @@ dependencies = [ [[package]] name = "maili-protocol" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfcd6cd2bab854872c24d551308cb84df4c20db38593b4392d68cacd75d4c60c" +checksum = "428caa534dd054a449e64d8007d0fd0a15519d1033b272d37d02b74a29cf69f7" dependencies = [ "alloc-no-stdlib", "alloy-consensus", @@ -2681,16 +2725,16 @@ dependencies = [ "op-alloy-genesis", "rand", "serde", - "thiserror 2.0.10", + "thiserror 2.0.11", "tracing", "unsigned-varint", ] [[package]] name = "maili-registry" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d337c05c34ec926b920bd918d8c9c2b59a49791a0d644fea39db77ade185415" +checksum = "f5cd036e01822d5258af348714a88aebb4a689e23fc2b8c2668b96e59459253e" dependencies = [ "alloy-primitives", "lazy_static", @@ -2763,7 +2807,7 @@ checksum = "1bb5c1d8184f13f7d0ccbeeca0def2f9a181bce2624302793005f5ca8aa62e5e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -2930,7 +2974,7 @@ checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -2978,7 +3022,7 @@ dependencies = [ "alloy-serde", "derive_more", "serde", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -2993,7 +3037,7 @@ dependencies = [ "alloy-sol-types", "serde", "serde_repr", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -3019,7 +3063,7 @@ dependencies = [ "op-alloy-consensus", "op-alloy-protocol", "serde", - "thiserror 2.0.10", + "thiserror 2.0.11", ] [[package]] @@ -3028,7 +3072,7 @@ version = "0.10.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "cfg-if", "foreign-types", "libc", @@ -3045,7 +3089,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -3158,7 +3202,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror 2.0.10", + "thiserror 2.0.11", "ucd-trie", ] @@ -3179,7 +3223,7 @@ checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -3324,14 +3368,14 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -3344,7 +3388,7 @@ checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.6.0", + "bitflags 2.7.0", "lazy_static", "num-traits", "rand", @@ -3364,7 +3408,7 @@ checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -3384,7 +3428,7 @@ checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -3492,7 +3536,7 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", ] [[package]] @@ -3632,7 +3676,7 @@ dependencies = [ "alloy-eip7702 0.2.0", "alloy-primitives", "auto_impl", - "bitflags 2.6.0", + "bitflags 2.7.0", "bitvec", "c-kzg", "cfg-if", @@ -3712,7 +3756,7 @@ checksum = "beb382a4d9f53bd5c0be86b10d8179c3f8a14c30bf774ff77096ed6581e35981" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -3814,7 +3858,7 @@ version = "0.38.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "errno", "libc", "linux-raw-sys", @@ -3823,9 +3867,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.20" +version = "0.23.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" +checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8" dependencies = [ "once_cell", "rustls-pki-types", @@ -3958,7 +4002,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "core-foundation", "core-foundation-sys", "libc", @@ -4016,7 +4060,7 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -4040,7 +4084,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -4080,7 +4124,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -4253,7 +4297,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -4277,9 +4321,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "symbolic-common" -version = "12.12.4" +version = "12.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd33e73f154e36ec223c18013f7064a2c120f1162fc086ac9933542def186b00" +checksum = "bf08b42a6f9469bd8584daee39a1352c8133ccabc5151ccccb15896ef047d99a" dependencies = [ "debugid", "memmap2", @@ -4289,9 +4333,9 @@ dependencies = [ [[package]] name = "symbolic-demangle" -version = "12.12.4" +version = "12.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e51191290147f071777e37fe111800bb82a9059f9c95b19d2dd41bfeddf477" +checksum = "32f73b5a5bd4da72720c45756a2d11edf110116b87f998bda59b97be8c2c7cf1" dependencies = [ "cpp_demangle", "rustc-demangle", @@ -4311,9 +4355,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.95" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -4329,7 +4373,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -4349,7 +4393,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -4358,7 +4402,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "core-foundation", "system-configuration-sys", ] @@ -4404,11 +4448,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.10" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3ac7f54ca534db81081ef1c1e7f6ea8a3ef428d2fc069097c079443d24124d3" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ - "thiserror-impl 2.0.10", + "thiserror-impl 2.0.11", ] [[package]] @@ -4419,18 +4463,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] name = "thiserror-impl" -version = "2.0.10" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e9465d30713b56a37ede7185763c3492a91be2f5fa68d958c44e41ab9248beb" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -4541,7 +4585,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -4652,7 +4696,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -4781,9 +4825,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "b913a3b5fe84142e269d63cc62b64319ccaf89b748fc31fe025177f767a756c4" [[package]] name = "valuable" @@ -4839,34 +4883,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.49" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", @@ -4877,9 +4922,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4887,22 +4932,25 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasmtimer" @@ -4920,9 +4968,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -5073,9 +5121,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.22" +version = "0.6.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" dependencies = [ "memchr", ] @@ -5121,7 +5169,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", "synstructure", ] @@ -5143,7 +5191,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -5163,7 +5211,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", "synstructure", ] @@ -5184,7 +5232,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -5206,5 +5254,5 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] diff --git a/Cargo.toml b/Cargo.toml index 2a30a88e..bc4b6a37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ members = [ "crates/driver", "crates/executor", "crates/mpt", + "crates/interop", "crates/proof-sdk/*", "bin/*" ] diff --git a/README.md b/README.md index 0de8e07a..75ca030b 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ see the [SDK section of the book](https://op-rs.github.io/kona/sdk/intro.html). - [`executor`](./crates/executor): `no_std` stateless block executor for the [OP Stack][op-stack]. - [`derive`](./crates/derive): `no_std` compatible implementation of the [derivation pipeline][g-derivation-pipeline]. - [`driver`](./crates/driver): Stateful derivation pipeline driver. +- [`interop`](./crates/interop): Core functionality and primitives for the [Interop feature](https://specs.optimism.io/interop/overview.html) of the OP Stack. **Proof SDK** diff --git a/crates/interop/Cargo.toml b/crates/interop/Cargo.toml new file mode 100644 index 00000000..6b4bb916 --- /dev/null +++ b/crates/interop/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "kona-interop" +description = "Core functionality and primitives for the Interop feature of the OP Stack." +version = "0.1.0" +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true + +[lints] +workspace = true + +[dependencies] +# General +thiserror.workspace = true +async-trait.workspace = true +tracing.workspace = true + +# Alloy +alloy-primitives = { workspace = true, features = ["rlp"] } +alloy-sol-types = "0.8.18" +alloy-consensus.workspace = true +alloy-rlp.workspace = true +op-alloy-consensus.workspace = true + +# Arbitrary +arbitrary = { version = "1.4", features = ["derive"], optional = true } + +[dev-dependencies] +tokio = { workspace = true, features = ["full"] } +alloy-primitives = { workspace = true, features = ["rlp", "arbitrary"] } +arbitrary = { version = "1.4", features = ["derive"] } +rand.workspace = true + +[features] +arbitrary = ["dep:arbitrary", "alloy-primitives/arbitrary"] diff --git a/crates/interop/README.md b/crates/interop/README.md new file mode 100644 index 00000000..33f1a5e2 --- /dev/null +++ b/crates/interop/README.md @@ -0,0 +1,8 @@ +# `kona-interop` + +CI +Kona MPT +License +Codecov + +Core functionality and primitives for the [Interop feature](https://specs.optimism.io/interop/overview.html) of the OP Stack. diff --git a/crates/interop/src/constants.rs b/crates/interop/src/constants.rs new file mode 100644 index 00000000..2a174d5c --- /dev/null +++ b/crates/interop/src/constants.rs @@ -0,0 +1,15 @@ +//! Constants for the OP Stack interop protocol. + +use alloy_primitives::{address, Address}; + +/// The address of the L2 cross chain inbox predeploy proxy. +pub const CROSS_L2_INBOX_ADDRESS: Address = address!("4200000000000000000000000000000000000022"); + +/// The expiry window for relaying an initiating message (in seconds). +/// +pub const MESSAGE_EXPIRY_WINDOW: u64 = 180 * 24 * 60 * 60; + +/// The current version of the [SuperRoot] encoding format. +/// +/// [SuperRoot]: crate::SuperRoot +pub const SUPER_ROOT_VERSION: u8 = 1; diff --git a/crates/interop/src/errors.rs b/crates/interop/src/errors.rs new file mode 100644 index 00000000..3a78f073 --- /dev/null +++ b/crates/interop/src/errors.rs @@ -0,0 +1,71 @@ +//! Error types for the `kona-interop` crate. + +use alloc::vec::Vec; +use alloy_primitives::{Address, B256}; +use thiserror::Error; + +/// An error type for the [MessageGraph] struct. +/// +/// [MessageGraph]: crate::MessageGraph +#[derive(Debug, Clone, PartialEq, Eq, Error)] +pub enum MessageGraphError { + /// Dependency set is impossibly empty + #[error("Dependency set is impossibly empty")] + EmptyDependencySet, + /// Remote message not found + #[error("Remote message not found on chain ID {0} with message hash {1}")] + RemoteMessageNotFound(u64, B256), + /// Invalid message origin + #[error("Invalid message origin. Expected {0}, got {1}")] + InvalidMessageOrigin(Address, Address), + /// Invalid message payload hash + #[error("Invalid message hash. Expected {0}, got {1}")] + InvalidMessageHash(B256, B256), + /// Invalid message timestamp + #[error("Invalid message timestamp. Expected {0}, got {1}")] + InvalidMessageTimestamp(u64, u64), + /// Message is in the future + #[error("Message is in the future. Expected timestamp to be <= {0}, got {1}")] + MessageInFuture(u64, u64), + /// Invalid messages were found + #[error("Invalid messages found on chains: {0:?}")] + InvalidMessages(Vec), + /// Interop provider error + #[error("Interop provider: {0}")] + InteropProviderError(#[from] InteropProviderError), +} + +/// A [Result] alias for the [MessageGraphError] type. +pub type MessageGraphResult = core::result::Result; + +/// An error type for the [InteropProvider] trait. +/// +/// [InteropProvider]: crate::InteropProvider +#[derive(Debug, Clone, PartialEq, Eq, Error)] +pub enum InteropProviderError { + /// Unknown Chain ID + #[error("Unknown Chain ID")] + UnknownChainId, + /// Not found + #[error("Not found")] + NotFound, +} + +/// A [Result] alias for the [InteropProviderError] type. +pub type InteropProviderResult = core::result::Result; + +/// An error type for the [SuperRoot] struct's serialization and deserialization. +/// +/// [SuperRoot]: crate::SuperRoot +#[derive(Debug, Clone, PartialEq, Eq, Error)] +pub enum SuperRootError { + /// Invalid super root version byte + #[error("Invalid super root version byte")] + InvalidVersionByte, + /// Unexpected encoded super root length + #[error("Unexpected encoded super root length")] + UnexpectedLength, +} + +/// A [Result] alias for the [SuperRootError] type. +pub type SuperRootResult = core::result::Result; diff --git a/crates/interop/src/graph.rs b/crates/interop/src/graph.rs new file mode 100644 index 00000000..97e0648b --- /dev/null +++ b/crates/interop/src/graph.rs @@ -0,0 +1,344 @@ +//! Interop [MessageGraph]. + +use crate::{ + errors::{MessageGraphError, MessageGraphResult}, + message::{extract_executing_messages, EnrichedExecutingMessage}, + traits::InteropProvider, + RawMessagePayload, +}; +use alloc::vec::Vec; +use alloy_consensus::{Header, Sealed}; +use alloy_primitives::{hex, keccak256}; +use tracing::{info, warn}; + +/// The message graph represents a set of blocks at a given timestamp and the interop +/// dependencies between them. +/// +/// This structure is used to determine whether or not any interop messages are invalid within the +/// set of blocks within the graph. An "invalid message" is one that was relayed from one chain to +/// another, but the original [MessageIdentifier] is not present within the graph or from a +/// dependency referenced via the [InteropProvider] (or otherwise is invalid, such as being older +/// than the message expiry window). +/// +/// Message validity rules: +/// +/// [MessageIdentifier]: crate::MessageIdentifier +#[derive(Debug)] +pub struct MessageGraph

{ + /// The horizon timestamp is the highest timestamp of all blocks containing [ExecutingMessage]s + /// within the graph. + /// + /// [ExecutingMessage]: crate::ExecutingMessage + horizon_timestamp: u64, + /// The edges within the graph. + /// + /// These are derived from the transactions within the blocks. + messages: Vec, + /// The data provider for the graph. Required for fetching headers, receipts and remote + /// messages within history during resolution. + provider: P, +} + +impl

MessageGraph

+where + P: InteropProvider, +{ + /// Derives the edges from the blocks within the graph by scanning all receipts within the + /// blocks and searching for [ExecutingMessage]s. + /// + /// [ExecutingMessage]: crate::ExecutingMessage + pub async fn derive(blocks: &[(u64, Sealed

)], provider: P) -> MessageGraphResult { + info!( + target: "message-graph", + "Deriving message graph from {} blocks.", + blocks.len() + ); + + // Get the highest timestamp from the blocks. This serves as the horizon timestamp for the + // graph. + let horizon_timestamp = blocks + .iter() + .map(|(_, header)| header.inner().timestamp) + .max() + .ok_or(MessageGraphError::EmptyDependencySet)?; + + let mut messages = Vec::with_capacity(blocks.len()); + for (chain_id, header) in blocks.iter() { + let receipts = provider.receipts_by_hash(*chain_id, header.hash()).await?; + let executing_messages = extract_executing_messages(receipts.as_slice()); + + messages.extend( + executing_messages + .into_iter() + .map(|message| EnrichedExecutingMessage::new(message, *chain_id)), + ); + } + + info!( + target: "message-graph", + "Derived {} executing messages from {} blocks.", + messages.len(), + blocks.len() + ); + Ok(Self { horizon_timestamp, messages, provider }) + } + + /// Checks the validity of all messages within the graph. + pub async fn resolve(mut self) -> MessageGraphResult<()> { + info!( + target: "message-graph", + "Checking the message graph for invalid messages." + ); + + // Reduce the graph to remove all valid messages. + self.reduce().await?; + + // Check if the graph is now empty. If not, there are invalid messages. + if !self.messages.is_empty() { + // Collect the chain IDs for all blocks containing invalid messages. + let mut bad_block_chain_ids = + self.messages.into_iter().map(|e| e.executing_chain_id).collect::>(); + bad_block_chain_ids.dedup_by(|a, b| a == b); + + warn!( + target: "message-graph", + "Failed to reduce the message graph entirely. Invalid messages found in chains {}", + bad_block_chain_ids + .iter() + .map(|id| alloc::format!("{}", id)) + .collect::>() + .join(", ") + ); + + // Return an error with the chain IDs of the blocks containing invalid messages. + return Err(MessageGraphError::InvalidMessages(bad_block_chain_ids)); + } + + Ok(()) + } + + /// Attempts to remove as many edges from the graph as possible by resolving the dependencies + /// of each message. If a message cannot be resolved, it is considered invalid. After this + /// function is called, any outstanding messages are invalid. + async fn reduce(&mut self) -> MessageGraphResult<()> { + // Create a new vector to store invalid edges + let mut invalid_messages = Vec::with_capacity(self.messages.len()); + + // Prune all valid edges. + for message in core::mem::take(&mut self.messages) { + if let Err(e) = self.check_single_dependency(&message).await { + warn!( + target: "message-graph", + "Invalid ExecutingMessage found - relayed on chain {} with message hash {}.", + message.executing_chain_id, + hex::encode(message.inner.msgHash) + ); + warn!("Invalid message error: {}", e); + invalid_messages.push(message); + } + } + + info!( + target: "message-graph", + "Successfully reduced the message graph. {} invalid messages found.", + invalid_messages.len() + ); + + // Replace the old edges with the filtered list + self.messages = invalid_messages; + + Ok(()) + } + + /// Checks the dependency of a single [EnrichedExecutingMessage]. If the message's dependencies + /// are unavailable, the message is considered invalid and an [Err] is returned. + async fn check_single_dependency( + &self, + message: &EnrichedExecutingMessage, + ) -> MessageGraphResult<()> { + // ChainID Invariant: The chain id of the initiating message MUST be in the dependency set + // This is enforced implicitly by the graph constructor and the provider. + + // Timestamp invariant: The timestamp at the time of inclusion of the initiating message + // MUST be less than or equal to the timestamp of the executing message as well as greater + // than or equal to the Interop Start Timestamp. + if message.inner.id.timestamp.saturating_to::() > self.horizon_timestamp { + // TODO(interop): Also need to check for the interop start timestamp. Requires + // `RollupConfig`s for each chain. + return Err(MessageGraphError::MessageInFuture( + self.horizon_timestamp, + message.inner.id.timestamp.saturating_to(), + )); + } + + // Fetch the header & receipts for the message's claimed origin block on the remote chain. + let remote_header = self + .provider + .header_by_number( + message.inner.id.chainId.saturating_to(), + message.inner.id.blockNumber.saturating_to(), + ) + .await?; + let remote_receipts = self + .provider + .receipts_by_number( + message.inner.id.chainId.saturating_to(), + message.inner.id.blockNumber.saturating_to(), + ) + .await?; + + // Find the log that matches the message's claimed log index. Note that the + // log index is global to the block, so we chain the full block's logs together + // to find it. + let remote_log = remote_receipts + .iter() + .flat_map(|receipt| receipt.logs()) + .nth(message.inner.id.logIndex.saturating_to()) + .ok_or(MessageGraphError::RemoteMessageNotFound( + message.inner.id.chainId.to(), + message.inner.msgHash, + ))?; + + // Validate the message's origin is correct. + if remote_log.address != message.inner.id.origin { + return Err(MessageGraphError::InvalidMessageOrigin( + message.inner.id.origin, + remote_log.address, + )); + } + + // Validate that the message hash is correct. + let remote_message = RawMessagePayload::from(remote_log); + let remote_message_hash = keccak256(remote_message.as_ref()); + if remote_message_hash != message.inner.msgHash { + return Err(MessageGraphError::InvalidMessageHash( + message.inner.msgHash, + remote_message_hash, + )); + } + + // Validate that the timestamp of the block header containing the log is correct. + if remote_header.timestamp != message.inner.id.timestamp.saturating_to::() { + return Err(MessageGraphError::InvalidMessageTimestamp( + message.inner.id.timestamp.saturating_to::(), + remote_header.timestamp, + )); + } + + Ok(()) + } +} + +#[cfg(test)] +mod test { + use super::MessageGraph; + use crate::{test_util::SuperchainBuilder, MessageGraphError}; + use alloy_primitives::{hex, keccak256, Address}; + + const MESSAGE: [u8; 4] = hex!("deadbeef"); + + #[tokio::test] + async fn test_derive_and_reduce_simple_graph() { + let mut superchain = SuperchainBuilder::new(0); + + superchain.chain(1).add_initiating_message(MESSAGE.into()); + superchain.chain(2).add_executing_message(keccak256(MESSAGE), 0, 1, 0); + + let (headers, provider) = superchain.build(); + + let graph = MessageGraph::derive(headers.as_slice(), provider).await.unwrap(); + graph.resolve().await.unwrap(); + } + + #[tokio::test] + async fn test_derive_and_reduce_cyclical_graph() { + let mut superchain = SuperchainBuilder::new(0); + + superchain.chain(1).add_initiating_message(MESSAGE.into()).add_executing_message( + keccak256(MESSAGE), + 1, + 2, + 0, + ); + superchain + .chain(2) + .add_executing_message(keccak256(MESSAGE), 0, 1, 0) + .add_initiating_message(MESSAGE.into()); + + let (headers, provider) = superchain.build(); + + let graph = MessageGraph::derive(headers.as_slice(), provider).await.unwrap(); + graph.resolve().await.unwrap(); + } + + #[tokio::test] + async fn test_derive_and_reduce_simple_graph_remote_message_not_found() { + let mut superchain = SuperchainBuilder::new(0); + + superchain.chain(1); + superchain.chain(2).add_executing_message(keccak256(MESSAGE), 0, 1, 0); + + let (headers, provider) = superchain.build(); + + let graph = MessageGraph::derive(headers.as_slice(), provider).await.unwrap(); + assert_eq!(graph.resolve().await.unwrap_err(), MessageGraphError::InvalidMessages(vec![2])); + } + + #[tokio::test] + async fn test_derive_and_reduce_simple_graph_invalid_chain_id() { + let mut superchain = SuperchainBuilder::new(0); + + superchain.chain(1).add_initiating_message(MESSAGE.into()); + superchain.chain(2).add_executing_message(keccak256(MESSAGE), 0, 2, 0); + + let (headers, provider) = superchain.build(); + + let graph = MessageGraph::derive(headers.as_slice(), provider).await.unwrap(); + assert_eq!(graph.resolve().await.unwrap_err(), MessageGraphError::InvalidMessages(vec![2])); + } + + #[tokio::test] + async fn test_derive_and_reduce_simple_graph_invalid_log_index() { + let mut superchain = SuperchainBuilder::new(0); + + superchain.chain(1).add_initiating_message(MESSAGE.into()); + superchain.chain(2).add_executing_message(keccak256(MESSAGE), 1, 1, 0); + + let (headers, provider) = superchain.build(); + + let graph = MessageGraph::derive(headers.as_slice(), provider).await.unwrap(); + assert_eq!(graph.resolve().await.unwrap_err(), MessageGraphError::InvalidMessages(vec![2])); + } + + #[tokio::test] + async fn test_derive_and_reduce_simple_graph_invalid_message_hash() { + let mut superchain = SuperchainBuilder::new(0); + + superchain.chain(1).add_initiating_message(MESSAGE.into()); + superchain.chain(2).add_executing_message(keccak256(hex!("0badc0de")), 0, 1, 0); + + let (headers, provider) = superchain.build(); + + let graph = MessageGraph::derive(headers.as_slice(), provider).await.unwrap(); + assert_eq!(graph.resolve().await.unwrap_err(), MessageGraphError::InvalidMessages(vec![2])); + } + + #[tokio::test] + async fn test_derive_and_reduce_simple_graph_invalid_origin_address() { + let mut superchain = SuperchainBuilder::new(0); + + superchain.chain(1).add_initiating_message(MESSAGE.into()); + superchain.chain(2).add_executing_message_with_origin( + keccak256(MESSAGE), + Address::left_padding_from(&[0x01]), + 0, + 1, + 0, + ); + + let (headers, provider) = superchain.build(); + + let graph = MessageGraph::derive(headers.as_slice(), provider).await.unwrap(); + assert_eq!(graph.resolve().await.unwrap_err(), MessageGraphError::InvalidMessages(vec![2])); + } +} diff --git a/crates/interop/src/lib.rs b/crates/interop/src/lib.rs new file mode 100644 index 00000000..be4da320 --- /dev/null +++ b/crates/interop/src/lib.rs @@ -0,0 +1,37 @@ +#![doc = include_str!("../README.md")] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/op-rs/kona/main/assets/square.png", + html_favicon_url = "https://raw.githubusercontent.com/op-rs/kona/main/assets/favicon.ico" +)] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(not(any(test, feature = "arbitrary")), no_std)] + +extern crate alloc; + +mod graph; +pub use graph::MessageGraph; + +mod message; +pub use message::{ + extract_executing_messages, EnrichedExecutingMessage, ExecutingMessage, MessageIdentifier, + RawMessagePayload, +}; + +mod constants; +pub use constants::{CROSS_L2_INBOX_ADDRESS, MESSAGE_EXPIRY_WINDOW, SUPER_ROOT_VERSION}; + +mod traits; +pub use traits::InteropProvider; + +mod errors; +pub use errors::{ + InteropProviderError, InteropProviderResult, MessageGraphError, MessageGraphResult, + SuperRootError, SuperRootResult, +}; + +mod super_root; +pub use super_root::{OutputRootWithChain, SuperRoot}; + +#[cfg(test)] +mod test_util; diff --git a/crates/interop/src/message.rs b/crates/interop/src/message.rs new file mode 100644 index 00000000..d5c756d6 --- /dev/null +++ b/crates/interop/src/message.rs @@ -0,0 +1,114 @@ +//! Interop message primitives. +//! +//! +//! + +use crate::constants::CROSS_L2_INBOX_ADDRESS; +use alloc::{vec, vec::Vec}; +use alloy_primitives::{keccak256, Bytes, Log}; +use alloy_sol_types::{sol, SolEvent}; +use op_alloy_consensus::OpReceiptEnvelope; + +sol! { + /// @notice The struct for a pointer to a message payload in a remote (or local) chain. + #[derive(Default, Debug, PartialEq, Eq)] + struct MessageIdentifier { + address origin; + uint256 blockNumber; + uint256 logIndex; + uint256 timestamp; + uint256 chainId; + } + + /// @notice Emitted when a cross chain message is being executed. + /// @param msgHash Hash of message payload being executed. + /// @param id Encoded Identifier of the message. + #[derive(Default, Debug, PartialEq, Eq)] + event ExecutingMessage(bytes32 indexed msgHash, MessageIdentifier id); + + /// @notice Executes a cross chain message on the destination chain. + /// @param _id Identifier of the message. + /// @param _target Target address to call. + /// @param _message Message payload to call target with. + function executeMessage( + MessageIdentifier calldata _id, + address _target, + bytes calldata _message + ) external; +} + +/// A [RawMessagePayload] is the raw payload of an initiating message. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RawMessagePayload(Bytes); + +impl From<&Log> for RawMessagePayload { + fn from(log: &Log) -> Self { + let mut data = vec![0u8; log.topics().len() * 32 + log.data.data.len()]; + for (i, topic) in log.topics().iter().enumerate() { + data[i * 32..(i + 1) * 32].copy_from_slice(topic.as_ref()); + } + data[(log.topics().len() * 32)..].copy_from_slice(log.data.data.as_ref()); + data.into() + } +} + +impl From> for RawMessagePayload { + fn from(data: Vec) -> Self { + Self(Bytes::from(data)) + } +} + +impl From for RawMessagePayload { + fn from(bytes: Bytes) -> Self { + Self(bytes) + } +} + +impl From for Bytes { + fn from(payload: RawMessagePayload) -> Self { + payload.0 + } +} + +impl AsRef<[u8]> for RawMessagePayload { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl From for ExecutingMessage { + fn from(call: executeMessageCall) -> Self { + Self { id: call._id, msgHash: keccak256(call._message.as_ref()) } + } +} + +/// A wrapper type for [ExecutingMessage] containing the chain ID of the chain that the message was +/// executed on. +#[derive(Debug)] +pub struct EnrichedExecutingMessage { + /// The inner [ExecutingMessage]. + pub inner: ExecutingMessage, + /// The chain ID of the chain that the message was executed on. + pub executing_chain_id: u64, +} + +impl EnrichedExecutingMessage { + /// Create a new [EnrichedExecutingMessage] from an [ExecutingMessage] and a chain ID. + pub const fn new(inner: ExecutingMessage, executing_chain_id: u64) -> Self { + Self { inner, executing_chain_id } + } +} + +/// Extracts all [ExecutingMessage] logs from a list of [OpReceiptEnvelope]s. +pub fn extract_executing_messages(receipts: &[OpReceiptEnvelope]) -> Vec { + receipts.iter().fold(Vec::new(), |mut acc, envelope| { + let executing_messages = envelope.logs().iter().filter_map(|log| { + (log.address == CROSS_L2_INBOX_ADDRESS && log.topics().len() == 2) + .then(|| ExecutingMessage::decode_log_data(&log.data, true).ok()) + .flatten() + }); + + acc.extend(executing_messages); + acc + }) +} diff --git a/crates/interop/src/super_root.rs b/crates/interop/src/super_root.rs new file mode 100644 index 00000000..a8d3c802 --- /dev/null +++ b/crates/interop/src/super_root.rs @@ -0,0 +1,205 @@ +//! The [SuperRoot] type. +//! +//! Represents a snapshot of the state of the superchain at a given integer timestamp. + +use crate::{ + errors::{SuperRootError, SuperRootResult}, + SUPER_ROOT_VERSION, +}; +use alloc::vec::Vec; +use alloy_primitives::{keccak256, B256, U256}; +use alloy_rlp::{Buf, BufMut}; + +/// The [SuperRoot] is the snapshot of the superchain at a given timestamp. +#[derive(Debug, Clone, Eq, PartialEq)] +#[cfg_attr(any(feature = "arbitrary", test), derive(arbitrary::Arbitrary))] +pub struct SuperRoot { + /// The timestamp of the superchain snapshot, in seconds. + pub timestamp: u64, + /// The chain IDs and output root commitments of all chains within the dependency set. + pub output_roots: Vec, +} + +impl SuperRoot { + /// Create a new [SuperRoot] with the given timestamp and output roots. + pub fn new(timestamp: u64, mut output_roots: Vec) -> Self { + // Guarantee that the output roots are sorted by chain ID. + output_roots.sort_by_key(|r| r.chain_id); + Self { timestamp, output_roots } + } + + /// Decodes a [SuperRoot] from the given buffer. + pub fn decode(buf: &mut &[u8]) -> SuperRootResult { + if buf.is_empty() { + return Err(SuperRootError::UnexpectedLength); + } + + let version = buf[0]; + if version != SUPER_ROOT_VERSION { + return Err(SuperRootError::InvalidVersionByte); + } + buf.advance(1); + + if buf.len() < 8 { + return Err(SuperRootError::UnexpectedLength); + } + let timestamp = u64::from_be_bytes(buf[0..8].try_into().unwrap()); + buf.advance(8); + + let mut output_roots = Vec::new(); + while !buf.is_empty() { + if buf.len() < 64 { + return Err(SuperRootError::UnexpectedLength); + } + + let chain_id = U256::from_be_bytes::<32>(buf[0..32].try_into().unwrap()); + buf.advance(32); + let output_root = B256::from_slice(&buf[0..32]); + buf.advance(32); + output_roots.push(OutputRootWithChain::new(chain_id.to(), output_root)); + } + + Ok(Self { timestamp, output_roots }) + } + + /// Encode the [SuperRoot] into the given buffer. + pub fn encode(&self, out: &mut dyn BufMut) { + out.put_u8(SUPER_ROOT_VERSION); + + out.put_u64(self.timestamp); + for output_root in &self.output_roots { + out.put_slice(U256::from(output_root.chain_id).to_be_bytes::<32>().as_slice()); + out.put_slice(output_root.output_root.as_slice()); + } + } + + /// Returns the encoded length of the [SuperRoot]. + pub fn encoded_length(&self) -> usize { + 1 + 8 + 64 * self.output_roots.len() + } + + /// Hashes the encoded [SuperRoot] using [keccak256]. + pub fn hash(&self) -> B256 { + let mut rlp_buf = Vec::with_capacity(self.encoded_length()); + self.encode(&mut rlp_buf); + keccak256(&rlp_buf) + } +} + +/// A wrapper around an output root hash with the chain ID it belongs to. +#[derive(Debug, Clone, Eq, PartialEq)] +#[cfg_attr(any(feature = "arbitrary", test), derive(arbitrary::Arbitrary))] +pub struct OutputRootWithChain { + /// The chain ID of the output root. + pub chain_id: u64, + /// The output root hash. + pub output_root: B256, +} + +impl OutputRootWithChain { + /// Create a new [OutputRootWithChain] with the given chain ID and output root hash. + pub const fn new(chain_id: u64, output_root: B256) -> Self { + Self { chain_id, output_root } + } +} + +#[cfg(test)] +mod test { + use crate::{errors::SuperRootError, SUPER_ROOT_VERSION}; + + use super::{OutputRootWithChain, SuperRoot}; + use alloy_primitives::{b256, B256}; + use arbitrary::Arbitrary; + use rand::Rng; + + #[test] + fn test_super_root_sorts_outputs() { + let super_root = SuperRoot::new( + 10, + vec![ + (OutputRootWithChain::new(3, B256::default())), + (OutputRootWithChain::new(2, B256::default())), + (OutputRootWithChain::new(1, B256::default())), + ], + ); + + assert!(super_root.output_roots.is_sorted_by_key(|r| r.chain_id)); + } + + #[test] + fn test_super_root_empty_buf() { + let buf: Vec = Vec::new(); + assert_eq!( + SuperRoot::decode(&mut buf.as_slice()).unwrap_err(), + SuperRootError::UnexpectedLength + ); + } + + #[test] + fn test_super_root_invalid_version() { + let buf = vec![0xFF]; + assert_eq!( + SuperRoot::decode(&mut buf.as_slice()).unwrap_err(), + SuperRootError::InvalidVersionByte + ); + } + + #[test] + fn test_super_root_invalid_length_at_timestamp() { + let buf = vec![SUPER_ROOT_VERSION, 0x00]; + assert_eq!( + SuperRoot::decode(&mut buf.as_slice()).unwrap_err(), + SuperRootError::UnexpectedLength + ); + } + + #[test] + fn test_super_root_invalid_length_malformed_output_roots() { + let buf = [&[SUPER_ROOT_VERSION], 64u64.to_be_bytes().as_ref(), &[0xbe, 0xef]].concat(); + assert_eq!( + SuperRoot::decode(&mut buf.as_slice()).unwrap_err(), + SuperRootError::UnexpectedLength + ); + } + + #[test] + fn test_static_hash_super_root() { + const EXPECTED: B256 = + b256!("0980033cbf4337f614a2401ab7efbfdc66ab647812f1c98d891d92ddfb376541"); + + let super_root = SuperRoot::new( + 10, + vec![ + (OutputRootWithChain::new(1, B256::default())), + (OutputRootWithChain::new(2, B256::default())), + ], + ); + assert_eq!(super_root.hash(), EXPECTED); + } + + #[test] + fn test_static_super_root_roundtrip() { + let super_root = SuperRoot::new( + 10, + vec![ + (OutputRootWithChain::new(1, B256::default())), + (OutputRootWithChain::new(2, B256::default())), + ], + ); + + let mut rlp_buf = Vec::with_capacity(super_root.encoded_length()); + super_root.encode(&mut rlp_buf); + assert_eq!(super_root, SuperRoot::decode(&mut rlp_buf.as_slice()).unwrap()); + } + + #[test] + fn test_arbitrary_super_root_roundtrip() { + let mut bytes = [0u8; 1024]; + rand::thread_rng().fill(bytes.as_mut_slice()); + let super_root = SuperRoot::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap(); + + let mut rlp_buf = Vec::with_capacity(super_root.encoded_length()); + super_root.encode(&mut rlp_buf); + assert_eq!(super_root, SuperRoot::decode(&mut rlp_buf.as_slice()).unwrap()); + } +} diff --git a/crates/interop/src/test_util.rs b/crates/interop/src/test_util.rs new file mode 100644 index 00000000..ab6a1627 --- /dev/null +++ b/crates/interop/src/test_util.rs @@ -0,0 +1,197 @@ +//! Test utilities for `kona-interop`. + +#![allow(missing_docs, unreachable_pub)] + +use crate::{ + errors::InteropProviderResult, traits::InteropProvider, ExecutingMessage, MessageIdentifier, + CROSS_L2_INBOX_ADDRESS, +}; +use alloy_consensus::{Header, Receipt, ReceiptWithBloom, Sealed}; +use alloy_primitives::{map::HashMap, Address, Bytes, Log, LogData, B256, U256}; +use alloy_sol_types::{SolEvent, SolValue}; +use async_trait::async_trait; +use op_alloy_consensus::OpReceiptEnvelope; + +#[derive(Debug, Clone, Default)] +pub(crate) struct MockInteropProvider { + pub headers: HashMap>>, + pub receipts: HashMap>>, +} + +impl MockInteropProvider { + pub const fn new( + headers: HashMap>>, + receipts: HashMap>>, + ) -> Self { + Self { headers, receipts } + } +} + +#[async_trait] +impl InteropProvider for MockInteropProvider { + /// Fetch a [Header] by its number. + async fn header_by_number(&self, chain_id: u64, number: u64) -> InteropProviderResult
{ + Ok(self + .headers + .get(&chain_id) + .and_then(|headers| headers.get(&number)) + .unwrap() + .inner() + .clone()) + } + + /// Fetch a [Header] by its hash. + async fn header_by_hash(&self, chain_id: u64, hash: B256) -> InteropProviderResult
{ + Ok(self + .headers + .get(&chain_id) + .and_then(|headers| headers.values().find(|header| header.hash() == hash)) + .unwrap() + .inner() + .clone()) + } + + /// Fetch all receipts for a given block by number. + async fn receipts_by_number( + &self, + chain_id: u64, + number: u64, + ) -> InteropProviderResult> { + Ok(self.receipts.get(&chain_id).and_then(|receipts| receipts.get(&number)).unwrap().clone()) + } + + /// Fetch all receipts for a given block by hash. + async fn receipts_by_hash( + &self, + chain_id: u64, + block_hash: B256, + ) -> InteropProviderResult> { + Ok(self + .receipts + .get(&chain_id) + .and_then(|receipts| { + let headers = self.headers.get(&chain_id).unwrap(); + let number = + headers.values().find(|header| header.hash() == block_hash).unwrap().number; + receipts.get(&number) + }) + .unwrap() + .clone()) + } +} + +pub struct SuperchainBuilder { + chains: HashMap, + timestamp: u64, +} + +pub struct ChainBuilder { + header: Header, + receipts: Vec, +} + +impl SuperchainBuilder { + pub fn new(timestamp: u64) -> Self { + Self { chains: HashMap::new(), timestamp } + } + + pub fn chain(&mut self, chain_id: u64) -> &mut ChainBuilder { + self.chains.entry(chain_id).or_insert_with(|| ChainBuilder::new(self.timestamp)) + } + + /// Builds the scenario into the format needed for testing + pub fn build(self) -> (Vec<(u64, Sealed
)>, MockInteropProvider) { + let mut headers_map = HashMap::new(); + let mut receipts_map = HashMap::new(); + let mut sealed_headers = Vec::new(); + + for (chain_id, chain) in self.chains { + let header = chain.header; + let header_hash = header.hash_slow(); + let sealed_header = header.seal(header_hash); + + let mut chain_headers = HashMap::new(); + chain_headers.insert(0, sealed_header.clone()); + headers_map.insert(chain_id, chain_headers); + + let mut chain_receipts = HashMap::new(); + chain_receipts.insert(0, chain.receipts); + receipts_map.insert(chain_id, chain_receipts); + + sealed_headers.push((chain_id, sealed_header)); + } + + (sealed_headers, MockInteropProvider::new(headers_map, receipts_map)) + } +} + +impl ChainBuilder { + pub fn new(timestamp: u64) -> Self { + Self { header: Header { timestamp, ..Default::default() }, receipts: Vec::new() } + } + + pub fn add_initiating_message(&mut self, message_data: Bytes) -> &mut Self { + let receipt = OpReceiptEnvelope::Eip1559(ReceiptWithBloom { + receipt: Receipt { + logs: vec![Log { + address: Address::ZERO, + data: LogData::new(vec![], message_data).unwrap(), + }], + ..Default::default() + }, + ..Default::default() + }); + self.receipts.push(receipt); + self + } + + pub fn add_executing_message( + &mut self, + message_hash: B256, + origin_log_index: u64, + origin_chain_id: u64, + origin_timestamp: u64, + ) -> &mut Self { + self.add_executing_message_with_origin( + message_hash, + Address::ZERO, + origin_log_index, + origin_chain_id, + origin_timestamp, + ) + } + + pub fn add_executing_message_with_origin( + &mut self, + message_hash: B256, + origin_address: Address, + origin_log_index: u64, + origin_chain_id: u64, + origin_timestamp: u64, + ) -> &mut Self { + let receipt = OpReceiptEnvelope::Eip1559(ReceiptWithBloom { + receipt: Receipt { + logs: vec![Log { + address: CROSS_L2_INBOX_ADDRESS, + data: LogData::new( + vec![ExecutingMessage::SIGNATURE_HASH, message_hash], + MessageIdentifier { + origin: origin_address, + blockNumber: U256::ZERO, + logIndex: U256::from(origin_log_index), + timestamp: U256::from(origin_timestamp), + chainId: U256::from(origin_chain_id), + } + .abi_encode() + .into(), + ) + .unwrap(), + }], + ..Default::default() + }, + ..Default::default() + }); + self.receipts.push(receipt); + self + } +} diff --git a/crates/interop/src/traits.rs b/crates/interop/src/traits.rs new file mode 100644 index 00000000..dde157ae --- /dev/null +++ b/crates/interop/src/traits.rs @@ -0,0 +1,33 @@ +//! Traits for the `kona-interop` crate. + +use crate::errors::InteropProviderResult; +use alloc::{boxed::Box, vec::Vec}; +use alloy_consensus::Header; +use alloy_primitives::B256; +use async_trait::async_trait; +use op_alloy_consensus::OpReceiptEnvelope; + +/// Describes the interface of the interop data provider. This provider is multiplexed over several +/// chains, with each method consuming a chain ID to determine the target chain. +#[async_trait] +pub trait InteropProvider { + /// Fetch a [Header] by its number. + async fn header_by_number(&self, chain_id: u64, number: u64) -> InteropProviderResult
; + + /// Fetch a [Header] by its hash. + async fn header_by_hash(&self, chain_id: u64, hash: B256) -> InteropProviderResult
; + + /// Fetch all receipts for a given block by number. + async fn receipts_by_number( + &self, + chain_id: u64, + number: u64, + ) -> InteropProviderResult>; + + /// Fetch all receipts for a given block by hash. + async fn receipts_by_hash( + &self, + chain_id: u64, + block_hash: B256, + ) -> InteropProviderResult>; +} From 8dac1b99eff5cfd2af3da256b7201fb764f05ceb Mon Sep 17 00:00:00 2001 From: clabby Date: Mon, 13 Jan 2025 13:47:11 -0500 Subject: [PATCH 2/3] op-rs! --- crates/interop/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/interop/README.md b/crates/interop/README.md index 33f1a5e2..4d213035 100644 --- a/crates/interop/README.md +++ b/crates/interop/README.md @@ -1,8 +1,8 @@ # `kona-interop` -CI +CI Kona MPT -License -Codecov +License +Codecov Core functionality and primitives for the [Interop feature](https://specs.optimism.io/interop/overview.html) of the OP Stack. From 497a0c9f884110a1d15f8f9e1f09157fd0a28591 Mon Sep 17 00:00:00 2001 From: clabby Date: Mon, 13 Jan 2025 13:55:18 -0500 Subject: [PATCH 3/3] deps --- Cargo.lock | 24 ------------------------ Cargo.toml | 1 + crates/interop/Cargo.toml | 2 +- 3 files changed, 2 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f5a6213c..e54ab572 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -166,18 +166,6 @@ dependencies = [ "sha2", ] -[[package]] -name = "alloy-json-abi" -version = "0.8.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "731ea743b3d843bc657e120fb1d1e9cc94f5dab8107e35a82125a63e6420a102" -dependencies = [ - "alloy-primitives", - "alloy-sol-type-parser", - "serde", - "serde_json", -] - [[package]] name = "alloy-json-rpc" version = "0.9.2" @@ -498,27 +486,15 @@ dependencies = [ "syn-solidity", ] -[[package]] -name = "alloy-sol-type-parser" -version = "0.8.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1eda2711ab2e1fb517fc6e2ffa9728c9a232e296d16810810e6957b781a1b8bc" -dependencies = [ - "serde", - "winnow", -] - [[package]] name = "alloy-sol-types" version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3b478bc9c0c4737a04cd976accde4df7eba0bdc0d90ad6ff43d58bc93cf79c1" dependencies = [ - "alloy-json-abi", "alloy-primitives", "alloy-sol-macro", "const-hex", - "serde", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index bc4b6a37..c7b521fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,6 +92,7 @@ alloy-node-bindings = { version = "0.9.2", default-features = false } alloy-transport-http = { version = "0.9.2", default-features = false } alloy-rpc-types-engine = { version = "0.9.2", default-features = false } alloy-rpc-types-beacon = { version = "0.9.2", default-features = false } +alloy-sol-types = { version = "0.8.18", default-features = false } # OP Alloy op-alloy-genesis = { version = "0.9.2", default-features = false } diff --git a/crates/interop/Cargo.toml b/crates/interop/Cargo.toml index 6b4bb916..adaabd1d 100644 --- a/crates/interop/Cargo.toml +++ b/crates/interop/Cargo.toml @@ -19,7 +19,7 @@ tracing.workspace = true # Alloy alloy-primitives = { workspace = true, features = ["rlp"] } -alloy-sol-types = "0.8.18" +alloy-sol-types.workspace = true alloy-consensus.workspace = true alloy-rlp.workspace = true op-alloy-consensus.workspace = true