From c43c8451b6b92e40b2d23a030461fee43440602d Mon Sep 17 00:00:00 2001 From: dancoombs Date: Tue, 10 Oct 2023 11:16:43 -0400 Subject: [PATCH] feat(p2p): add rpc messages and codec --- .github/codecov.yaml | 4 + Cargo.lock | 1854 ++++++++++++++++++++-- Makefile | 9 +- crates/network/Cargo.toml | 28 + crates/network/src/lib.rs | 29 + crates/network/src/rpc/handler/codec.rs | 420 +++++ crates/network/src/rpc/handler/mod.rs | 17 + crates/network/src/rpc/handler/serde.rs | 554 +++++++ crates/network/src/rpc/handler/snappy.rs | 210 +++ crates/network/src/rpc/message.rs | 214 +++ crates/network/src/rpc/mod.rs | 16 + crates/network/src/rpc/protocol.rs | 142 ++ 12 files changed, 3358 insertions(+), 139 deletions(-) create mode 100644 crates/network/Cargo.toml create mode 100644 crates/network/src/lib.rs create mode 100644 crates/network/src/rpc/handler/codec.rs create mode 100644 crates/network/src/rpc/handler/mod.rs create mode 100644 crates/network/src/rpc/handler/serde.rs create mode 100644 crates/network/src/rpc/handler/snappy.rs create mode 100644 crates/network/src/rpc/message.rs create mode 100644 crates/network/src/rpc/mod.rs create mode 100644 crates/network/src/rpc/protocol.rs diff --git a/.github/codecov.yaml b/.github/codecov.yaml index aa7b9fc7c..827a4ebd8 100644 --- a/.github/codecov.yaml +++ b/.github/codecov.yaml @@ -52,3 +52,7 @@ component_management: name: utils paths: - crates/utils/** + - component_id: network + name: network + paths: + - crates/network/** diff --git a/Cargo.lock b/Cargo.lock index 18682ef2f..b55add5f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,6 +27,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aead" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +dependencies = [ + "generic-array", +] + [[package]] name = "aes" version = "0.7.5" @@ -50,6 +59,20 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "aes-gcm" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" +dependencies = [ + "aead", + "aes 0.7.5", + "cipher 0.3.0", + "ctr 0.8.0", + "ghash", + "subtle", +] + [[package]] name = "ahash" version = "0.8.3" @@ -133,6 +156,12 @@ version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + [[package]] name = "arrayvec" version = "0.7.2" @@ -148,6 +177,51 @@ dependencies = [ "term", ] +[[package]] +name = "asn1-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time 0.3.20", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.107", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.107", +] + +[[package]] +name = "asn1_der" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" + [[package]] name = "async-channel" version = "1.9.0" @@ -204,7 +278,7 @@ dependencies = [ "polling", "rustix 0.37.3", "slab", - "socket2", + "socket2 0.4.9", "waker-fn", ] @@ -293,6 +367,19 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "asynchronous-codec" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4057f2c32adbb2fc158e22fb38433c8e9bbf76b75a4732c7c0cbaf695fb65568" +dependencies = [ + "bytes", + "futures-sink", + "futures-util", + "memchr", + "pin-project-lite", +] + [[package]] name = "atomic-waker" version = "1.1.1" @@ -388,6 +475,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base-x" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + [[package]] name = "base16ct" version = "0.2.0" @@ -499,6 +592,15 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "block-buffer" version = "0.9.0" @@ -541,6 +643,15 @@ dependencies = [ "sha2 0.9.9", ] +[[package]] +name = "bs58" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" +dependencies = [ + "tinyvec", +] + [[package]] name = "bumpalo" version = "3.12.0" @@ -645,6 +756,31 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chacha20" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6" +dependencies = [ + "cfg-if", + "cipher 0.3.0", + "cpufeatures", + "zeroize", +] + +[[package]] +name = "chacha20poly1305" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18446b09be63d457bbec447509e85f662f32952b035ce892290396bc0b0cff5" +dependencies = [ + "aead", + "chacha20", + "cipher 0.3.0", + "poly1305", + "zeroize", +] + [[package]] name = "chrono" version = "0.4.24" @@ -787,15 +923,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b30a84aab436fcb256a2ab3c80663d8aec686e6bae12827bb05fef3e1e439c9f" dependencies = [ "bincode", - "bs58", + "bs58 0.4.0", "coins-core", - "digest 0.10.6", - "getrandom", + "digest 0.10.7", + "getrandom 0.2.8", "hmac 0.12.1", "k256", "lazy_static", "serde", - "sha2 0.10.6", + "sha2 0.10.8", "thiserror", ] @@ -807,12 +943,12 @@ checksum = "84f4d04ee18e58356accd644896aeb2094ddeafb6a713e056cef0c0a8e468c15" dependencies = [ "bitvec 0.17.4", "coins-bip32", - "getrandom", + "getrandom 0.2.8", "hmac 0.12.1", "once_cell", "pbkdf2 0.12.1", "rand", - "sha2 0.10.6", + "sha2 0.10.8", "thiserror", ] @@ -824,14 +960,14 @@ checksum = "9b949a1c63fb7eb591eb7ba438746326aedf0ae843e51ec92ba6bec5bb382c4f" dependencies = [ "base64 0.21.0", "bech32", - "bs58", - "digest 0.10.6", + "bs58 0.4.0", + "digest 0.10.7", "generic-array", "hex", "ripemd", "serde", "serde_derive", - "sha2 0.10.6", + "sha2 0.10.8", "sha3", "thiserror", ] @@ -949,11 +1085,20 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] @@ -1023,7 +1168,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c2538c4e68e52548bacb3e83ac549f903d44f011ac9d5abb5e132e67d0808f7" dependencies = [ "generic-array", - "rand_core", + "rand_core 0.6.4", "subtle", "zeroize", ] @@ -1038,6 +1183,16 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + [[package]] name = "crypto-mac" version = "0.11.1" @@ -1049,13 +1204,12 @@ dependencies = [ ] [[package]] -name = "ctor" -version = "0.1.26" +name = "ctr" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" +checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" dependencies = [ - "quote", - "syn 1.0.107", + "cipher 0.3.0", ] [[package]] @@ -1067,6 +1221,47 @@ dependencies = [ "cipher 0.4.3", ] +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "platforms", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + [[package]] name = "cxx" version = "1.0.92" @@ -1111,14 +1306,38 @@ dependencies = [ "syn 1.0.107", ] +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core 0.13.4", + "darling_macro 0.13.4", +] + [[package]] name = "darling" version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.20.3", + "darling_macro 0.20.3", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.107", ] [[package]] @@ -1135,13 +1354,24 @@ dependencies = [ "syn 2.0.32", ] +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core 0.13.4", + "quote", + "syn 1.0.107", +] + [[package]] name = "darling_macro" version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ - "darling_core", + "darling_core 0.20.3", "quote", "syn 2.0.32", ] @@ -1152,6 +1382,26 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +[[package]] +name = "data-encoding-macro" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c904b33cc60130e1aeea4956ab803d08a3f4a0ca82d64ed757afac3891f2bb99" +dependencies = [ + "data-encoding", + "data-encoding-macro-internal", +] + +[[package]] +name = "data-encoding-macro-internal" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fdf3fce3ce863539ec1d7fd1b6dcc3c645663376b43ed376bbf887733e4f772" +dependencies = [ + "data-encoding", + "syn 1.0.107", +] + [[package]] name = "der" version = "0.7.4" @@ -1162,6 +1412,31 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der-parser" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.107", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -1208,9 +1483,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.3", "crypto-common", @@ -1238,6 +1513,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + [[package]] name = "dotenv" version = "0.15.0" @@ -1250,6 +1536,12 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + [[package]] name = "dunce" version = "1.0.3" @@ -1268,11 +1560,35 @@ dependencies = [ "signature", ] +[[package]] +name = "ed25519" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60f6d271ca33075c88028be6f04d502853d63a5ece419d269c15315d4fc1cf1d" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7277392b266383ef8396db7fdeb1e77b6c52fed775f5df15bb24f35b72156980" +dependencies = [ + "curve25519-dalek 4.1.1", + "ed25519", + "rand_core 0.6.4", + "serde", + "sha2 0.10.8", + "zeroize", +] + [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "elliptic-curve" @@ -1282,12 +1598,12 @@ checksum = "75c71eaa367f2e5d556414a8eea812bc62985c879748d6403edabd9cb03f16e7" dependencies = [ "base16ct", "crypto-bigint", - "digest 0.10.6", + "digest 0.10.7", "ff", "generic-array", "group", "pkcs8", - "rand_core", + "rand_core 0.6.4", "sec1", "subtle", "zeroize", @@ -1341,6 +1657,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "enum-as-inner" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 1.0.107", +] + [[package]] name = "enum_dispatch" version = "0.3.12" @@ -1398,8 +1726,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab" dependencies = [ "aes 0.8.2", - "ctr", - "digest 0.10.6", + "ctr 0.9.2", + "digest 0.10.7", "hex", "hmac 0.12.1", "pbkdf2 0.11.0", @@ -1407,7 +1735,7 @@ dependencies = [ "scrypt", "serde", "serde_json", - "sha2 0.10.6", + "sha2 0.10.8", "sha3", "thiserror", "uuid", @@ -1462,16 +1790,64 @@ dependencies = [ ] [[package]] -name = "ethers" -version = "2.0.8" +name = "ethereum_hashing" +version = "1.0.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b4026b97da8281276744741fac7eb385da905f6093c583331fa2953fdd4253" +checksum = "233dc6f434ce680dbabf4451ee3380cec46cb3c45d66660445a435619710dd35" dependencies = [ - "ethers-addressbook", - "ethers-contract", - "ethers-core", - "ethers-etherscan", - "ethers-middleware", + "cpufeatures", + "lazy_static", + "ring", + "sha2 0.10.8", +] + +[[package]] +name = "ethereum_serde_utils" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f8cb04ea380a33e9c269fa5f8df6f2d63dee19728235f3e639e7674e038686a" +dependencies = [ + "ethereum-types", + "hex", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "ethereum_ssz" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e61ffea29f26e8249d35128a82ec8d3bd4fbc80179ea5f5e5e3daafef6a80fcb" +dependencies = [ + "ethereum-types", + "itertools 0.10.5", + "smallvec", +] + +[[package]] +name = "ethereum_ssz_derive" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6085d7fd3cf84bd2b8fec150d54c8467fb491d8db9c460607c5534f653a0ee38" +dependencies = [ + "darling 0.13.4", + "proc-macro2", + "quote", + "syn 1.0.107", +] + +[[package]] +name = "ethers" +version = "2.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b4026b97da8281276744741fac7eb385da905f6093c583331fa2953fdd4253" +dependencies = [ + "ethers-addressbook", + "ethers-contract", + "ethers-core", + "ethers-etherscan", + "ethers-middleware", "ethers-providers", "ethers-signers", "ethers-solc", @@ -1673,7 +2049,7 @@ dependencies = [ "rand", "rusoto_core", "rusoto_kms", - "sha2 0.10.6", + "sha2 0.10.8", "spki", "thiserror", "tracing", @@ -1741,10 +2117,16 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ - "rand_core", + "rand_core 0.6.4", "subtle", ] +[[package]] +name = "fiat-crypto" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0870c84016d4b481be5c9f323c24f65e31e901ae618f0e80f4308fb00de1d2d" + [[package]] name = "fixed-hash" version = "0.8.0" @@ -1859,6 +2241,7 @@ dependencies = [ "futures-core", "futures-task", "futures-util", + "num_cpus", ] [[package]] @@ -1903,6 +2286,16 @@ dependencies = [ "syn 2.0.32", ] +[[package]] +name = "futures-rustls" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd3cf68c183738046838e300353e4716c674dc5e56890de4826801a6622a28" +dependencies = [ + "futures-io", + "rustls 0.21.7", +] + [[package]] name = "futures-sink" version = "0.3.28" @@ -1963,6 +2356,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + [[package]] name = "getrandom" version = "0.2.8" @@ -1976,6 +2380,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "ghash" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "gimli" version = "0.27.3" @@ -2041,7 +2455,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -2130,13 +2544,32 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac 0.12.1", +] + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac 0.8.0", + "digest 0.9.0", +] + [[package]] name = "hmac" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" dependencies = [ - "crypto-mac", + "crypto-mac 0.11.1", "digest 0.9.0", ] @@ -2146,7 +2579,18 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", +] + +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array", + "hmac 0.8.1", ] [[package]] @@ -2158,6 +2602,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + [[package]] name = "http" version = "0.2.9" @@ -2209,7 +2664,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -2288,6 +2743,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "idna" version = "0.4.0" @@ -2298,6 +2764,35 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "if-addrs" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc0fa01ffc752e9dbc72818cdb072cd028b86be5e09dd04c5a643704fe101a9" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "if-watch" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9465340214b296cd17a0009acdb890d6160010b8adf8f78a00d0d7ab270f79f" +dependencies = [ + "async-io", + "core-foundation", + "fnv", + "futures", + "if-addrs", + "ipnet", + "log", + "rtnetlink", + "system-configuration", + "tokio", + "windows 0.34.0", +] + [[package]] name = "impl-codec" version = "0.6.0" @@ -2404,6 +2899,18 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "ipconfig" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" +dependencies = [ + "socket2 0.5.4", + "widestring", + "windows-sys 0.48.0", + "winreg 0.50.0", +] + [[package]] name = "ipnet" version = "2.7.1" @@ -2635,7 +3142,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "once_cell", - "sha2 0.10.6", + "sha2 0.10.8", "signature", ] @@ -2714,105 +3221,488 @@ dependencies = [ ] [[package]] -name = "libproc" -version = "0.13.0" +name = "libp2p" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b18cbf29f8ff3542ba22bdce9ac610fcb75d74bb4e2b306b2a2762242025b4f" +checksum = "32d07d1502a027366d55afe187621c2d7895dc111a3df13b35fed698049681d7" dependencies = [ - "bindgen", - "errno 0.2.8", - "libc", + "bytes", + "futures", + "futures-timer", + "getrandom 0.2.8", + "instant", + "libp2p-allow-block-list", + "libp2p-connection-limits", + "libp2p-core", + "libp2p-dns", + "libp2p-identify", + "libp2p-identity", + "libp2p-mdns", + "libp2p-metrics", + "libp2p-noise", + "libp2p-quic", + "libp2p-swarm", + "libp2p-tcp", + "libp2p-yamux", + "multiaddr", + "pin-project", ] [[package]] -name = "link-cplusplus" -version = "1.0.8" +name = "libp2p-allow-block-list" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +checksum = "55b46558c5c0bf99d3e2a1a38fd54ff5476ca66dd1737b12466a1824dd219311" dependencies = [ - "cc", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "void", ] [[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - -[[package]] -name = "linux-raw-sys" -version = "0.1.4" +name = "libp2p-connection-limits" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "2f5107ad45cb20b2f6c3628c7b6014b996fcb13a88053f4569c872c6e30abf58" +dependencies = [ + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "void", +] [[package]] -name = "linux-raw-sys" -version = "0.3.1" +name = "libp2p-core" +version = "0.40.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" +checksum = "dd44289ab25e4c9230d9246c475a22241e301b23e8f4061d3bdef304a1a99713" +dependencies = [ + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "libp2p-identity", + "log", + "multiaddr", + "multihash", + "multistream-select", + "once_cell", + "parking_lot", + "pin-project", + "quick-protobuf", + "rand", + "rw-stream-sink", + "smallvec", + "thiserror", + "unsigned-varint", + "void", +] [[package]] -name = "lock_api" -version = "0.4.9" +name = "libp2p-dns" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "fd4394c81c0c06d7b4a60f3face7e8e8a9b246840f98d2c80508d0721b032147" dependencies = [ - "autocfg", - "scopeguard", + "futures", + "libp2p-core", + "libp2p-identity", + "log", + "parking_lot", + "smallvec", + "trust-dns-resolver", ] [[package]] -name = "log" -version = "0.4.17" +name = "libp2p-identify" +version = "0.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "6a29675a32dbcc87790db6cf599709e64308f1ae9d5ecea2d259155889982db8" dependencies = [ - "cfg-if", - "value-bag", + "asynchronous-codec", + "either", + "futures", + "futures-timer", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "log", + "lru", + "quick-protobuf", + "quick-protobuf-codec", + "smallvec", + "thiserror", + "void", ] [[package]] -name = "mach2" -version = "0.4.1" +name = "libp2p-identity" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8" +checksum = "57bf6e730ec5e7022958da53ffb03b326e681b7316939012ae9b3c7449a812d4" dependencies = [ - "libc", + "asn1_der", + "bs58 0.5.0", + "ed25519-dalek", + "hkdf", + "libsecp256k1", + "log", + "multihash", + "quick-protobuf", + "rand", + "sha2 0.10.8", + "thiserror", + "zeroize", ] [[package]] -name = "matchers" -version = "0.1.0" +name = "libp2p-mdns" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +checksum = "42a2567c305232f5ef54185e9604579a894fd0674819402bb0ac0246da82f52a" dependencies = [ - "regex-automata 0.1.10", + "data-encoding", + "futures", + "if-watch", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "log", + "rand", + "smallvec", + "socket2 0.5.4", + "tokio", + "trust-dns-proto", + "void", ] [[package]] -name = "matchit" -version = "0.7.0" +name = "libp2p-metrics" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" +checksum = "239ba7d28f8d0b5d77760dc6619c05c7e88e74ec8fbbe97f856f20a56745e620" +dependencies = [ + "instant", + "libp2p-core", + "libp2p-identify", + "libp2p-identity", + "libp2p-swarm", + "once_cell", + "prometheus-client", +] [[package]] -name = "md-5" -version = "0.9.1" +name = "libp2p-noise" +version = "0.43.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" +checksum = "71ce70757f2c0d82e9a3ef738fb10ea0723d16cec37f078f719e2c247704c1bb" dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "opaque-debug", + "bytes", + "curve25519-dalek 4.1.1", + "futures", + "libp2p-core", + "libp2p-identity", + "log", + "multiaddr", + "multihash", + "once_cell", + "quick-protobuf", + "rand", + "sha2 0.10.8", + "snow", + "static_assertions", + "thiserror", + "x25519-dalek", + "zeroize", ] [[package]] -name = "md-5" -version = "0.10.5" +name = "libp2p-quic" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +checksum = "4cb763e88f9a043546bfebd3575f340e7dd3d6c1b2cf2629600ec8965360c63a" dependencies = [ - "digest 0.10.6", + "bytes", + "futures", + "futures-timer", + "if-watch", + "libp2p-core", + "libp2p-identity", + "libp2p-tls", + "log", + "parking_lot", + "quinn", + "rand", + "rustls 0.21.7", + "socket2 0.5.4", + "thiserror", + "tokio", +] + +[[package]] +name = "libp2p-swarm" +version = "0.43.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab94183f8fc2325817835b57946deb44340c99362cd4606c0a5717299b2ba369" +dependencies = [ + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm-derive", + "log", + "multistream-select", + "once_cell", + "rand", + "smallvec", + "tokio", + "void", +] + +[[package]] +name = "libp2p-swarm-derive" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4d5ec2a3df00c7836d7696c136274c9c59705bac69133253696a6c932cd1d74" +dependencies = [ + "heck", + "proc-macro-warning", + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "libp2p-tcp" +version = "0.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09bfdfb6f945c5c014b87872a0bdb6e0aef90e92f380ef57cd9013f118f9289d" +dependencies = [ + "futures", + "futures-timer", + "if-watch", + "libc", + "libp2p-core", + "libp2p-identity", + "log", + "socket2 0.5.4", + "tokio", +] + +[[package]] +name = "libp2p-tls" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8218d1d5482b122ccae396bbf38abdcb283ecc96fa54760e1dfd251f0546ac61" +dependencies = [ + "futures", + "futures-rustls", + "libp2p-core", + "libp2p-identity", + "rcgen", + "ring", + "rustls 0.21.7", + "rustls-webpki 0.101.4", + "thiserror", + "x509-parser", + "yasna", +] + +[[package]] +name = "libp2p-yamux" +version = "0.44.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eedcb62824c4300efb9cfd4e2a6edaf3ca097b9e68b36dabe45a44469fd6a85" +dependencies = [ + "futures", + "libp2p-core", + "log", + "thiserror", + "yamux", +] + +[[package]] +name = "libproc" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b18cbf29f8ff3542ba22bdce9ac610fcb75d74bb4e2b306b2a2762242025b4f" +dependencies = [ + "bindgen", + "errno 0.2.8", + "libc", +] + +[[package]] +name = "libsecp256k1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +dependencies = [ + "arrayref", + "base64 0.13.1", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + +[[package]] +name = "linux-raw-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +dependencies = [ + "value-bag", +] + +[[package]] +name = "lru" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "718e8fae447df0c7e1ba7f5189829e63fd536945c8988d61444c19039f16b670" +dependencies = [ + "hashbrown 0.13.1", +] + +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "mach2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8" +dependencies = [ + "libc", +] + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "matchit" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" + +[[package]] +name = "md-5" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "md-5" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +dependencies = [ + "digest 0.10.7", ] [[package]] @@ -2882,7 +3772,7 @@ dependencies = [ "once_cell", "procfs", "rlimit", - "windows", + "windows 0.51.1", ] [[package]] @@ -2972,12 +3862,132 @@ dependencies = [ "syn 1.0.107", ] +[[package]] +name = "multiaddr" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92a651988b3ed3ad1bc8c87d016bb92f6f395b84ed1db9b926b32b1fc5a2c8b5" +dependencies = [ + "arrayref", + "byteorder", + "data-encoding", + "libp2p-identity", + "multibase", + "multihash", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", + "url", +] + +[[package]] +name = "multibase" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" +dependencies = [ + "base-x", + "data-encoding", + "data-encoding-macro", +] + +[[package]] +name = "multihash" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "076d548d76a0e2a0d4ab471d0b1c36c577786dfc4471242035d97a12a735c492" +dependencies = [ + "core2", + "unsigned-varint", +] + [[package]] name = "multimap" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +[[package]] +name = "multistream-select" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea0df8e5eec2298a62b326ee4f0d7fe1a6b90a09dfcf9df37b38f947a8c42f19" +dependencies = [ + "bytes", + "futures", + "log", + "pin-project", + "smallvec", + "unsigned-varint", +] + +[[package]] +name = "netlink-packet-core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345b8ab5bd4e71a2986663e88c56856699d060e78e152e6e9d7966fcd5491297" +dependencies = [ + "anyhow", + "byteorder", + "libc", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-route" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9ea4302b9759a7a88242299225ea3688e63c85ea136371bb6cf94fd674efaab" +dependencies = [ + "anyhow", + "bitflags", + "byteorder", + "libc", + "netlink-packet-core", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-utils" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" +dependencies = [ + "anyhow", + "byteorder", + "paste", + "thiserror", +] + +[[package]] +name = "netlink-proto" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65b4b14489ab424703c092062176d52ba55485a89c076b4f9db05092b7223aa6" +dependencies = [ + "bytes", + "futures", + "log", + "netlink-packet-core", + "netlink-sys", + "thiserror", + "tokio", +] + +[[package]] +name = "netlink-sys" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" +dependencies = [ + "bytes", + "futures", + "libc", + "log", + "tokio", +] + [[package]] name = "new_debug_unreachable" version = "1.0.4" @@ -2993,6 +4003,23 @@ dependencies = [ "smallvec", ] +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags", + "cfg-if", + "libc", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + [[package]] name = "nom" version = "7.1.3" @@ -3028,6 +4055,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -3093,11 +4131,20 @@ dependencies = [ "memchr", ] +[[package]] +name = "oid-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +dependencies = [ + "asn1-rs", +] + [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "opaque-debug" @@ -3245,10 +4292,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" dependencies = [ "base64ct", - "rand_core", + "rand_core 0.6.4", "subtle", ] +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "path-slash" version = "0.2.1" @@ -3261,10 +4314,10 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", "hmac 0.12.1", "password-hash", - "sha2 0.10.6", + "sha2 0.10.8", ] [[package]] @@ -3273,7 +4326,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0ca0b5a68607598bf3bad68f32227a8164f6254833f84eafaac409cd6746c31" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", "hmac 0.12.1", ] @@ -3283,6 +4336,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +[[package]] +name = "pem" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +dependencies = [ + "base64 0.13.1", +] + [[package]] name = "percent-encoding" version = "2.3.0" @@ -3382,9 +4444,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -3408,6 +4470,12 @@ version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +[[package]] +name = "platforms" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4503fa043bf02cee09a9582e9554b4c6403b2ef55e4612e96561d294419429f8" + [[package]] name = "polling" version = "2.8.0" @@ -3424,6 +4492,29 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "poly1305" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "portable-atomic" version = "1.2.0" @@ -3530,6 +4621,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "proc-macro-warning" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1eaa7fa0aa1929ffdf7eeb6eac234dde6268914a14ad44d23521ab6a9b258e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + [[package]] name = "proc-macro2" version = "1.0.66" @@ -3552,6 +4654,29 @@ dependencies = [ "rustix 0.36.15", ] +[[package]] +name = "prometheus-client" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c99afa9a01501019ac3a14d71d9f94050346f55ca471ce90c799a15c58f61e2" +dependencies = [ + "dtoa", + "itoa", + "parking_lot", + "prometheus-client-derive-encode", +] + +[[package]] +name = "prometheus-client-derive-encode" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + [[package]] name = "prost" version = "0.12.0" @@ -3622,6 +4747,82 @@ dependencies = [ "winapi", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quick-protobuf" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6da84cc204722a989e01ba2f6e1e276e190f22263d0cb6ce8526fcdb0d2e1f" +dependencies = [ + "byteorder", +] + +[[package]] +name = "quick-protobuf-codec" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ededb1cd78531627244d51dd0c7139fbe736c7d57af0092a76f0ffb2f56e98" +dependencies = [ + "asynchronous-codec", + "bytes", + "quick-protobuf", + "thiserror", + "unsigned-varint", +] + +[[package]] +name = "quinn" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cc2c5017e4b43d5995dcea317bc46c1e09404c0a9664d2908f7f02dfe943d75" +dependencies = [ + "bytes", + "futures-io", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls 0.21.7", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c78e758510582acc40acb90458401172d41f1016f8c9dde89e49677afb7eec1" +dependencies = [ + "bytes", + "rand", + "ring", + "rustc-hash", + "rustls 0.21.7", + "slab", + "thiserror", + "tinyvec", + "tracing", +] + +[[package]] +name = "quinn-udp" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "055b4e778e8feb9f93c4e439f71dc2156ef13360b432b799e179a8c4cdf0b1d7" +dependencies = [ + "bytes", + "libc", + "socket2 0.5.4", + "tracing", + "windows-sys 0.48.0", +] + [[package]] name = "quote" version = "1.0.33" @@ -3661,7 +4862,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -3671,7 +4872,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", ] [[package]] @@ -3680,7 +4890,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.8", ] [[package]] @@ -3714,6 +4924,18 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "rcgen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" +dependencies = [ + "pem", + "ring", + "time 0.3.20", + "yasna", +] + [[package]] name = "redis" version = "0.21.7" @@ -3759,7 +4981,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom", + "getrandom 0.2.8", "redox_syscall 0.2.16", "thiserror", ] @@ -3844,7 +5066,17 @@ dependencies = [ "wasm-bindgen-futures", "web-sys", "webpki-roots 0.22.6", - "winreg", + "winreg 0.10.1", +] + +[[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error", ] [[package]] @@ -3878,7 +5110,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -3930,6 +5162,21 @@ dependencies = [ "tokio", ] +[[package]] +name = "rtnetlink" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322c53fd76a18698f1c27381d58091de3a043d356aa5bd0d510608b565f469a0" +dependencies = [ + "futures", + "log", + "netlink-packet-route", + "netlink-proto", + "nix", + "thiserror", + "tokio", +] + [[package]] name = "rundler" version = "0.1.0-beta" @@ -4014,6 +5261,24 @@ dependencies = [ "rundler-types", ] +[[package]] +name = "rundler-network" +version = "0.1.0-beta" +dependencies = [ + "ethereum_ssz", + "ethereum_ssz_derive", + "ethers", + "libp2p", + "rand", + "rundler-types", + "snap", + "ssz_types", + "thiserror", + "tokio-util", + "tracing", + "unsigned-varint", +] + [[package]] name = "rundler-pool" version = "0.1.0-beta" @@ -4304,6 +5569,15 @@ dependencies = [ "semver", ] +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + [[package]] name = "rustix" version = "0.36.15" @@ -4403,6 +5677,17 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" +[[package]] +name = "rw-stream-sink" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8c9026ff5d2f23da5e45bbc283f156383001bfb09c4e44256d02c1a685fe9a1" +dependencies = [ + "futures", + "pin-project", + "static_assertions", +] + [[package]] name = "ryu" version = "1.0.12" @@ -4481,7 +5766,7 @@ dependencies = [ "hmac 0.12.1", "pbkdf2 0.11.0", "salsa20", - "sha2 0.10.6", + "sha2 0.10.8", ] [[package]] @@ -4627,7 +5912,7 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e6be15c453eb305019bfa438b1593c731f36a289a7853f7707ee29e870b3b3c" dependencies = [ - "darling", + "darling 0.20.3", "proc-macro2", "quote", "syn 2.0.32", @@ -4663,7 +5948,7 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -4687,13 +5972,13 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -4702,7 +5987,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", "keccak", ] @@ -4736,8 +6021,8 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d" dependencies = [ - "digest 0.10.6", - "rand_core", + "digest 0.10.7", + "rand_core 0.6.4", ] [[package]] @@ -4763,9 +6048,32 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" + +[[package]] +name = "snap" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831" + +[[package]] +name = "snow" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "0c9d1425eb528a21de2755c75af4c9b5d57f50a0d4c3b7f1828a4cd03f8ba155" +dependencies = [ + "aes-gcm", + "blake2", + "chacha20poly1305", + "curve25519-dalek 4.1.1", + "rand_core 0.6.4", + "ring", + "rustc_version", + "sha2 0.10.8", + "subtle", +] [[package]] name = "socket2" @@ -4777,6 +6085,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "soketto" version = "0.7.1" @@ -4850,6 +6168,23 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "ssz_types" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "382939886cb24ee8ac885d09116a60f6262d827c7a9e36012b4f6d3d0116d0b3" +dependencies = [ + "derivative", + "ethereum_serde_utils", + "ethereum_ssz", + "itertools 0.10.5", + "serde", + "serde_derive", + "smallvec", + "tree_hash", + "typenum", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -4985,6 +6320,39 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.107", + "unicode-xid", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tap" version = "1.0.1" @@ -5140,11 +6508,10 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.29.1" +version = "1.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" dependencies = [ - "autocfg", "backtrace", "bytes", "libc", @@ -5153,7 +6520,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.4", "tokio-macros", "windows-sys 0.48.0", ] @@ -5488,6 +6855,63 @@ dependencies = [ "tracing-serde", ] +[[package]] +name = "tree_hash" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c998ac5fe2b07c025444bdd522e6258110b63861c6698eedc610c071980238d" +dependencies = [ + "ethereum-types", + "ethereum_hashing", + "smallvec", +] + +[[package]] +name = "trust-dns-proto" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f7f83d1e4a0e4358ac54c5c3681e5d7da5efc5a7a632c90bb6d6669ddd9bc26" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna 0.2.3", + "ipnet", + "lazy_static", + "rand", + "smallvec", + "socket2 0.4.9", + "thiserror", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "trust-dns-resolver" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aff21aa4dcefb0a1afbfac26deb0adc93888c7d295fb63ab273ef276ba2b7cfe" +dependencies = [ + "cfg-if", + "futures-util", + "ipconfig", + "lazy_static", + "lru-cache", + "parking_lot", + "resolv-conf", + "smallvec", + "thiserror", + "tokio", + "tracing", + "trust-dns-proto", +] + [[package]] name = "try-lock" version = "0.2.4" @@ -5572,6 +6996,27 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "universal-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "unsigned-varint" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6889a77d49f1f013504cec6bf97a2c730394adedaeb1deb5ea08949a50541105" +dependencies = [ + "asynchronous-codec", + "bytes", + "tokio-util", +] + [[package]] name = "untrusted" version = "0.7.1" @@ -5585,7 +7030,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" dependencies = [ "form_urlencoded", - "idna", + "idna 0.4.0", "percent-encoding", ] @@ -5607,7 +7052,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "getrandom", + "getrandom 0.2.8", "serde", ] @@ -5619,13 +7064,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "value-bag" -version = "1.0.0-alpha.9" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55" -dependencies = [ - "ctor", - "version_check", -] +checksum = "d92ccd67fb88503048c01b59152a04effd0782d035a83a6d256ce6085f08f4a3" [[package]] name = "version_check" @@ -5633,6 +7074,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + [[package]] name = "waker-fn" version = "1.1.0" @@ -5660,6 +7107,12 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" @@ -5793,6 +7246,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "widestring" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" + [[package]] name = "winapi" version = "0.3.9" @@ -5824,6 +7283,19 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45296b64204227616fdbf2614cefa4c236b98ee64dfaaaa435207ed99fe7829f" +dependencies = [ + "windows_aarch64_msvc 0.34.0", + "windows_i686_gnu 0.34.0", + "windows_i686_msvc 0.34.0", + "windows_x86_64_gnu 0.34.0", + "windows_x86_64_msvc 0.34.0", +] + [[package]] name = "windows" version = "0.51.1" @@ -5918,6 +7390,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" + [[package]] name = "windows_aarch64_msvc" version = "0.42.1" @@ -5930,6 +7408,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_i686_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" + [[package]] name = "windows_i686_gnu" version = "0.42.1" @@ -5942,6 +7426,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" + [[package]] name = "windows_i686_msvc" version = "0.42.1" @@ -5954,6 +7444,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_x86_64_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" + [[package]] name = "windows_x86_64_gnu" version = "0.42.1" @@ -5978,6 +7474,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" + [[package]] name = "windows_x86_64_msvc" version = "0.42.1" @@ -6008,6 +7510,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "ws_stream_wasm" version = "0.7.4" @@ -6036,23 +7548,89 @@ dependencies = [ "tap", ] +[[package]] +name = "x25519-dalek" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" +dependencies = [ + "curve25519-dalek 3.2.0", + "rand_core 0.5.1", + "zeroize", +] + +[[package]] +name = "x509-parser" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7069fba5b66b9193bd2c5d3d4ff12b839118f6bcbef5328efafafb5395cf63da" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror", + "time 0.3.20", +] + [[package]] name = "xml-rs" version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52839dc911083a8ef63efa4d039d1f58b5e409f923e44c80828f206f66e5541c" +[[package]] +name = "yamux" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0329ef377816896f014435162bb3711ea7a07729c23d0960e6f8048b21b8fe91" +dependencies = [ + "futures", + "log", + "nohash-hasher", + "parking_lot", + "pin-project", + "rand", + "static_assertions", +] + [[package]] name = "yansi" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "time 0.3.20", +] + [[package]] name = "zeroize" -version = "1.5.7" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] [[package]] name = "zip" diff --git a/Makefile b/Makefile index 6b3339083..d8b27a9a2 100644 --- a/Makefile +++ b/Makefile @@ -28,4 +28,11 @@ test-spec-integrated: ## Run spec tests in integrated mode .PHONY: test-spec-modular test-spec-modular: ## Run spec tests in modular mode test/spec-tests/remote/run-spec-tests.sh - + +.PHONY: test-coverage-all +test-coverage-all: ## Run tests and display coverage + cargo llvm-cov nextest --all-features --open --workspace + +.PHONY: test-coverage +test-coverage: ## Run tests and display coverage for a single package + cargo llvm-cov nextest --all-features --open -p $(package) diff --git a/crates/network/Cargo.toml b/crates/network/Cargo.toml new file mode 100644 index 000000000..82d2c37fa --- /dev/null +++ b/crates/network/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "rundler-network" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +repository.workspace = true + +[dependencies] +rundler-types = { path = "../types" } + +ethereum_ssz = "0.5.3" +ethereum_ssz_derive = "0.5.3" +ethers.workspace = true +snap = "1.1.0" +ssz_types = "0.5.4" +tokio-util = { workspace = true, features = ["codec", "compat"] } +thiserror.workspace = true +tracing.workspace = true +unsigned-varint = { version = "0.7.2", features = ["codec"] } + +[dependencies.libp2p] +version = "0.52.3" +default-features = false +features = ["tokio", "noise", "macros", "tcp", "identify", "yamux", "secp256k1"] + +[dev-dependencies] +rand.workspace = true diff --git a/crates/network/src/lib.rs b/crates/network/src/lib.rs new file mode 100644 index 000000000..f105462d6 --- /dev/null +++ b/crates/network/src/lib.rs @@ -0,0 +1,29 @@ +// This file is part of Rundler. +// +// Rundler is free software: you can redistribute it and/or modify it under the +// terms of the GNU Lesser General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later version. +// +// Rundler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with Rundler. +// If not, see https://www.gnu.org/licenses/. + +#![warn(missing_docs, unreachable_pub)] +#![deny(unused_must_use, rust_2018_idioms)] +#![doc(test( + no_crate_inject, + attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables)) +))] + +//! Implementation of the ERC-4337 Bundler P2P Network Protocol. +//! See the [specification](https://github.com/eth-infinitism/bundler-spec/blob/main/p2p-specs/p2p-interface.md) for more details. +//! +//! Lots of inspiration for the components of this implementation were taken from the Lighthouse implementation +//! of the Ethereum consensus layer p2p protocol. See [here](https://github.com/sigp/lighthouse/) for more details. + +// TODO(danc): remove this before release +#[allow(dead_code)] +mod rpc; diff --git a/crates/network/src/rpc/handler/codec.rs b/crates/network/src/rpc/handler/codec.rs new file mode 100644 index 000000000..20e9e5eb3 --- /dev/null +++ b/crates/network/src/rpc/handler/codec.rs @@ -0,0 +1,420 @@ +// This file is part of Rundler. +// +// Rundler is free software: you can redistribute it and/or modify it under the +// terms of the GNU Lesser General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later version. +// +// Rundler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with Rundler. +// If not, see https://www.gnu.org/licenses/. + +use std::{ + cmp, + io::{self, Write}, +}; + +use libp2p::bytes::{BufMut, BytesMut}; +use tokio_util::codec::{Decoder, Encoder}; + +use super::{ + serde::{self, SszChunkResult}, + snappy::LengthPrefixedSnappyCodec, +}; +use crate::rpc::{ + message::{Request, MAX_ERROR_MESSAGE_LEN}, + protocol::{Protocol, ProtocolSchema}, +}; + +/// Error type for the codecs. +#[derive(Debug, thiserror::Error)] +pub(crate) enum CodecError { + #[error(transparent)] + IoError(#[from] io::Error), + #[error("Invalid schema for protocol")] + InvalidSchema, + #[error("Invalid data: {0}")] + InvalidData(String), + #[error("SSZ deserialize error {0:?}")] + SszDeserializeError(ssz::DecodeError), +} + +/// Inbound codec. Decodes inbound requests and encodes response chunks. +pub(crate) struct InboundCodec { + protocol: Protocol, + max_chunk_size: usize, + inner: LengthPrefixedSnappyCodec, +} + +impl InboundCodec { + /// Create a new codec for a particular protocol. + pub(crate) fn new(protocol: Protocol, max_chunk_size: usize) -> Self { + let (min, max) = protocol.schema.ssz_request_limits(); + + Self { + protocol, + max_chunk_size, + inner: LengthPrefixedSnappyCodec::new(min, cmp::min(max, max_chunk_size)), + } + } +} + +impl Encoder for InboundCodec { + type Error = CodecError; + + fn encode(&mut self, item: SszChunkResult, dst: &mut BytesMut) -> Result<(), Self::Error> { + let code = item.code(); + let bytes = serde::seraialize_response_chunk(item)?; + let len = bytes.len(); + + if (code > 0 && len > MAX_ERROR_MESSAGE_LEN) + || ssz_response_length_invalid(len, self.protocol.schema, self.max_chunk_size) + { + return Err(CodecError::InvalidData(format!( + "Invalid response length {}", + len + ))); + } + + // response code + dst.writer().write_all(&[code])?; + + // snappy + self.inner.encode(bytes, dst)?; + + Ok(()) + } +} + +impl Decoder for InboundCodec { + type Error = CodecError; + type Item = Request; + + fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { + if src.is_empty() { + return Ok(None); + } + + if let Some(buf) = self.inner.decode(src)? { + let req = serde::deserialize_request(self.protocol.schema, &buf)?; + Ok(Some(req)) + } else { + Ok(None) + } + } +} + +/// Outbound codec. Encodes outbound requests and decodes response chunk. +pub(crate) struct OutboundCodec { + protocol: Protocol, + max_chunk_size: usize, + inner: LengthPrefixedSnappyCodec, + inner_error: LengthPrefixedSnappyCodec, + + // decoder state + response_code: Option, +} + +impl OutboundCodec { + /// Create a new codec for a particular protocol. + pub(crate) fn new(protocol: Protocol, max_chunk_size: usize) -> Self { + let (min, max) = protocol.schema.ssz_response_limits(); + + Self { + protocol, + max_chunk_size, + response_code: None, + inner: LengthPrefixedSnappyCodec::new(min, cmp::min(max, max_chunk_size)), + inner_error: LengthPrefixedSnappyCodec::new(0, MAX_ERROR_MESSAGE_LEN), + } + } +} + +impl Encoder for OutboundCodec { + type Error = CodecError; + + fn encode(&mut self, item: Request, dst: &mut BytesMut) -> Result<(), Self::Error> { + // NOTE: assuming that the chunk type is valid for the protocol + let bytes = serde::serialize_request(item)?; + + if ssz_request_length_invalid(bytes.len(), self.protocol.schema, self.max_chunk_size) { + return Err(CodecError::InvalidData(format!( + "Invalid request length {}", + bytes.len() + ))); + } + + // snappy + // can use either codec since no length checking is done on encode + self.inner.encode(bytes, dst)?; + + Ok(()) + } +} + +impl Decoder for OutboundCodec { + type Error = CodecError; + type Item = SszChunkResult; + + fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { + if src.is_empty() { + return Ok(None); + } + + // get the decoder for the response code + let response_code = self.response_code.unwrap_or_else(|| { + let response_code = src.split_to(1)[0]; + self.response_code = Some(response_code); + response_code + }); + + if response_code > 0 { + if let Some(buf) = self.inner_error.decode(src)? { + let err = serde::deserialize_error_chunk(response_code, &buf)?; + // reset the codec + self.response_code.take(); + return Ok(Some(SszChunkResult::err(err))); + } + } else if let Some(buf) = self.inner.decode(src)? { + let chunk = serde::deserialize_response_chunk(self.protocol.schema, &buf)?; + // reset the codec + self.response_code.take(); + return Ok(Some(SszChunkResult::ok(chunk))); + } + + Ok(None) + } +} + +impl From for CodecError { + fn from(e: unsigned_varint::decode::Error) -> Self { + CodecError::InvalidData(format!("Invalid unsigned varint: {:?}", e)) + } +} + +fn ssz_request_length_invalid( + length: usize, + protocol: ProtocolSchema, + max_chunk_size: usize, +) -> bool { + let (min, max) = protocol.ssz_request_limits(); + length < min || length > cmp::min(max, max_chunk_size) +} + +fn ssz_response_length_invalid( + length: usize, + protocol: ProtocolSchema, + max_chunk_size: usize, +) -> bool { + let (min, max) = protocol.ssz_response_limits(); + length < min || length > cmp::min(max, max_chunk_size) +} + +#[cfg(test)] +mod test { + use ethers::types::{Bytes, H256, U256}; + use rundler_types::UserOperation; + + use super::*; + use crate::rpc::{ + handler::serde::{CodedError, PooledUserOpsByHashChunkSsz, SszChunk, UserOperationSsz}, + message::{ + ErrorKind, Goodbye, GoodbyeReason, Metadata, Ping, Pong, PooledUserOpHashesRequest, + PooledUserOpHashesResponse, PooledUserOpsByHashRequest, ResponseError, Status, + }, + protocol::Encoding, + }; + + const MAX_CHUNK_SIZE: usize = 1048576; + + #[test] + fn test_response_status() { + let res = SszChunkResult::ok(SszChunk::Status(Status { + supported_mempools: vec![H256::random()], + })); + let protocol = Protocol::new(ProtocolSchema::StatusV1, Encoding::SSZSnappy); + encode_decode_response(res, protocol); + } + + #[test] + fn test_response_ping() { + let res = SszChunkResult::ok(SszChunk::Ping(Pong { + metadata_seq_number: 1, + })); + let protocol = Protocol::new(ProtocolSchema::PingV1, Encoding::SSZSnappy); + encode_decode_response(res, protocol); + } + + #[test] + fn test_response_metadata() { + let res = SszChunkResult::ok(SszChunk::Metadata(Metadata { seq_number: 1 })); + let protocol = Protocol::new(ProtocolSchema::MetadataV1, Encoding::SSZSnappy); + encode_decode_response(res, protocol); + } + + #[test] + fn test_response_pooled_user_op_hashes() { + let res = SszChunkResult::ok(SszChunk::PooledUserOpHashes(PooledUserOpHashesResponse { + more_flag: true, + hashes: vec![H256::random()], + })); + let protocol = Protocol::new(ProtocolSchema::PooledUserOpHashesV1, Encoding::SSZSnappy); + encode_decode_response(res, protocol); + } + + #[test] + fn test_response_pooled_user_ops_by_hash() { + let res = SszChunkResult::ok(SszChunk::PooledUserOpsByHash(Box::new( + PooledUserOpsByHashChunkSsz { + user_op: user_op_ssz(), + }, + ))); + let protocol = Protocol::new(ProtocolSchema::PooledUserOpsByHashV1, Encoding::SSZSnappy); + encode_decode_response(res, protocol); + } + + #[test] + fn test_response_pooled_user_ops_by_hash_multiple() { + let protocol = Protocol::new(ProtocolSchema::PooledUserOpsByHashV1, Encoding::SSZSnappy); + let mut inbound = InboundCodec::new(protocol.clone(), MAX_CHUNK_SIZE); + let mut outbound = OutboundCodec::new(protocol, MAX_CHUNK_SIZE); + let mut buf = BytesMut::new(); + let mut results = vec![]; + + for _ in 0..10 { + let res = SszChunkResult::ok(SszChunk::PooledUserOpsByHash(Box::new( + PooledUserOpsByHashChunkSsz { + user_op: user_op_ssz(), + }, + ))); + inbound.encode(res.clone(), &mut buf).unwrap(); + results.push(res); + } + + for result in results { + let res = outbound.decode(&mut buf).unwrap().unwrap(); + assert_eq!(res, result); + } + } + + #[test] + fn test_error_response() { + let protocol = Protocol::new(ProtocolSchema::StatusV1, Encoding::SSZSnappy); + let res = SszChunkResult::err( + ResponseError { + kind: ErrorKind::InvalidRequest, + message: "this was an invalid request".into(), + } + .try_into() + .unwrap(), + ); + encode_decode_response(res, protocol); + } + + #[test] + fn test_encode_invalid_error_response() { + let protocol = Protocol::new(ProtocolSchema::StatusV1, Encoding::SSZSnappy); + let res = SszChunkResult::err(CodedError { + code: 1, + message: vec![0; MAX_ERROR_MESSAGE_LEN + 1], + }); + let mut inbound = InboundCodec::new(protocol.clone(), MAX_CHUNK_SIZE); + let mut buf = BytesMut::new(); + let err = inbound.encode(res.clone(), &mut buf).unwrap_err(); + assert!(matches!(err, CodecError::InvalidData(_))); + } + + #[test] + fn test_request_status() { + let protocol = Protocol::new(ProtocolSchema::StatusV1, Encoding::SSZSnappy); + + let req = Request::Status(Status { + supported_mempools: vec![H256::random()], + }); + + encode_decode_request(req, protocol); + } + + #[test] + fn test_request_ping() { + let protocol = Protocol::new(ProtocolSchema::PingV1, Encoding::SSZSnappy); + + let req = Request::Ping(Ping { + metadata_seq_number: 1, + }); + encode_decode_request(req, protocol); + } + + #[test] + fn test_request_goodbye() { + let protocol = Protocol::new(ProtocolSchema::GoodbyeV1, Encoding::SSZSnappy); + + let req = Request::Goodbye(Goodbye { + reason: GoodbyeReason::ClientShutdown, + }); + encode_decode_request(req, protocol); + } + + #[test] + fn test_request_pooled_user_op_hashes() { + let protocol = Protocol::new(ProtocolSchema::PooledUserOpHashesV1, Encoding::SSZSnappy); + + let req = Request::PooledUserOpHashes(PooledUserOpHashesRequest { + mempool: H256::random(), + offset: 0, + }); + encode_decode_request(req, protocol); + } + + #[test] + fn test_request_pooled_user_ops_by_hash() { + let protocol = Protocol::new(ProtocolSchema::PooledUserOpsByHashV1, Encoding::SSZSnappy); + + let req = Request::PooledUserOpsByHash(PooledUserOpsByHashRequest { + hashes: vec![H256::random()], + }); + encode_decode_request(req, protocol); + } + + fn encode_decode_response(chunk: SszChunkResult, protocol: Protocol) { + let mut inbound = InboundCodec::new(protocol.clone(), MAX_CHUNK_SIZE); + let mut outbound = OutboundCodec::new(protocol, MAX_CHUNK_SIZE); + + let mut buf = BytesMut::new(); + inbound.encode(chunk.clone(), &mut buf).unwrap(); + let resp = outbound.decode(&mut buf).unwrap(); + assert_eq!(resp, Some(chunk)); + } + + fn encode_decode_request(request: Request, protocol: Protocol) { + let mut inbound = InboundCodec::new(protocol.clone(), MAX_CHUNK_SIZE); + let mut outbound = OutboundCodec::new(protocol, MAX_CHUNK_SIZE); + + let mut buf = BytesMut::new(); + outbound.encode(request.clone(), &mut buf).unwrap(); + let req = inbound.decode(&mut buf).unwrap(); + assert_eq!(req, Some(request)); + } + + fn user_op_ssz() -> UserOperationSsz { + UserOperation { + sender: "0x0000000000000000000000000000000000000000" + .parse() + .unwrap(), + nonce: U256::zero(), + init_code: Bytes::default(), + call_data: Bytes::default(), + call_gas_limit: U256::zero(), + verification_gas_limit: U256::zero(), + pre_verification_gas: U256::zero(), + max_fee_per_gas: U256::zero(), + max_priority_fee_per_gas: U256::zero(), + paymaster_and_data: Bytes::default(), + signature: Bytes::default(), + } + .try_into() + .unwrap() + } +} diff --git a/crates/network/src/rpc/handler/mod.rs b/crates/network/src/rpc/handler/mod.rs new file mode 100644 index 000000000..a8dc973e0 --- /dev/null +++ b/crates/network/src/rpc/handler/mod.rs @@ -0,0 +1,17 @@ +// This file is part of Rundler. +// +// Rundler is free software: you can redistribute it and/or modify it under the +// terms of the GNU Lesser General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later version. +// +// Rundler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with Rundler. +// If not, see https://www.gnu.org/licenses/. + +mod codec; +pub(crate) use codec::CodecError; +mod serde; +mod snappy; diff --git a/crates/network/src/rpc/handler/serde.rs b/crates/network/src/rpc/handler/serde.rs new file mode 100644 index 000000000..6dc5004eb --- /dev/null +++ b/crates/network/src/rpc/handler/serde.rs @@ -0,0 +1,554 @@ +// This file is part of Rundler. +// +// Rundler is free software: you can redistribute it and/or modify it under the +// terms of the GNU Lesser General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later version. +// +// Rundler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with Rundler. +// If not, see https://www.gnu.org/licenses/. + +use ethers::types::{Address, U256}; +use rundler_types::UserOperation; +use ssz::{Decode, DecodeError, Encode}; +use ssz_derive::{Decode, Encode}; +use tracing::warn; + +use crate::rpc::{ + handler::CodecError, + message::{ + ErrorKind, Goodbye, Metadata, Ping, Pong, PooledUserOpHashesRequest, + PooledUserOpHashesResponse, PooledUserOpsByHashRequest, PooledUserOpsByHashResponse, + Request, Response, ResponseError, ResponseResult, Status, MAX_ERROR_MESSAGE_LEN, + MAX_OPS_PER_REQUEST, + }, + protocol::ProtocolSchema, +}; + +// TODO: determine what this actually should be +const MAX_SSZ_SIZE: usize = 1_048_576; // 1MB + +/// Wrapper around a response chunk result +/// +/// The result can be either a valid chunk or an error chunk +#[derive(Debug, Clone, PartialEq)] +pub(crate) struct SszChunkResult(Result); + +/// Coded error for a response chunk +#[derive(Debug, Clone, PartialEq)] +pub(crate) struct CodedError { + pub(crate) code: u8, + pub(crate) message: Vec, +} + +impl From for CodedError { + fn from(error: ResponseError) -> Self { + let mut bytes = error.message.into_bytes(); + if bytes.len() > MAX_ERROR_MESSAGE_LEN { + warn!("Error message is too long, truncating"); + bytes.truncate(MAX_ERROR_MESSAGE_LEN); + }; + + Self { + code: error.kind.into(), + message: bytes, + } + } +} + +impl From for ResponseError { + fn from(error: CodedError) -> Self { + Self { + kind: error.code.into(), + message: String::from_utf8_lossy(&error.message).to_string(), + } + } +} + +/// A chunk of a response +#[derive(Debug, Clone, PartialEq)] +pub(crate) enum SszChunk { + Status(Status), + Ping(Pong), + Metadata(Metadata), + PooledUserOpHashes(PooledUserOpHashesResponse), + // This response is much larger than the rest, to save space in the enum + // we store it in a box. + PooledUserOpsByHash(Box), +} + +impl SszChunkResult { + /// Create a ok response chunk + pub(crate) fn ok(chunk: SszChunk) -> Self { + Self(Ok(chunk)) + } + + /// Create an error response chunk + pub(crate) fn err(error: CodedError) -> Self { + Self(Err(error)) + } + + /// The code associated with a response chunk + pub(crate) fn code(&self) -> u8 { + match self.0 { + Ok(_) => 0, + Err(CodedError { + code: error_kind, .. + }) => error_kind, + } + } +} + +/// Create a response from a list of chunks for a given protocol schema. +pub(crate) fn response_from_chunks( + schema: ProtocolSchema, + mut chunks: Vec, +) -> ResponseResult { + if chunks.is_empty() || chunks.len() > schema.max_response_chunks() { + return invalid_response("invalid number of chunks"); + } + + match schema { + ProtocolSchema::StatusV1 => match chunks.pop().unwrap().0 { + Ok(SszChunk::Status(status)) => Ok(Response::Status(status)), + Ok(_) => invalid_response("wrong kind"), + Err(err) => Err(err)?, + }, + ProtocolSchema::GoodbyeV1 => invalid_response("goodbye is not a valid response"), + ProtocolSchema::PingV1 => match chunks.pop().unwrap().0 { + Ok(SszChunk::Ping(pong)) => Ok(Response::Ping(pong)), + Ok(_) => invalid_response("wrong kind"), + Err(err) => Err(err)?, + }, + ProtocolSchema::MetadataV1 => match chunks.pop().unwrap().0 { + Ok(SszChunk::Metadata(metadata)) => Ok(Response::Metadata(metadata)), + Ok(_) => invalid_response("wrong kind"), + Err(err) => Err(err)?, + }, + ProtocolSchema::PooledUserOpHashesV1 => match chunks.pop().unwrap().0 { + Ok(SszChunk::PooledUserOpHashes(hashes)) => Ok(Response::PooledUserOpHashes(hashes)), + Ok(_) => invalid_response("wrong kind"), + Err(err) => Err(err)?, + }, + ProtocolSchema::PooledUserOpsByHashV1 => { + let mut user_ops = Vec::new(); + for chunk in chunks { + match chunk.0 { + Ok(SszChunk::PooledUserOpsByHash(chunk)) => match chunk.user_op.try_into() { + Ok(user_op) => user_ops.push(user_op), + Err(_) => return invalid_response("invalid user op"), + }, + Ok(_) => return invalid_response("wrong kind"), + Err(err) => return Err(err.into()), + } + } + Ok(Response::PooledUserOpsByHash(PooledUserOpsByHashResponse { + user_ops, + })) + } + } +} + +fn invalid_response(message: &str) -> ResponseResult { + Err(ResponseError { + kind: ErrorKind::InvalidRequest, + message: message.to_string(), + }) +} + +/// Create a list of chunks from a response +pub(crate) fn chunks_from_response( + response: ResponseResult, +) -> Result, CodecError> { + let res = match response { + Ok(response) => match response { + Response::Status(status) => { + vec![SszChunkResult::ok(SszChunk::Status(status))] + } + Response::Metadata(metadata) => { + vec![SszChunkResult::ok(SszChunk::Metadata(metadata))] + } + Response::Ping(pong) => { + vec![SszChunkResult::ok(SszChunk::Ping(pong))] + } + Response::PooledUserOpHashes(hashes) => { + vec![SszChunkResult::ok(SszChunk::PooledUserOpHashes(hashes))] + } + Response::PooledUserOpsByHash(r) => r + .user_ops + .into_iter() + .map(|op| { + SszChunkResult::ok(SszChunk::PooledUserOpsByHash(Box::new( + PooledUserOpsByHashChunkSsz { user_op: op.into() }, + ))) + }) + .collect(), + }, + Err(error) => { + vec![SszChunkResult::err(error.into())] + } + }; + Ok(res) +} + +/// Serialize a request to bytes +pub(crate) fn serialize_request(request: Request) -> Result, CodecError> { + match request { + Request::Status(r) => Ok(r.as_ssz_bytes()), + Request::Goodbye(r) => Ok(GoodbyeSsz::from(r).as_ssz_bytes()), + Request::Ping(r) => Ok(r.as_ssz_bytes()), + Request::Metadata => Err(CodecError::InvalidSchema), + Request::PooledUserOpHashes(r) => Ok(r.as_ssz_bytes()), + Request::PooledUserOpsByHash(r) => Ok(r.as_ssz_bytes()), + } +} + +/// Deserialize a request from bytes +pub(crate) fn deserialize_request( + schema: ProtocolSchema, + bytes: &[u8], +) -> Result { + match schema { + ProtocolSchema::StatusV1 => Ok(Request::Status(Status::from_ssz_bytes(bytes)?)), + ProtocolSchema::GoodbyeV1 => { + let req = GoodbyeSsz::from_ssz_bytes(bytes)?; + Ok(Request::Goodbye(req.into())) + } + ProtocolSchema::PingV1 => Ok(Request::Ping(Ping::from_ssz_bytes(bytes)?)), + ProtocolSchema::MetadataV1 => Ok(Request::Metadata), + ProtocolSchema::PooledUserOpHashesV1 => Ok(Request::PooledUserOpHashes( + PooledUserOpHashesRequest::from_ssz_bytes(bytes)?, + )), + ProtocolSchema::PooledUserOpsByHashV1 => Ok(Request::PooledUserOpsByHash( + PooledUserOpsByHashRequest::from_ssz_bytes(bytes)?, + )), + } +} + +/// Serialize a response chunk to bytes +pub(crate) fn seraialize_response_chunk(chunk: SszChunkResult) -> Result, CodecError> { + match chunk.0 { + Ok(SszChunk::Status(r)) => Ok(r.as_ssz_bytes()), + Ok(SszChunk::Ping(r)) => Ok(r.as_ssz_bytes()), + Ok(SszChunk::Metadata(r)) => Ok(r.as_ssz_bytes()), + Ok(SszChunk::PooledUserOpHashes(r)) => Ok(r.as_ssz_bytes()), + Ok(SszChunk::PooledUserOpsByHash(r)) => Ok(r.as_ssz_bytes()), + // only serialize the message portion of the error, the code is already written + Err(error) => Ok(error.message.as_ssz_bytes()), + } +} + +/// Deserialize a response chunk from bytes for a given protocol schema +pub(crate) fn deserialize_response_chunk( + schema: ProtocolSchema, + bytes: &[u8], +) -> Result { + match schema { + ProtocolSchema::StatusV1 => Ok(SszChunk::Status(Status::from_ssz_bytes(bytes)?)), + ProtocolSchema::GoodbyeV1 => Err(CodecError::InvalidSchema), + ProtocolSchema::PingV1 => Ok(SszChunk::Ping(Pong::from_ssz_bytes(bytes)?)), + ProtocolSchema::MetadataV1 => Ok(SszChunk::Metadata(Metadata::from_ssz_bytes(bytes)?)), + ProtocolSchema::PooledUserOpHashesV1 => Ok(SszChunk::PooledUserOpHashes( + PooledUserOpHashesResponse::from_ssz_bytes(bytes)?, + )), + ProtocolSchema::PooledUserOpsByHashV1 => Ok(SszChunk::PooledUserOpsByHash(Box::new( + PooledUserOpsByHashChunkSsz::from_ssz_bytes(bytes)?, + ))), + } +} + +/// Deserialize an error chunk from bytes +pub(crate) fn deserialize_error_chunk(code: u8, bytes: &[u8]) -> Result { + let bytes = Vec::::from_ssz_bytes(bytes)?; + Ok(CodedError { + code, + message: bytes, + }) +} + +#[derive(Debug, Default, Encode, Decode)] +struct GoodbyeSsz { + reason: u64, +} + +impl From for GoodbyeSsz { + fn from(request: Goodbye) -> Self { + Self { + reason: request.reason.into(), + } + } +} + +impl From for Goodbye { + fn from(request: GoodbyeSsz) -> Self { + Self { + reason: request.reason.into(), + } + } +} + +/// Response chunk for pooled user ops by hash +/// This is a wrapper around a user operation that implements ssz +#[derive(Debug, Clone, PartialEq, Encode, Decode)] +pub(crate) struct PooledUserOpsByHashChunkSsz { + pub(crate) user_op: UserOperationSsz, +} + +#[derive(Debug, Clone, PartialEq, Encode, Decode)] +pub(crate) struct UserOperationSsz { + sender: Vec, + nonce: U256, + init_code: Vec, + call_data: Vec, + call_gas_limit: U256, + verification_gas_limit: U256, + pre_verification_gas: U256, + max_fee_per_gas: U256, + max_priority_fee_per_gas: U256, + paymaster_and_data: Vec, + signature: Vec, +} + +impl From for UserOperationSsz { + fn from(uo: UserOperation) -> Self { + Self { + sender: uo.sender.as_bytes().to_vec(), + nonce: uo.nonce, + init_code: uo.init_code.to_vec(), + call_data: uo.call_data.to_vec(), + call_gas_limit: uo.call_gas_limit, + verification_gas_limit: uo.verification_gas_limit, + pre_verification_gas: uo.pre_verification_gas, + max_fee_per_gas: uo.max_fee_per_gas, + max_priority_fee_per_gas: uo.max_priority_fee_per_gas, + paymaster_and_data: uo.paymaster_and_data.to_vec(), + signature: uo.signature.to_vec(), + } + } +} + +impl TryFrom for UserOperation { + type Error = CodecError; + + fn try_from(uo_ssz: UserOperationSsz) -> Result { + if uo_ssz.sender.len() != 20 { + return Err(CodecError::InvalidData("invalid sender bytes".into())); + } + + Ok(Self { + sender: Address::from_slice(&uo_ssz.sender), + nonce: uo_ssz.nonce, + init_code: uo_ssz.init_code.into(), + call_data: uo_ssz.call_data.into(), + call_gas_limit: uo_ssz.call_gas_limit, + verification_gas_limit: uo_ssz.verification_gas_limit, + pre_verification_gas: uo_ssz.pre_verification_gas, + max_fee_per_gas: uo_ssz.max_fee_per_gas, + max_priority_fee_per_gas: uo_ssz.max_priority_fee_per_gas, + paymaster_and_data: uo_ssz.paymaster_and_data.into(), + signature: uo_ssz.signature.into(), + }) + } +} + +impl ProtocolSchema { + pub(crate) fn ssz_request_limits(&self) -> (usize, usize) { + match self { + ProtocolSchema::StatusV1 => (0, MAX_SSZ_SIZE), + ProtocolSchema::GoodbyeV1 => ( + ::ssz_fixed_len(), + ::ssz_fixed_len(), + ), + ProtocolSchema::PingV1 => ( + ::ssz_fixed_len(), + ::ssz_fixed_len(), + ), + ProtocolSchema::MetadataV1 => (0, 0), + ProtocolSchema::PooledUserOpHashesV1 => (0, MAX_SSZ_SIZE), + ProtocolSchema::PooledUserOpsByHashV1 => (0, MAX_SSZ_SIZE), + } + } + + pub(crate) fn ssz_response_limits(&self) -> (usize, usize) { + match self { + ProtocolSchema::StatusV1 => (0, MAX_SSZ_SIZE), + ProtocolSchema::GoodbyeV1 => (0, 0), + ProtocolSchema::PingV1 => ( + ::ssz_fixed_len(), + ::ssz_fixed_len(), + ), + ProtocolSchema::MetadataV1 => ( + ::ssz_fixed_len(), + ::ssz_fixed_len(), + ), + ProtocolSchema::PooledUserOpHashesV1 => (0, MAX_SSZ_SIZE), + ProtocolSchema::PooledUserOpsByHashV1 => (0, MAX_SSZ_SIZE), + } + } + + pub(crate) fn max_response_chunks(&self) -> usize { + match self { + ProtocolSchema::StatusV1 => 1, + ProtocolSchema::GoodbyeV1 => 1, + ProtocolSchema::PingV1 => 1, + ProtocolSchema::MetadataV1 => 1, + ProtocolSchema::PooledUserOpHashesV1 => 1, + ProtocolSchema::PooledUserOpsByHashV1 => MAX_OPS_PER_REQUEST, + } + } +} + +impl From for CodecError { + fn from(err: DecodeError) -> Self { + Self::SszDeserializeError(err) + } +} + +#[cfg(test)] +mod test { + use ethers::types::H256; + + use super::*; + + #[test] + fn test_status_response_chunk() { + let resp = ResponseResult::Ok(Response::Status(Status { + supported_mempools: vec![H256::random()], + })); + let chunks = chunks_from_response(resp.clone()).unwrap(); + assert_eq!(chunks.len(), 1); + let resp_from_chunks = response_from_chunks(ProtocolSchema::StatusV1, chunks); + assert_eq!(resp, resp_from_chunks); + } + + #[test] + fn test_ping_response_chunk() { + let resp = ResponseResult::Ok(Response::Ping(Pong { + metadata_seq_number: 1, + })); + let chunks = chunks_from_response(resp.clone()).unwrap(); + assert_eq!(chunks.len(), 1); + let resp_from_chunks = response_from_chunks(ProtocolSchema::PingV1, chunks); + assert_eq!(resp, resp_from_chunks); + } + + #[test] + fn test_metadata_response_chunk() { + let resp = ResponseResult::Ok(Response::Metadata(Metadata { seq_number: 1 })); + let chunks = chunks_from_response(resp.clone()).unwrap(); + assert_eq!(chunks.len(), 1); + let resp_from_chunks = response_from_chunks(ProtocolSchema::MetadataV1, chunks); + assert_eq!(resp, resp_from_chunks); + } + + #[test] + fn test_pooled_user_op_hashes_response_chunk() { + let resp = ResponseResult::Ok(Response::PooledUserOpHashes(PooledUserOpHashesResponse { + more_flag: false, + hashes: vec![H256::random()], + })); + let chunks = chunks_from_response(resp.clone()).unwrap(); + assert_eq!(chunks.len(), 1); + let resp_from_chunks = response_from_chunks(ProtocolSchema::PooledUserOpHashesV1, chunks); + assert_eq!(resp, resp_from_chunks); + } + + #[test] + fn test_pooled_user_ops_by_hash_response_chunk() { + let resp = ResponseResult::Ok(Response::PooledUserOpsByHash(PooledUserOpsByHashResponse { + user_ops: vec![UserOperation::default(); 5], + })); + let chunks = chunks_from_response(resp.clone()).unwrap(); + assert_eq!(chunks.len(), 5); + let resp_from_chunks = response_from_chunks(ProtocolSchema::PooledUserOpsByHashV1, chunks); + assert_eq!(resp, resp_from_chunks); + } + + #[test] + fn test_error_response_chunk() { + let resp = ResponseResult::Err(ResponseError { + kind: ErrorKind::InvalidRequest, + message: "invalid request".to_string(), + }); + let chunks = chunks_from_response(resp.clone()).unwrap(); + assert_eq!(chunks.len(), 1); + let resp_from_chunks = response_from_chunks(ProtocolSchema::StatusV1, chunks); + assert_eq!(resp, resp_from_chunks); + } + + #[test] + fn test_error_after_ok_chunk() { + let chunks = vec![ + SszChunkResult::ok(SszChunk::PooledUserOpsByHash(Box::new( + PooledUserOpsByHashChunkSsz { + user_op: UserOperation::default().try_into().unwrap(), + }, + ))), + SszChunkResult::err(CodedError { + code: 1, + message: "invalid request".to_string().into_bytes(), + }), + ]; + let resp_from_chunks = response_from_chunks(ProtocolSchema::PooledUserOpsByHashV1, chunks); + assert_eq!( + resp_from_chunks, + ResponseResult::Err(ResponseError { + kind: ErrorKind::InvalidRequest, + message: "invalid request".to_string(), + }) + ); + } + + #[test] + fn test_truncate_error() { + let error = ResponseError { + kind: ErrorKind::InvalidRequest, + message: String::from_utf8(vec![b'X'; MAX_ERROR_MESSAGE_LEN * 2]).unwrap(), + }; + + let coded_error = CodedError::from(error); + assert_eq!(coded_error.message.len(), MAX_ERROR_MESSAGE_LEN); + } + + #[test] + fn test_invalid_schema() { + let chunk = SszChunkResult::ok(SszChunk::Metadata(Metadata { seq_number: 1 })); + let err = response_from_chunks(ProtocolSchema::GoodbyeV1, vec![chunk]).unwrap_err(); + assert_eq!(err.kind, ErrorKind::InvalidRequest); + } + + #[test] + fn test_empty_chunks() { + let err = response_from_chunks(ProtocolSchema::StatusV1, vec![]).unwrap_err(); + assert_eq!(err.kind, ErrorKind::InvalidRequest); + } + + #[test] + fn test_too_many_chunks() { + let chunks = vec![ + SszChunkResult::ok(SszChunk::Status(Status { + supported_mempools: vec![H256::random()], + })), + SszChunkResult::ok(SszChunk::Status(Status { + supported_mempools: vec![H256::random()], + })), + ]; + let err = response_from_chunks(ProtocolSchema::StatusV1, chunks).unwrap_err(); + assert_eq!(err.kind, ErrorKind::InvalidRequest); + } + + #[test] + fn test_too_many_ops() { + let chunk = SszChunkResult::ok(SszChunk::PooledUserOpsByHash(Box::new( + PooledUserOpsByHashChunkSsz { + user_op: UserOperation::default().try_into().unwrap(), + }, + ))); + let chunks = vec![chunk; MAX_OPS_PER_REQUEST + 1]; + let err = response_from_chunks(ProtocolSchema::PooledUserOpsByHashV1, chunks).unwrap_err(); + assert_eq!(err.kind, ErrorKind::InvalidRequest); + } +} diff --git a/crates/network/src/rpc/handler/snappy.rs b/crates/network/src/rpc/handler/snappy.rs new file mode 100644 index 000000000..0d5553b3b --- /dev/null +++ b/crates/network/src/rpc/handler/snappy.rs @@ -0,0 +1,210 @@ +// This file is part of Rundler. +// +// Rundler is free software: you can redistribute it and/or modify it under the +// terms of the GNU Lesser General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later version. +// +// Rundler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with Rundler. +// If not, see https://www.gnu.org/licenses/. + +use std::io::{self, Cursor, Read, Write}; + +use libp2p::bytes::{BufMut, BytesMut}; +use tokio_util::codec::{Decoder, Encoder}; +use unsigned_varint::codec::Uvi; + +use super::CodecError; + +pub(crate) struct LengthPrefixedSnappyCodec { + decode_state: State, + uvi: Uvi, + decode_min_length: usize, + decode_max_length: usize, +} + +enum State { + Init, + DecodeLength, + DecompressPayload { + max_compressed_len: usize, + buf: Vec, + }, +} + +impl LengthPrefixedSnappyCodec { + pub(crate) fn new(decode_min_length: usize, decode_max_length: usize) -> Self { + Self { + decode_state: State::Init, + uvi: Uvi::default(), + decode_min_length, + decode_max_length, + } + } +} + +impl Decoder for LengthPrefixedSnappyCodec { + type Error = CodecError; + type Item = Vec; + + fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { + loop { + match &mut self.decode_state { + State::Init => { + if src.is_empty() { + return Ok(None); + } else { + self.decode_state = State::DecodeLength; + continue; + } + } + State::DecodeLength => { + if let Some(length) = self.uvi.decode(src)? { + if length < self.decode_min_length || length > self.decode_max_length { + return Err(CodecError::InvalidData(format!( + "Invalid length: {}", + length + ))); + } + + self.decode_state = State::DecompressPayload { + max_compressed_len: snap::raw::max_compress_len(length), + buf: vec![0; length], + }; + continue; + } else { + return Ok(None); + } + } + State::DecompressPayload { + max_compressed_len, + buf, + } => { + // Read up to `max_compressed_len bytes` from decoder, writing exactly `length` bytes to buf, + // using a cursor to track the number of bytes read to consume from source. + // This will not consume from `src` until the entire payload can be decompressed. + let reader = Cursor::new(src.as_ref()).take(*max_compressed_len as u64); + let mut decoder = snap::read::FrameDecoder::new(reader); + let res = decoder.read_exact(buf); + let read = decoder.get_ref().get_ref().position() as usize; + + match res { + Ok(()) => { + let buf = std::mem::take(buf); + let _ = src.split_to(read); + self.decode_state = State::Init; + return Ok(Some(buf)); + } + Err(e) => match e.kind() { + io::ErrorKind::UnexpectedEof => { + if read >= *max_compressed_len { + return Err(CodecError::InvalidData( + "Invalid snappy compression".into(), + )); + } + // not enough data, try again + return Ok(None); + } + _ => { + return Err(CodecError::from(e)); + } + }, + } + } + } + } + } +} + +impl Encoder> for LengthPrefixedSnappyCodec { + type Error = CodecError; + + fn encode(&mut self, item: Vec, dst: &mut BytesMut) -> Result<(), Self::Error> { + let mut writer = dst.writer(); + + // length-prefixed header header + let mut buf = unsigned_varint::encode::usize_buffer(); + let uvi = unsigned_varint::encode::usize(item.len(), &mut buf); + writer.write_all(uvi)?; + + // snappy body + let mut encoder = snap::write::FrameEncoder::new(writer); + encoder.write_all(&item)?; + encoder.flush()?; + + Ok(()) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_encode_decode() { + let mut codec = LengthPrefixedSnappyCodec::new(0, 1024); + let src = random_bytes(1024); + let mut buf = BytesMut::new(); + codec.encode(src.clone(), &mut buf).unwrap(); + let dst = codec.decode(&mut buf).unwrap().unwrap(); + assert_eq!(src, dst); + } + + #[test] + fn test_decode_too_many() { + let mut codec = LengthPrefixedSnappyCodec::new(0, 1024); + let src = random_bytes(2048); + let mut buf = BytesMut::new(); + codec.encode(src, &mut buf).unwrap(); + + let res = codec.decode(&mut buf).unwrap_err(); + assert!(matches!(res, CodecError::InvalidData(_))); + } + + #[test] + fn test_stream() { + let mut codec = LengthPrefixedSnappyCodec::new(1024, 1024); + let src = random_bytes(1024); + let mut buf = BytesMut::new(); + codec.encode(src.clone(), &mut buf).unwrap(); + + let encoded_len = buf.len(); + let mut stream_buf = BytesMut::new(); + + // feed in bytes one at a time + for _ in 0..encoded_len - 1 { + stream_buf.put_u8(buf.split_to(1)[0]); + let res = codec.decode(&mut stream_buf).unwrap(); + assert!(res.is_none()); + } + + stream_buf.put_u8(buf.split_to(1)[0]); + let res = codec.decode(&mut stream_buf).unwrap().unwrap(); + assert_eq!(src, res); + } + + #[test] + fn test_invalid_compression() { + let buf = BytesMut::new(); + let mut writer = buf.writer(); + + let mut uvi_buf = unsigned_varint::encode::usize_buffer(); + let uvi = unsigned_varint::encode::usize(1024, &mut uvi_buf); + writer.write_all(uvi).unwrap(); + + let mut buf = writer.into_inner(); + let random = random_bytes(4096); + buf.put_slice(&random); + + let mut codec = LengthPrefixedSnappyCodec::new(0, 1024); + let res = codec.decode(&mut buf).unwrap_err(); + assert!(matches!(res, CodecError::IoError(_))); + } + + fn random_bytes(len: usize) -> Vec { + (0..len).map(|_| rand::random::()).collect() + } +} diff --git a/crates/network/src/rpc/message.rs b/crates/network/src/rpc/message.rs new file mode 100644 index 000000000..791c02d45 --- /dev/null +++ b/crates/network/src/rpc/message.rs @@ -0,0 +1,214 @@ +// This file is part of Rundler. +// +// Rundler is free software: you can redistribute it and/or modify it under the +// terms of the GNU Lesser General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later version. +// +// Rundler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with Rundler. +// If not, see https://www.gnu.org/licenses/. + +use ethers::types::H256; +use rundler_types::UserOperation; +use ssz_derive::{Decode, Encode}; + +pub(crate) const MAX_ERROR_MESSAGE_LEN: usize = 256; +pub(crate) const MAX_OPS_PER_REQUEST: usize = 4096; + +/// Request types +#[derive(Clone, Debug, PartialEq)] +pub(crate) enum Request { + Status(Status), + Goodbye(Goodbye), + Ping(Ping), + Metadata, + PooledUserOpHashes(PooledUserOpHashesRequest), + PooledUserOpsByHash(PooledUserOpsByHashRequest), +} + +impl Request { + /// Number of expected response chunks for the request + /// + /// Codec will wait to receive this many chunks before returning or timing out. + pub(crate) fn num_expected_response_chunks(&self) -> usize { + match self { + Request::Status(_) => 1, + Request::Goodbye(_) => 0, + Request::Ping(_) => 1, + Request::Metadata => 1, + Request::PooledUserOpHashes(_) => 1, + // TODO(danc): is this valid? What if the UO isn't found, need to handle less. + Request::PooledUserOpsByHash(r) => r.hashes.len(), + } + } +} + +/// Response types +#[derive(Clone, Debug, PartialEq)] +pub(crate) enum Response { + Status(Status), + Ping(Pong), + Metadata(Metadata), + PooledUserOpHashes(PooledUserOpHashesResponse), + PooledUserOpsByHash(PooledUserOpsByHashResponse), +} + +/// Response result +pub(crate) type ResponseResult = Result; + +/// Error for a response +#[derive(Clone, Debug, PartialEq)] +pub(crate) struct ResponseError { + /// Kind of error + pub(crate) kind: ErrorKind, + /// Error message, may be empty if could not decode as UTF8 string + pub(crate) message: String, +} + +/// Error kinds +#[derive(Clone, Debug, PartialEq)] +pub(crate) enum ErrorKind { + /// Request is invalid + InvalidRequest, + /// Server error + ServerError, + /// Resource unavailable + ResourceUnavailable, + Unknown, +} + +impl From for ErrorKind { + fn from(value: u8) -> Self { + match value { + 1 => ErrorKind::InvalidRequest, + 2 => ErrorKind::ServerError, + 3 => ErrorKind::ResourceUnavailable, + _ => ErrorKind::Unknown, + } + } +} + +impl From for u8 { + fn from(kind: ErrorKind) -> Self { + match kind { + ErrorKind::InvalidRequest => 1, + ErrorKind::ServerError => 2, + ErrorKind::ResourceUnavailable => 3, + ErrorKind::Unknown => 255, + } + } +} + +/// Status request/response +/// +/// TODO(danc): should supported mempools be moved to metadata? +#[derive(Clone, Debug, PartialEq, Encode, Decode)] +pub(crate) struct Status { + /// Hash ids of the supported mempools + pub(crate) supported_mempools: Vec, +} + +/// Ping request +#[derive(Clone, Debug, PartialEq, Encode, Decode)] +pub(crate) struct Ping { + /// Metadata sequence number + pub(crate) metadata_seq_number: u64, +} + +/// Pong response +#[derive(Clone, Debug, PartialEq, Encode, Decode)] +pub(crate) struct Pong { + /// Metadata sequence number + /// + /// Upon receipt a sequencer number, check if its greater than + /// the last one received. If so, request new metadata from the peer. + pub(crate) metadata_seq_number: u64, +} + +// Metadata response +#[derive(Clone, Debug, PartialEq, Encode, Decode)] +pub(crate) struct Metadata { + /// Metadata sequence number + pub(crate) seq_number: u64, + // TODO: spec has an invalid field here, add if needed +} + +/// Goodbye request +#[derive(Clone, Debug, PartialEq)] +pub(crate) struct Goodbye { + pub(crate) reason: GoodbyeReason, +} + +/// Goodbye reason +#[derive(Clone, Debug, PartialEq)] +pub(crate) enum GoodbyeReason { + /// Client is shutting down + ClientShutdown, + /// Irrelevant network, no mempools in common + IrrelevantNetwork, + /// Fault or error + FaultOrError, + /// Too many peers, so dropping this one + TooManyPeers, + /// Unknown reason + Unknown, +} + +impl From for GoodbyeReason { + fn from(value: u64) -> Self { + match value { + 1 => GoodbyeReason::ClientShutdown, + 2 => GoodbyeReason::IrrelevantNetwork, + 3 => GoodbyeReason::FaultOrError, + 129 => GoodbyeReason::TooManyPeers, + _ => GoodbyeReason::Unknown, + } + } +} + +impl From for u64 { + fn from(reason: GoodbyeReason) -> Self { + match reason { + GoodbyeReason::ClientShutdown => 1, + GoodbyeReason::IrrelevantNetwork => 2, + GoodbyeReason::FaultOrError => 3, + GoodbyeReason::TooManyPeers => 129, + GoodbyeReason::Unknown => 255, + } + } +} + +/// Pooled user op hashes request +#[derive(Clone, Debug, PartialEq, Encode, Decode)] +pub(crate) struct PooledUserOpHashesRequest { + /// Hash id of the mempool + pub(crate) mempool: H256, + /// Offset into the mempool + pub(crate) offset: u64, +} + +/// Pooled user op hashes response +#[derive(Clone, Debug, PartialEq, Encode, Decode)] +pub(crate) struct PooledUserOpHashesResponse { + /// More user ops are available + pub(crate) more_flag: bool, + /// Hashes of the user ops + pub(crate) hashes: Vec, +} + +/// Pooled user ops by hash request +#[derive(Clone, Debug, PartialEq, Encode, Decode)] +pub(crate) struct PooledUserOpsByHashRequest { + /// Hashes of the user ops + pub(crate) hashes: Vec, +} + +/// Pooled user ops by hash response +#[derive(Clone, Debug, PartialEq)] +pub(crate) struct PooledUserOpsByHashResponse { + /// User ops + pub(crate) user_ops: Vec, +} diff --git a/crates/network/src/rpc/mod.rs b/crates/network/src/rpc/mod.rs new file mode 100644 index 000000000..7db612ff8 --- /dev/null +++ b/crates/network/src/rpc/mod.rs @@ -0,0 +1,16 @@ +// This file is part of Rundler. +// +// Rundler is free software: you can redistribute it and/or modify it under the +// terms of the GNU Lesser General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later version. +// +// Rundler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with Rundler. +// If not, see https://www.gnu.org/licenses/. + +mod handler; +mod message; +mod protocol; diff --git a/crates/network/src/rpc/protocol.rs b/crates/network/src/rpc/protocol.rs new file mode 100644 index 000000000..c0e7f1add --- /dev/null +++ b/crates/network/src/rpc/protocol.rs @@ -0,0 +1,142 @@ +// This file is part of Rundler. +// +// Rundler is free software: you can redistribute it and/or modify it under the +// terms of the GNU Lesser General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later version. +// +// Rundler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with Rundler. +// If not, see https://www.gnu.org/licenses/. + +use super::handler::CodecError; +use crate::rpc::message::Request; + +const PROTOCOL_PREFIX: &str = "/account_abstraction/req"; + +/// A protocol in the request/response family. +/// +/// Identified by a schema and an encoding. +/// +/// Its string identifier is of the form: +/// `/account_abstraction_req/{message_name}/{version}/{encoding}` +#[derive(Clone, Debug)] +pub(crate) struct Protocol { + /// The schema of the protocol. A combination + /// of the message name and version. + pub(crate) schema: ProtocolSchema, + pub(crate) encoding: Encoding, + id: String, +} + +impl AsRef for Protocol { + fn as_ref(&self) -> &str { + self.id.as_ref() + } +} + +impl Protocol { + pub(crate) fn new(schema: ProtocolSchema, encoding: Encoding) -> Self { + let id = format!( + "{}/{}/{}", + PROTOCOL_PREFIX, + schema.as_ref(), + encoding.as_ref() + ); + Self { + schema, + encoding, + id, + } + } +} + +/// The encoding of a protocol. +/// +/// Currently only SSZSnappy is supported. +#[derive(Clone, Debug)] +pub(crate) enum Encoding { + SSZSnappy, +} + +impl AsRef for Encoding { + fn as_ref(&self) -> &str { + match self { + Encoding::SSZSnappy => "ssz_snappy", + } + } +} + +/// Known protocol schemas. +#[derive(Copy, Clone, Debug)] +pub(crate) enum ProtocolSchema { + StatusV1, + GoodbyeV1, + PingV1, + MetadataV1, + PooledUserOpHashesV1, + PooledUserOpsByHashV1, +} + +impl AsRef for ProtocolSchema { + fn as_ref(&self) -> &str { + match self { + ProtocolSchema::StatusV1 => "status/1", + ProtocolSchema::GoodbyeV1 => "goodbye/1", + ProtocolSchema::PingV1 => "ping/1", + ProtocolSchema::MetadataV1 => "metadata/1", + ProtocolSchema::PooledUserOpHashesV1 => "pooled_user_op_hashes/1", + ProtocolSchema::PooledUserOpsByHashV1 => "pooled_user_ops_by_hash/1", + } + } +} + +#[derive(Debug, thiserror::Error)] +pub(crate) enum ProtocolError { + #[error(transparent)] + CodecError(#[from] CodecError), + #[error("Stream timeout")] + StreamTimeout, + #[error("Stream ended unexpectedly")] + IncompleteStream, + #[error("{0}")] + Internal(String), +} + +/// Returns the list of supported protocols by Rundler. +pub(crate) fn supported_protocols() -> Vec { + vec![ + Protocol::new(ProtocolSchema::StatusV1, Encoding::SSZSnappy), + Protocol::new(ProtocolSchema::GoodbyeV1, Encoding::SSZSnappy), + Protocol::new(ProtocolSchema::PingV1, Encoding::SSZSnappy), + Protocol::new(ProtocolSchema::MetadataV1, Encoding::SSZSnappy), + Protocol::new(ProtocolSchema::PooledUserOpHashesV1, Encoding::SSZSnappy), + Protocol::new(ProtocolSchema::PooledUserOpsByHashV1, Encoding::SSZSnappy), + ] +} + +/// Returns the list of protocols supported by the given request. +pub(crate) fn request_protocols(req: &Request) -> Vec { + match req { + Request::Status(_) => vec![Protocol::new(ProtocolSchema::StatusV1, Encoding::SSZSnappy)], + Request::Goodbye(_) => vec![Protocol::new( + ProtocolSchema::GoodbyeV1, + Encoding::SSZSnappy, + )], + Request::Ping(_) => vec![Protocol::new(ProtocolSchema::PingV1, Encoding::SSZSnappy)], + Request::Metadata => vec![Protocol::new( + ProtocolSchema::MetadataV1, + Encoding::SSZSnappy, + )], + Request::PooledUserOpHashes(_) => vec![Protocol::new( + ProtocolSchema::PooledUserOpHashesV1, + Encoding::SSZSnappy, + )], + Request::PooledUserOpsByHash(_) => vec![Protocol::new( + ProtocolSchema::PooledUserOpsByHashV1, + Encoding::SSZSnappy, + )], + } +}