commit b3cd0d6386446dbc382473e7c9c4fd298c49254e Author: Uncle Stretch Date: Thu Dec 26 14:41:16 2024 +0300 initial draft for the ghost-walker Signed-off-by: Uncle Stretch diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2f7896d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..03c2114 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,4083 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", +] + +[[package]] +name = "anyhow" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[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 1.0.69", + "time", +] + +[[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.109", + "synstructure 0.12.6", +] + +[[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.109", +] + +[[package]] +name = "async-io" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-trait" +version = "0.1.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[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 = "asynchronous-codec" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a860072022177f903e59730004fb5dc13db9275b79bb2aef7ba8ce831956c233" +dependencies = [ + "bytes", + "futures-sink", + "futures-util", + "memchr", + "pin-project-lite", +] + +[[package]] +name = "attohttpc" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d9a9bf8b79a749ee0b911b91b671cc2b6c670bdbc7e3dfd537576ddc94bb2a2" +dependencies = [ + "http", + "log", + "url", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base-x" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" + +[[package]] +name = "cc" +version = "1.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "clap" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[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.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] +name = "data-encoding-macro" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1559b6cba622276d6d63706db152618eeb15b89b3e4041446b05876e352e639" +dependencies = [ + "data-encoding", + "data-encoding-macro-internal", +] + +[[package]] +name = "data-encoding-macro-internal" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "332d754c0af53bc87c108fed664d121ecf59207ec4196041f04d6ab9002ad33f" +dependencies = [ + "data-encoding", + "syn 1.0.109", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "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 = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "enum-as-inner" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "enum-as-inner" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-bounded" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b07bbbe7d7e78809544c6f718d875627addc73a7c3582447abc052cd3dc67e0" +dependencies = [ + "futures-timer", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "futures-rustls" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd3cf68c183738046838e300353e4716c674dc5e56890de4826801a6622a28" +dependencies = [ + "futures-io", + "rustls", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "ghost-walker" +version = "0.1.0" +dependencies = [ + "asynchronous-codec 0.7.0", + "bytes", + "clap", + "either", + "futures", + "hex", + "ip_network", + "libp2p", + "maxminddb", + "parity-scale-codec", + "pin-project", + "primitive-types", + "thiserror 2.0.9", + "tokio", + "tracing", + "tracing-subscriber", + "trust-dns-resolver", + "unsigned-varint 0.8.0", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[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.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.8", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "if-addrs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cabb0019d51a643781ff15c9c8a3e5dedc365c47211270f4e8f82812fedd8f0a" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "if-watch" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdf9d64cfcf380606e64f9a0bcf493616b65331199f984151a6fa11a7b3cde38" +dependencies = [ + "async-io", + "core-foundation", + "fnv", + "futures", + "if-addrs", + "ipnet", + "log", + "netlink-packet-core", + "netlink-packet-route", + "netlink-proto", + "netlink-sys", + "rtnetlink", + "system-configuration", + "tokio", + "windows", +] + +[[package]] +name = "igd-next" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064d90fec10d541084e7b39ead8875a5a80d9114a2b18791565253bae25f49e4" +dependencies = [ + "async-trait", + "attohttpc", + "bytes", + "futures", + "http", + "hyper", + "log", + "rand", + "tokio", + "url", + "xmltree", +] + +[[package]] +name = "impl-codec" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67aa010c1e3da95bf151bd8b4c059b2ed7e75387cdb969b4f8f2723a43f9941" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-serde" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a143eada6a1ec4aefa5049037a26a6d597bfd64f8c026d07b77133e02b7dd0b" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "indexmap" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ip_network" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2f047c0a98b2f299aa5d6d7088443570faae494e9ae1305e48be000c9e0eb1" + +[[package]] +name = "ipconfig" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" +dependencies = [ + "socket2 0.5.8", + "widestring", + "windows-sys 0.48.0", + "winreg", +] + +[[package]] +name = "ipnet" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" + +[[package]] +name = "ipnetwork" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" +dependencies = [ + "serde", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "js-sys" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "libp2p" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94495eb319a85b70a68b85e2389a95bb3555c71c49025b78c691a854a7e6464" +dependencies = [ + "bytes", + "either", + "futures", + "futures-timer", + "getrandom", + "instant", + "libp2p-allow-block-list", + "libp2p-connection-limits", + "libp2p-core", + "libp2p-dns", + "libp2p-identify", + "libp2p-identity", + "libp2p-kad", + "libp2p-mdns", + "libp2p-metrics", + "libp2p-noise", + "libp2p-ping", + "libp2p-quic", + "libp2p-request-response", + "libp2p-swarm", + "libp2p-tcp", + "libp2p-upnp", + "libp2p-websocket", + "libp2p-yamux", + "multiaddr", + "pin-project", + "rw-stream-sink", + "thiserror 1.0.69", +] + +[[package]] +name = "libp2p-allow-block-list" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55b46558c5c0bf99d3e2a1a38fd54ff5476ca66dd1737b12466a1824dd219311" +dependencies = [ + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "void", +] + +[[package]] +name = "libp2p-connection-limits" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f5107ad45cb20b2f6c3628c7b6014b996fcb13a88053f4569c872c6e30abf58" +dependencies = [ + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "void", +] + +[[package]] +name = "libp2p-core" +version = "0.40.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +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 1.0.69", + "unsigned-varint 0.7.2", + "void", +] + +[[package]] +name = "libp2p-dns" +version = "0.40.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6a18db73084b4da2871438f6239fef35190b05023de7656e877c18a00541a3b" +dependencies = [ + "async-trait", + "futures", + "libp2p-core", + "libp2p-identity", + "log", + "parking_lot", + "smallvec", + "trust-dns-resolver", +] + +[[package]] +name = "libp2p-identify" +version = "0.43.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a96638a0a176bec0a4bcaebc1afa8cf909b114477209d7456ade52c61cd9cd" +dependencies = [ + "asynchronous-codec 0.6.2", + "either", + "futures", + "futures-bounded", + "futures-timer", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "log", + "lru", + "quick-protobuf", + "quick-protobuf-codec", + "smallvec", + "thiserror 1.0.69", + "void", +] + +[[package]] +name = "libp2p-identity" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "257b5621d159b32282eac446bed6670c39c7dc68a200a992d8f056afa0066f6d" +dependencies = [ + "bs58", + "ed25519-dalek", + "hkdf", + "multihash", + "quick-protobuf", + "rand", + "sha2", + "thiserror 1.0.69", + "tracing", + "zeroize", +] + +[[package]] +name = "libp2p-kad" +version = "0.44.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16ea178dabba6dde6ffc260a8e0452ccdc8f79becf544946692fff9d412fc29d" +dependencies = [ + "arrayvec", + "asynchronous-codec 0.6.2", + "bytes", + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "log", + "quick-protobuf", + "quick-protobuf-codec", + "rand", + "sha2", + "smallvec", + "thiserror 1.0.69", + "uint 0.9.5", + "unsigned-varint 0.7.2", + "void", +] + +[[package]] +name = "libp2p-mdns" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a2567c305232f5ef54185e9604579a894fd0674819402bb0ac0246da82f52a" +dependencies = [ + "data-encoding", + "futures", + "if-watch", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "log", + "rand", + "smallvec", + "socket2 0.5.8", + "tokio", + "trust-dns-proto 0.22.0", + "void", +] + +[[package]] +name = "libp2p-metrics" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239ba7d28f8d0b5d77760dc6619c05c7e88e74ec8fbbe97f856f20a56745e620" +dependencies = [ + "instant", + "libp2p-core", + "libp2p-identify", + "libp2p-identity", + "libp2p-kad", + "libp2p-ping", + "libp2p-swarm", + "once_cell", + "prometheus-client", +] + +[[package]] +name = "libp2p-noise" +version = "0.43.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2eeec39ad3ad0677551907dd304b2f13f17208ccebe333bef194076cd2e8921" +dependencies = [ + "bytes", + "curve25519-dalek", + "futures", + "libp2p-core", + "libp2p-identity", + "log", + "multiaddr", + "multihash", + "once_cell", + "quick-protobuf", + "rand", + "sha2", + "snow", + "static_assertions", + "thiserror 1.0.69", + "x25519-dalek", + "zeroize", +] + +[[package]] +name = "libp2p-ping" +version = "0.43.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e702d75cd0827dfa15f8fd92d15b9932abe38d10d21f47c50438c71dd1b5dae3" +dependencies = [ + "either", + "futures", + "futures-timer", + "instant", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "log", + "rand", + "void", +] + +[[package]] +name = "libp2p-quic" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "130d451d83f21b81eb7b35b360bc7972aeafb15177784adc56528db082e6b927" +dependencies = [ + "bytes", + "futures", + "futures-timer", + "if-watch", + "libp2p-core", + "libp2p-identity", + "libp2p-tls", + "log", + "parking_lot", + "quinn", + "rand", + "ring 0.16.20", + "rustls", + "socket2 0.5.8", + "thiserror 1.0.69", + "tokio", +] + +[[package]] +name = "libp2p-request-response" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8e3b4d67870478db72bac87bfc260ee6641d0734e0e3e275798f089c3fecfd4" +dependencies = [ + "async-trait", + "futures", + "instant", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "log", + "rand", + "smallvec", + "void", +] + +[[package]] +name = "libp2p-swarm" +version = "0.43.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "580189e0074af847df90e75ef54f3f30059aedda37ea5a1659e8b9fca05c0141" +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 0.4.1", + "proc-macro-warning", + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "libp2p-tcp" +version = "0.40.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b558dd40d1bcd1aaaed9de898e9ec6a436019ecc2420dd0016e712fbb61c5508" +dependencies = [ + "futures", + "futures-timer", + "if-watch", + "libc", + "libp2p-core", + "libp2p-identity", + "log", + "socket2 0.5.8", + "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 0.16.20", + "rustls", + "rustls-webpki", + "thiserror 1.0.69", + "x509-parser", + "yasna", +] + +[[package]] +name = "libp2p-upnp" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82775a47b34f10f787ad3e2a22e2c1541e6ebef4fe9f28f3ac553921554c94c1" +dependencies = [ + "futures", + "futures-timer", + "igd-next", + "libp2p-core", + "libp2p-swarm", + "log", + "tokio", + "void", +] + +[[package]] +name = "libp2p-websocket" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "004ee9c4a4631435169aee6aad2f62e3984dc031c43b6d29731e8e82a016c538" +dependencies = [ + "either", + "futures", + "futures-rustls", + "libp2p-core", + "libp2p-identity", + "log", + "parking_lot", + "pin-project-lite", + "rw-stream-sink", + "soketto", + "thiserror 1.0.69", + "url", + "webpki-roots", +] + +[[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 1.0.69", + "yamux", +] + +[[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.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown", +] + +[[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 = "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 = "maxminddb" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6087e5d8ea14861bb7c7f573afbc7be3798d3ef0fae87ec4fd9a4de9a127c3c" +dependencies = [ + "ipnetwork", + "log", + "memchr", + "serde", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "multiaddr" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6351f60b488e04c1d21bc69e56b89cb3f5e8f5d22557d6e8031bdfd79b6961" +dependencies = [ + "arrayref", + "byteorder", + "data-encoding", + "libp2p-identity", + "multibase", + "multihash", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint 0.8.0", + "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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b430e7953c29dd6a09afc29ff0bb69c6e306329ee6794700aee27b76a1aea8d" +dependencies = [ + "core2", + "unsigned-varint 0.8.0", +] + +[[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 0.7.2", +] + +[[package]] +name = "netlink-packet-core" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4" +dependencies = [ + "anyhow", + "byteorder", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-route" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053998cea5a306971f88580d0829e90f270f940befd7cf928da179d4187a5a66" +dependencies = [ + "anyhow", + "bitflags 1.3.2", + "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 1.0.69", +] + +[[package]] +name = "netlink-proto" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b33524dc0968bfad349684447bfce6db937a9ac3332a1fe60c0c5a5ce63f21" +dependencies = [ + "bytes", + "futures", + "log", + "netlink-packet-core", + "netlink-sys", + "thiserror 1.0.69", + "tokio", +] + +[[package]] +name = "netlink-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16c903aa70590cb93691bf97a767c8d1d6122d2cc9070433deb3bbf36ce8bd23" +dependencies = [ + "bytes", + "futures", + "libc", + "log", + "tokio", +] + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.9", + "libc", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +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.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parity-scale-codec" +version = "3.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "polling" +version = "3.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi 0.4.0", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "primitive-types" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d15600a7d856470b7d278b3fe0e311fe28c2526348549f8ef2ff7db3299c87f5" +dependencies = [ + "fixed-hash", + "impl-codec", + "impl-serde", + "scale-info", + "uint 0.10.0", +] + +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit", +] + +[[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.90", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[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.90", +] + +[[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 0.6.2", + "bytes", + "quick-protobuf", + "thiserror 1.0.69", + "unsigned-varint 0.7.2", +] + +[[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", + "thiserror 1.0.69", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "141bf7dfde2fbc246bfd3fe12f2455aa24b0fbd9af535d8c86c7bd1381ff2b1a" +dependencies = [ + "bytes", + "rand", + "ring 0.16.20", + "rustc-hash", + "rustls", + "slab", + "thiserror 1.0.69", + "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.8", + "tracing", + "windows-sys 0.48.0", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rcgen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" +dependencies = [ + "pem", + "ring 0.16.20", + "time", + "yasna", +] + +[[package]] +name = "redox_syscall" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[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]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted 0.7.1", + "web-sys", + "winapi", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "rtnetlink" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a552eb82d19f38c3beed3f786bd23aa434ceb9ac43ab44419ca6d67a7e186c0" +dependencies = [ + "futures", + "log", + "netlink-packet-core", + "netlink-packet-route", + "netlink-packet-utils", + "netlink-proto", + "netlink-sys", + "nix", + "thiserror 1.0.69", + "tokio", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +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.38.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring 0.17.8", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring 0.17.8", + "untrusted 0.9.0", +] + +[[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 = "scale-info" +version = "2.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346a3b32eba2640d17a9cb5927056b08f3de90f65b72fe09402c2ad07d684d0b" +dependencies = [ + "cfg-if", + "derive_more", + "parity-scale-codec", + "scale-info-derive", +] + +[[package]] +name = "scale-info-derive" +version = "2.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6630024bf739e2179b91fb424b28898baf819414262c5d376677dbff1fe7ebf" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring 0.17.8", + "untrusted 0.9.0", +] + +[[package]] +name = "semver" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" + +[[package]] +name = "serde" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "snow" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "850948bee068e713b8ab860fe1adc4d109676ab4c3b621fd8147f06b261f2f85" +dependencies = [ + "aes-gcm", + "blake2", + "chacha20poly1305", + "curve25519-dalek", + "rand_core", + "ring 0.17.8", + "rustc_version", + "sha2", + "subtle", +] + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "soketto" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e859df029d160cb88608f5d7df7fb4753fd20fdfb4de5644f3d8b8440841721" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures", + "httparse", + "log", + "rand", + "sha1", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[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.109", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" +dependencies = [ + "thiserror-impl 2.0.9", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2 0.5.8", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "tokio-util" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[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 0.5.1", + "futures-channel", + "futures-io", + "futures-util", + "idna 0.2.3", + "ipnet", + "lazy_static", + "rand", + "smallvec", + "socket2 0.4.10", + "thiserror 1.0.69", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "trust-dns-proto" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3119112651c157f4488931a01e586aa459736e9d6046d3bd9105ffb69352d374" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner 0.6.1", + "futures-channel", + "futures-io", + "futures-util", + "idna 0.4.0", + "ipnet", + "once_cell", + "rand", + "smallvec", + "thiserror 1.0.69", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "trust-dns-resolver" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a3e6c3aff1718b3c73e395d1f35202ba2ffa847c6a62eea0db8fb4cfe30be6" +dependencies = [ + "cfg-if", + "futures-util", + "ipconfig", + "lru-cache", + "once_cell", + "parking_lot", + "rand", + "resolv-conf", + "smallvec", + "thiserror 1.0.69", + "tokio", + "tracing", + "trust-dns-proto 0.23.2", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "uint" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "unsigned-varint" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6889a77d49f1f013504cec6bf97a2c730394adedaeb1deb5ea08949a50541105" +dependencies = [ + "asynchronous-codec 0.6.2", + "bytes", +] + +[[package]] +name = "unsigned-varint" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06" +dependencies = [ + "asynchronous-codec 0.7.0", + "bytes", + "futures-io", + "futures-util", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna 1.0.3", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.90", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" + +[[package]] +name = "web-sys" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efc5cf48f83140dcaab716eeaea345f9e93d0018fb81162753a3f76c3397b538" +dependencies = [ + "windows-core", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dcc5b895a6377f1ab9fa55acedab1fd5ac0db66ad1e6c7f47e28a22e446a5dd" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core", + "serde", + "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 1.0.69", + "time", +] + +[[package]] +name = "xml-rs" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea8b391c9a790b496184c29f7f93b9ed5b16abb306c05415b68bcc16e4d06432" + +[[package]] +name = "xmltree" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7d8a75eaf6557bb84a65ace8609883db44a29951042ada9b393151532e41fcb" +dependencies = [ + "xml-rs", +] + +[[package]] +name = "yamux" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed0164ae619f2dc144909a9f082187ebb5893693d8c0196e8085283ccd4b776" +dependencies = [ + "futures", + "log", + "nohash-hasher", + "parking_lot", + "pin-project", + "rand", + "static_assertions", +] + +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "time", +] + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "synstructure 0.13.1", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "synstructure 0.13.1", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..833c755 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "ghost-walker" +version = "0.1.0" +edition = "2021" + +[dependencies] +asynchronous-codec = "0.7.0" +bytes = "1.9.0" +clap = { version = "4.5.23", features = ["derive"] } +codec = { version = "3.6.12", package = "parity-scale-codec", features = ["derive"] } +either = "1.13.0" +futures = "0.3.31" +hex = "0.4.3" +ip_network = "0.4.1" +libp2p = { version = "0.52.0", features = ["dns", "identify", "kad", "macros", "mdns", "noise", "ping", "tcp", "tokio", "yamux", "websocket", "request-response"] } +maxminddb = "0.24.0" +pin-project = "1.1.7" +primitive-types = { version = "0.13.1", default-features = false, features = ["codec", "scale-info", "serde"] } +thiserror = "2.0.9" +tokio = { version = "1.42.0", features = ["macros", "time", "rt-multi-thread"] } +tracing = "0.1.41" +tracing-subscriber = { version = "0.3.19", features = ["env-filter", "fmt"] } +trust-dns-resolver = "0.23.2" +unsigned-varint = { version = "0.8.0", features = ["futures", "asynchronous_codec"] } diff --git a/artifacts/GeoLite2-City.mmdb b/artifacts/GeoLite2-City.mmdb new file mode 100644 index 0000000..22ddf58 Binary files /dev/null and b/artifacts/GeoLite2-City.mmdb differ diff --git a/src/locator.rs b/src/locator.rs new file mode 100644 index 0000000..6b72aa2 --- /dev/null +++ b/src/locator.rs @@ -0,0 +1,76 @@ +use std::{collections::HashMap, net::IpAddr}; + +use trust_dns_resolver::{ + config::{ResolverConfig, ResolverOpts}, + TokioAsyncResolver, +}; +use maxminddb::{geoip2::City, Reader as GeoIpReader}; +use libp2p::{ + identify::Info, multiaddr::Protocol, + PeerId +}; + +pub struct Locator<'a> { + peers: &'a HashMap<&'a PeerId, &'a Info>, + db: maxminddb::Reader<&'static [u8]>, +} + +impl<'a> Locator<'a> { + const CITY_DATA: &'static [u8] = include_bytes!("../artifacts/GeoLite2-City.mmdb"); + + pub fn new(peers: &'a HashMap<&'a PeerId, &'a Info>) -> Self { + Self { + peers, + db: GeoIpReader::from_source(Self::CITY_DATA) + .expect("City data is always valid; qed"), + } + } + + pub async fn locate_peers(&self) { + let mut geo_peers: HashMap = HashMap::new(); + + let resolver = TokioAsyncResolver::tokio( + ResolverConfig::default(), + ResolverOpts::default(), + ); + + for (peer, info) in self.peers { + for addr in &info.listen_addrs { + let located = match addr.iter().next() { + Some(Protocol::Ip4(ip)) => self.locate(IpAddr::V4(ip)), + Some(Protocol::Ip6(ip)) => self.locate(IpAddr::V6(ip)), + Some(Protocol::Dns(dns)) | + Some(Protocol::Dns4(dns)) | + Some(Protocol::Dns6(dns)) => { + let Ok(lookup) = resolver.lookup_ip(dns.to_string()).await else { + continue; + }; + lookup.iter().find_map(|ip| self.locate(ip)) + } + _ => continue, + }; + + let Some(located) = located else { continue; }; + geo_peers.insert(**peer, located); + } + } + + for (peer, location) in geo_peers { + println!("\tPeer={peer} location={location}"); + } + } + + fn locate(&self, ip: IpAddr) -> Option { + let City { city, .. } = self.db.lookup(ip).ok()?; + + let city = city + .as_ref()? + .names + .as_ref()? + .get("en")? + .to_string() + .into_boxed_str(); + + Some(city.into_string()) + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..5ec0691 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,54 @@ +use clap::Parser; + +mod walker; +mod locator; +mod p2p; + +use walker::Walker; + +#[derive(Debug, Parser)] +struct Opts { + #[clap( + short, + long, + default_value = "0x7657c8c868863dea692178d462fe9018ac8f1e16f51be31eea8f29274d85525b")] + genesis: String, + + #[clap( + short, + long, + default_value = "/dns/bootnode69.chain.ghostchain.io/tcp/30334/p2p/12D3KooWF9SWxz9dmy6vfndQhoxqCa7PESaoFWEiF8Jkqh4xKDRf", + use_value_delimiter = true, + value_parser)] + bootnodes: Vec, + + #[clap( + short, + long, + default_value = "60", + value_parser = parse_duration, + )] + timeout: std::time::Duration, +} + +fn parse_duration(arg: &str) -> Result { + let seconds = arg.parse()?; + Ok(std::time::Duration::from_secs(seconds)) +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let filter = tracing_subscriber::filter::EnvFilter::from_default_env(); + tracing_subscriber::fmt() + .with_env_filter(filter) + .init(); + + let args = Opts::parse(); + Walker::new() + .with_genesis(args.genesis) + .with_bootnodes(args.bootnodes) + .with_timeout(args.timeout) + .build()? + .walk_around() + .await +} diff --git a/src/p2p/discovery.rs b/src/p2p/discovery.rs new file mode 100644 index 0000000..bbe626c --- /dev/null +++ b/src/p2p/discovery.rs @@ -0,0 +1,58 @@ +use std::time::Duration; + +use libp2p::{ + kad::{store::MemoryStore, Behaviour as Kademlia, Config as KademliaConfig}, + PeerId, StreamProtocol, +}; + +pub type Discovery = Kademlia; + +pub struct DiscoveryBuilder { + max_packet_size: usize, + record_ttl: Option, + provider_ttl: Option, + query_timeout: Duration, +} + +impl DiscoveryBuilder { + pub fn new() -> DiscoveryBuilder { + DiscoveryBuilder { + max_packet_size: 8192, + record_ttl: None, + provider_ttl: None, + query_timeout: Duration::from_secs(60), + } + } + + pub fn record_ttl(mut self, record_ttl: Option) -> Self { + self.record_ttl = record_ttl; + self + } + + pub fn provider_ttl(mut self, provider_ttl: Option) -> Self { + self.provider_ttl = provider_ttl; + self + } + + pub fn query_timeout(mut self, query_timeout: Duration) -> Self { + self.query_timeout = query_timeout; + self + } + + pub fn build(self, local_peer_id: PeerId, genesis_hash: &str) -> Discovery { + let mut config = KademliaConfig::default(); + config.set_max_packet_size(self.max_packet_size); + config.set_record_ttl(self.record_ttl); + config.set_provider_record_ttl(self.provider_ttl); + config.set_query_timeout(self.query_timeout); + + let kademlia_protocols = vec![ + StreamProtocol::try_from_owned(format!("/{genesis_hash}/kad")) + .expect("Protocol name starts with '/'; qed"), + ]; + config.set_protocol_names(kademlia_protocols.into_iter().map(Into::into).collect()); + + let store = MemoryStore::new(local_peer_id); + Kademlia::with_config(local_peer_id, store, config) + } +} diff --git a/src/p2p/mod.rs b/src/p2p/mod.rs new file mode 100644 index 0000000..bc4fb76 --- /dev/null +++ b/src/p2p/mod.rs @@ -0,0 +1,13 @@ +use libp2p::swarm::NetworkBehaviour; + +pub mod discovery; +pub mod notifications; +pub mod peer_behaviour; +pub mod transport; + +#[derive(NetworkBehaviour)] +pub struct Behaviour { + pub notifications: notifications::behaviour::Notifications, + pub peer_info: peer_behaviour::PeerBehaviour, + pub discovery: discovery::Discovery, +} diff --git a/src/p2p/notifications/behaviour.rs b/src/p2p/notifications/behaviour.rs new file mode 100644 index 0000000..c3d6726 --- /dev/null +++ b/src/p2p/notifications/behaviour.rs @@ -0,0 +1,308 @@ +#![allow(dead_code)] + +use crate::p2p::notifications::{ + handler::{ + NotificationsHandler, NotificationsHandlerFromBehavior, + NotificationsHandlerToBehavior, + }, + messages::BlockHash, + messages::ProtocolRole, +}; + +use bytes::BytesMut; +use futures::channel::mpsc; +use libp2p::{ + core::{ConnectedPoint, Endpoint}, + swarm::{ + derive_prelude::ConnectionEstablished, ConnectionClosed, ConnectionDenied, + ConnectionId, NetworkBehaviour, NotifyHandler, ToSwarm, + }, + Multiaddr, PeerId, +}; +use std::{ + collections::{HashMap, HashSet, VecDeque}, + task::{Poll, Waker}, +}; + +const LOG_TARGET: &str = "ghost-wlaker-behavior"; + +#[derive(Debug)] +pub enum NotificationsToSwarm { + CustomProtocolOpen { + peer_id: PeerId, + index: usize, + received_handshake: Vec, + inbound: bool, + sender: mpsc::Sender>, + }, + + CustomProtocolClosed { + peer_id: PeerId, + index: usize, + }, + + Notification { + peer_id: PeerId, + index: usize, + message: BytesMut, + }, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ProtocolsData { + pub genesis_hash: BlockHash, + pub node_role: ProtocolRole, +} + +pub struct Notifications { + events: VecDeque>, + peers_details: HashMap>, + data: ProtocolsData, + waker: Option, +} + +impl Notifications { + pub fn new(data: ProtocolsData) -> Self { + Notifications { + events: VecDeque::with_capacity(16), + peers_details: HashMap::default(), + data, + waker: None, + } + } + + fn propagate_event( + &mut self, + event: ToSwarm, + ) { + if let Some(waker) = self.waker.take() { + waker.wake(); + } + + self.events.push_back(event); + } +} + +impl NetworkBehaviour for Notifications { + type ConnectionHandler = NotificationsHandler; + type ToSwarm = NotificationsToSwarm; + + fn handle_pending_inbound_connection( + &mut self, + _connection_id: ConnectionId, + _local_addr: &Multiaddr, + _remote_addr: &Multiaddr, + ) -> Result<(), ConnectionDenied> { + Ok(()) + } + + fn handle_pending_outbound_connection( + &mut self, + _connection_id: ConnectionId, + _maybe_peer: Option, + _addresses: &[Multiaddr], + _effective_role: Endpoint, + ) -> Result, ConnectionDenied> { + Ok(Vec::new()) + } + + fn handle_established_inbound_connection( + &mut self, + _connection_id: libp2p::swarm::ConnectionId, + peer: libp2p::PeerId, + local_addr: &libp2p::Multiaddr, + remote_addr: &libp2p::Multiaddr, + ) -> Result, libp2p::swarm::ConnectionDenied> { + tracing::debug!(target: LOG_TARGET, "Notifications new inbound for peer={:?}", peer); + + let handler = NotificationsHandler::new( + peer, + ConnectedPoint::Listener { + local_addr: local_addr.clone(), + send_back_addr: remote_addr.clone(), + }, + self.data.clone(), + ); + + Ok(handler) + } + + fn handle_established_outbound_connection( + &mut self, + _connection_id: libp2p::swarm::ConnectionId, + peer: libp2p::PeerId, + addr: &libp2p::Multiaddr, + _role_override: libp2p::core::Endpoint, + ) -> Result, libp2p::swarm::ConnectionDenied> { + tracing::debug!(target: LOG_TARGET, "Notifications new outbound for peer={:?}", peer); + + let handler = NotificationsHandler::new( + peer, + ConnectedPoint::Dialer { + role_override: Endpoint::Dialer, + address: addr.clone(), + }, + self.data.clone(), + ); + + Ok(handler) + } + + fn on_swarm_event(&mut self, event: libp2p::swarm::FromSwarm) { + match event { + libp2p::swarm::FromSwarm::ConnectionEstablished(ConnectionEstablished { + peer_id, + connection_id, + .. + }) => { + tracing::debug!(target: LOG_TARGET, + "Notifications swarm connection established peer={:?} connection={:?}", + peer_id, + connection_id + ); + + self.peers_details + .entry(peer_id) + .and_modify(|entry| { + let _ = entry.insert(connection_id); + }) + .or_insert_with(|| { + let mut hash = HashSet::new(); + hash.insert(connection_id); + hash + }); + + for index in 0..2 { + self.propagate_event(ToSwarm::NotifyHandler { + peer_id, + handler: NotifyHandler::One(connection_id), + event: NotificationsHandlerFromBehavior::Open { index }, + }); + } + } + libp2p::swarm::FromSwarm::ConnectionClosed(ConnectionClosed { + peer_id, + connection_id, + .. + }) => { + tracing::debug!(target: LOG_TARGET, + "Notifications swarm connection closed peer={:?} connection={:?}", + peer_id, + connection_id + ); + + if let Some(details) = self.peers_details.get_mut(&peer_id) { + let removed = details.remove(&connection_id); + if !removed { + tracing::trace!(target: LOG_TARGET, + "Notifications swarm connection closed for untracked connection peer={:?} connection={:?}", + peer_id, + connection_id + ); + } + } else { + tracing::trace!(target: LOG_TARGET, + "Notifications swarm connection closed for untracked peer, peer={:?} connection={:?}", + peer_id, + connection_id + ); + } + + for index in 0..2 { + self.propagate_event(ToSwarm::NotifyHandler { + peer_id, + handler: NotifyHandler::One(connection_id), + event: NotificationsHandlerFromBehavior::Close { index }, + }); + } + } + _ => (), + } + } + + fn on_connection_handler_event( + &mut self, + peer_id: libp2p::PeerId, + connection_id: libp2p::swarm::ConnectionId, + event: libp2p::swarm::THandlerOutEvent, + ) { + tracing::debug!(target: LOG_TARGET, + "Notifications new substream for peer {:?} {:?}", + peer_id, + event + ); + + match event { + NotificationsHandlerToBehavior::HandshakeCompleted { + index, + handshake, + is_inbound, + sender, + .. + } => { + tracing::trace!(target: LOG_TARGET, + "Notifications handler complited handshake peer={:?} connection={:?} index={:?} handshake={:?}", + peer_id, + connection_id, + index, + handshake, + ); + + self.propagate_event(ToSwarm::GenerateEvent( + NotificationsToSwarm::CustomProtocolOpen { + index, + peer_id, + received_handshake: handshake, + inbound: is_inbound, + sender, + }, + )); + } + NotificationsHandlerToBehavior::HandshakeError { index } => { + tracing::trace!(target: LOG_TARGET, + "Notifications handler error handshake peer={:?} connection={:?} index={:?}", + peer_id, + connection_id, + index, + ); + } + NotificationsHandlerToBehavior::OpenDesiredByRemote { index } => { + // Note: extend to reject protocols for specific peers in the future. + self.propagate_event(ToSwarm::NotifyHandler { + peer_id, + handler: NotifyHandler::One(connection_id), + event: NotificationsHandlerFromBehavior::Open { index }, + }); + } + NotificationsHandlerToBehavior::CloseDesired { index } => { + self.propagate_event(ToSwarm::NotifyHandler { + peer_id, + handler: NotifyHandler::One(connection_id), + event: NotificationsHandlerFromBehavior::Close { index }, + }); + } + NotificationsHandlerToBehavior::Close { .. } => {} + NotificationsHandlerToBehavior::Notification { bytes, index } => { + self.propagate_event(ToSwarm::GenerateEvent(NotificationsToSwarm::Notification { + peer_id, + index, + message: bytes, + })); + } + } + } + + fn poll( + &mut self, + cx: &mut std::task::Context<'_>, + _params: &mut impl libp2p::swarm::PollParameters, + ) -> std::task::Poll>> { + self.waker = Some(cx.waker().clone()); + + if let Some(event) = self.events.pop_front() { + return Poll::Ready(event); + } + + Poll::Pending + } +} diff --git a/src/p2p/notifications/handler.rs b/src/p2p/notifications/handler.rs new file mode 100644 index 0000000..6d9c17d --- /dev/null +++ b/src/p2p/notifications/handler.rs @@ -0,0 +1,663 @@ +#![allow(dead_code)] + +use crate::p2p::notifications::{ + behaviour::ProtocolsData, + messages::BlockAnnouncesHandshake, + upgrades::{ + combine_upgrades::CombineUpgrades, + handshake::{ + HandshakeInbound, HandshakeInboundSubstream, HandshakeOutbound, + HandshakeOutboundSubstream, + }, + }, +}; +use bytes::BytesMut; +use codec::Encode; +use futures::{channel::mpsc, prelude::*, SinkExt}; +use libp2p::{ + core::ConnectedPoint, + swarm::{ + handler::{ConnectionEvent, FullyNegotiatedInbound}, + ConnectionHandler, ConnectionHandlerEvent, KeepAlive, + Stream as NegotiatedSubstream, SubstreamProtocol, + }, + PeerId, +}; +use std::{ + collections::VecDeque, + mem, + pin::Pin, + task::{Context, Poll}, +}; + +const LOG_TARGET: &str = "ghost-walker-handler"; + +pub struct ProtocolDetails { + pub name: String, + pub handshake: Vec, + pub upgrade: HandshakeInbound, + pub state: State, +} + +pub struct NotificationsHandler { + protocols: Vec, + + pending_events: VecDeque< + ConnectionHandlerEvent< + HandshakeOutbound, + usize, + NotificationsHandlerToBehavior, + NotificationsHandlerError, + >, + >, + + endpoint: ConnectedPoint, + peer: PeerId, +} + +#[derive(Debug, Clone)] +pub enum NotificationsHandlerFromBehavior { + Open { index: usize }, + Close { index: usize }, +} + +#[derive(Debug, Clone)] +pub enum NotificationsHandlerToBehavior { + HandshakeCompleted { + index: usize, + endpoint: ConnectedPoint, + handshake: Vec, + is_inbound: bool, + sender: mpsc::Sender>, + }, + HandshakeError { + index: usize, + }, + OpenDesiredByRemote { + index: usize, + }, + CloseDesired { + index: usize, + }, + Close { + index: usize, + }, + Notification { + index: usize, + bytes: BytesMut, + }, +} + +pub enum State { + Closed { + pending_opening: bool, + }, + OpenDesiredByRemote { + inbound_substream: HandshakeInboundSubstream, + pending_opening: bool, + }, + Opening { + inbound_substream: Option>, + inbound: bool, + }, + Open { + recv: stream::Peekable>>, + inbound_substream: Option>, + outbound_substream: Option>, + }, +} + +impl NotificationsHandler { + pub fn new(peer: PeerId, endpoint: ConnectedPoint, data: ProtocolsData) -> Self { + let genesis_string = hex::encode(data.genesis_hash); + let blocks = format!("/{}/block-announces/1", genesis_string); + + let tx = format!("/{}/transactions/1", genesis_string); + let block_announces = BlockAnnouncesHandshake::from_genesis(data.genesis_hash); + + let protocols = vec![ + ProtocolDetails { + name: blocks.clone(), + handshake: block_announces.encode(), + upgrade: HandshakeInbound { + name: blocks.clone(), + }, + state: State::Closed { + pending_opening: false, + }, + }, + ProtocolDetails { + name: tx.clone(), + handshake: vec![data.node_role.encoded()], + upgrade: HandshakeInbound { name: tx.clone() }, + state: State::Closed { + pending_opening: false, + }, + }, + ]; + + NotificationsHandler { + peer, + pending_events: VecDeque::with_capacity(16), + endpoint, + protocols, + } + } +} + +#[derive(Debug, thiserror::Error)] +pub enum NotificationsHandlerError {} + +impl ConnectionHandler for NotificationsHandler { + // Received and submitted events. + type FromBehaviour = NotificationsHandlerFromBehavior; + type ToBehaviour = NotificationsHandlerToBehavior; + + type Error = NotificationsHandlerError; + + // Handle handshakes. + type InboundProtocol = CombineUpgrades; + type OutboundProtocol = HandshakeOutbound; + + // Extra information upon connections. + type OutboundOpenInfo = usize; + type InboundOpenInfo = (); + + fn listen_protocol(&self) -> SubstreamProtocol { + let protocol_upgrades: Vec<_> = self.protocols.iter().map(|p| p.upgrade.clone()).collect(); + let combine_upgrades = CombineUpgrades::from(protocol_upgrades); + SubstreamProtocol::new(combine_upgrades, ()) + } + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + '_, + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + match event { + ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { + protocol, .. + }) => { + let (mut stream, index) = (protocol.data, protocol.index); + + tracing::debug!(target: LOG_TARGET, + "Handler negotiated inbound peer={:?} index={:?}", + self.peer, + index + ); + + let proto = &mut self.protocols[index]; + match proto.state { + State::Closed { pending_opening } => { + tracing::trace!( + target: LOG_TARGET, + "Handler negotiated inbound Closed -> OpenDesiredByRemote peer={:?} index={:?}", + self.peer, + index + ); + + self.pending_events + .push_back(ConnectionHandlerEvent::NotifyBehaviour( + NotificationsHandlerToBehavior::OpenDesiredByRemote { index }, + )); + + proto.state = State::OpenDesiredByRemote { + inbound_substream: stream.substream, + pending_opening, + }; + } + State::OpenDesiredByRemote { .. } => { + tracing::trace!( + target: LOG_TARGET, + "Handler negotiated inbound OpenDesiredByRemote peer={:?} index={:?}", + self.peer, + index + ); + } + State::Opening { + ref mut inbound_substream, + .. + } + | State::Open { + ref mut inbound_substream, + .. + } => { + if inbound_substream.is_some() { + tracing::trace!( + target: LOG_TARGET, + "Handler negotiated inbound handshake already handled peer={:?} index={:?}", + self.peer, + index + ); + return; + } + + tracing::trace!( + target: LOG_TARGET, + "Handler negotiated inbound setup handshake peer={:?} index={:?}", + self.peer, + index + ); + + let handshake_message = proto.handshake.clone(); + stream.substream.set_handshake(handshake_message); + *inbound_substream = Some(stream.substream); + } + } + } + ConnectionEvent::FullyNegotiatedOutbound(outbound) => { + let (opened, index) = (outbound.protocol, outbound.info); + + tracing::debug!( + target: LOG_TARGET, + "Handler negotiated outbound peer={:?} index={:?}", + self.peer, + index + ); + + let proto = &mut self.protocols[index]; + match proto.state { + State::Closed { + ref mut pending_opening, + } + | State::OpenDesiredByRemote { + ref mut pending_opening, + .. + } => { + tracing::trace!( + target: LOG_TARGET, + "Handler negotiated outbound Closed|OpenDesiredByRemote peer={:?} index={:?}", + self.peer, + index + ); + + *pending_opening = false; + } + State::Opening { + ref mut inbound_substream, + inbound, + } => { + tracing::trace!( + target: LOG_TARGET, + "Handler negotiated outbound Opening successful peer={:?} index={:?}", + self.peer, + index + ); + + let (send, recv) = mpsc::channel(1024); + proto.state = State::Open { + inbound_substream: inbound_substream.take(), + outbound_substream: Some(opened.substream), + recv: recv.peekable(), + }; + + self.pending_events + .push_back(ConnectionHandlerEvent::NotifyBehaviour( + NotificationsHandlerToBehavior::HandshakeCompleted { + index, + endpoint: self.endpoint.clone(), + handshake: opened.handshake, + is_inbound: inbound, + sender: send, + }, + )); + } + State::Open { .. } => { + tracing::trace!( + target: LOG_TARGET, + "Handler negotiated outbound Open missmatch-state peer={:?} index={:?}", + self.peer, + index + ); + } + } + } + ConnectionEvent::DialUpgradeError(err) => { + tracing::debug!( + target: LOG_TARGET, + "Handler DialError peer={:?} index={:?} error={:?}", + self.peer, + err.info, + err.error, + ); + + let proto = &mut self.protocols[err.info]; + + match proto.state { + State::Closed { + ref mut pending_opening, + } + | State::OpenDesiredByRemote { + ref mut pending_opening, + .. + } => { + tracing::trace!( + target: LOG_TARGET, + "Handler DialError Closed|OpenDesiredByRemote peer={:?} info={:?}", + self.peer, + err.info, + ); + + *pending_opening = false; + } + State::Opening { .. } => { + proto.state = State::Closed { + pending_opening: false, + }; + + tracing::trace!( + target: LOG_TARGET, + "Handler DialError Opening -> Closed peer={:?} info={:?}", + self.peer, + err.info, + ); + + self.pending_events + .push_back(ConnectionHandlerEvent::NotifyBehaviour( + NotificationsHandlerToBehavior::HandshakeError { index: err.info }, + )); + } + State::Open { .. } => {} + } + } + _ => {} + } + } + + fn on_behaviour_event(&mut self, message: NotificationsHandlerFromBehavior) { + match message { + NotificationsHandlerFromBehavior::Open { index } => { + tracing::debug!( + target: LOG_TARGET, + "Handler from behaviour Open peer={:?} index={:?}", + self.peer, + index + ); + + let proto = &mut self.protocols[index]; + + match &mut proto.state { + State::Closed { pending_opening } => { + if !*pending_opening { + let protocol = HandshakeOutbound { + name: proto.name.clone(), + handshake: proto.handshake.clone(), + }; + + tracing::trace!( + target: LOG_TARGET, + "Handler from behaviour Closed -> request new substream peer={:?} index={:?}", + self.peer, + index + ); + + self.pending_events.push_back( + ConnectionHandlerEvent::OutboundSubstreamRequest { + protocol: SubstreamProtocol::new(protocol, index), + }, + ) + } + + tracing::trace!( + target: LOG_TARGET, + "Handler from behaviour Closed -> Opening peer={:?} index={:?}", + self.peer, + index + ); + + proto.state = State::Opening { + inbound_substream: None, + inbound: false, + }; + } + State::OpenDesiredByRemote { + inbound_substream, + pending_opening, + } => { + if !*pending_opening { + let protocol = HandshakeOutbound { + name: proto.name.clone(), + handshake: proto.handshake.clone(), + }; + + tracing::trace!( + target: LOG_TARGET, + "Handler from behaviour OpenDesiredByRemote -> request new substream peer={:?} index={:?}", + self.peer, + index + ); + + self.pending_events.push_back( + ConnectionHandlerEvent::OutboundSubstreamRequest { + protocol: SubstreamProtocol::new(protocol, index), + }, + ) + } + + tracing::trace!( + target: LOG_TARGET, + "Handler from behaviour OpenDesiredByRemote setup handshake peer={:?} index={:?}", + self.peer, + index + ); + + let handshake = proto.handshake.clone(); + inbound_substream.set_handshake(handshake); + + let inbound_substream = match mem::replace( + &mut proto.state, + State::Opening { + inbound_substream: None, + inbound: false, + }, + ) { + State::OpenDesiredByRemote { + inbound_substream, .. + } => inbound_substream, + _ => unreachable!(), + }; + proto.state = State::Opening { + inbound_substream: Some(inbound_substream), + inbound: true, + }; + } + State::Opening { .. } | State::Open { .. } => { + tracing::trace!( + target: LOG_TARGET, + "Handler from behaviour Opening|Open statemissmatch peer={:?} index={:?}", + self.peer, + index + ); + } + } + } + + NotificationsHandlerFromBehavior::Close { index } => { + tracing::debug!( + target: LOG_TARGET, + "Handler from behaviour Close peer={:?} index={:?}", + self.peer, + index + ); + + let proto = &mut self.protocols[index]; + + match proto.state { + State::Closed { .. } => {} + State::OpenDesiredByRemote { + pending_opening, .. + } => { + proto.state = State::Closed { pending_opening }; + } + State::Opening { .. } => { + proto.state = State::Closed { + pending_opening: true, + }; + + tracing::trace!( + target: LOG_TARGET, + "Handler from behaviour Close with handshake in progress peer={:?} index={:?}", + self.peer, + index + ); + + self.pending_events + .push_back(ConnectionHandlerEvent::NotifyBehaviour( + NotificationsHandlerToBehavior::HandshakeError { index }, + )); + } + State::Open { .. } => { + proto.state = State::Closed { + pending_opening: false, + }; + } + } + + self.pending_events + .push_back(ConnectionHandlerEvent::NotifyBehaviour( + NotificationsHandlerToBehavior::Close { index }, + )); + } + } + } + + fn connection_keep_alive(&self) -> KeepAlive { + if self + .protocols + .iter() + .any(|p| !matches!(p.state, State::Closed { .. })) + { + return KeepAlive::Yes; + } + + KeepAlive::No + } + + #[allow(deprecated)] + fn poll( + &mut self, + cx: &mut Context, + ) -> Poll, + > { + if let Some(ev) = self.pending_events.pop_front() { + return Poll::Ready(ev); + } + + for index in 0..self.protocols.len() { + if let State::Open { + outbound_substream: Some(outbound_substream), + recv, + .. + } = &mut self.protocols[index].state + { + loop { + match Pin::new(&mut *recv).as_mut().poll_peek(cx) { + Poll::Ready(Some(..)) => {} + _ => break, + }; + + match outbound_substream.poll_ready_unpin(cx) { + Poll::Ready(_) => {} + Poll::Pending => break, + }; + + let message = match recv.poll_next_unpin(cx) { + Poll::Ready(Some(message)) => message, + Poll::Ready(None) | Poll::Pending => { + debug_assert!(false); + break; + } + }; + + tracing::trace!( + target: LOG_TARGET, + "Handler poll send message peer={:?} index={:?} message={:?}", + self.peer, + index, + message + ); + + let _ = outbound_substream.start_send_unpin(message); + } + } + } + + for index in 0..self.protocols.len() { + if let State::Open { + outbound_substream: outbound_substream @ Some(_), + .. + } = &mut self.protocols[index].state + { + match Sink::poll_flush(Pin::new(outbound_substream.as_mut().unwrap()), cx) { + Poll::Pending | Poll::Ready(Ok(())) => {} + Poll::Ready(Err(_)) => { + *outbound_substream = None; + + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( + NotificationsHandlerToBehavior::CloseDesired { index }, + )); + } + } + } + } + + for index in 0..self.protocols.len() { + match &mut self.protocols[index].state { + State::Open { + inbound_substream: inbound_substream @ Some(_), + .. + } => match Stream::poll_next(Pin::new(inbound_substream.as_mut().unwrap()), cx) { + Poll::Pending => {} + Poll::Ready(Some(Ok(bytes))) => { + let event = NotificationsHandlerToBehavior::Notification { index, bytes }; + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(event)); + } + Poll::Ready(None) | Poll::Ready(Some(Err(_))) => *inbound_substream = None, + }, + + State::OpenDesiredByRemote { + inbound_substream, + pending_opening, + } => match HandshakeInboundSubstream::poll_process(Pin::new(inbound_substream), cx) + { + Poll::Pending => {} + Poll::Ready(Ok(void)) => match void {}, + Poll::Ready(Err(_)) => { + self.protocols[index].state = State::Closed { + pending_opening: *pending_opening, + }; + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( + NotificationsHandlerToBehavior::CloseDesired { index }, + )); + } + }, + + State::Opening { + inbound_substream: inbound_substream @ Some(_), + .. + } => match HandshakeInboundSubstream::poll_process( + Pin::new(inbound_substream.as_mut().unwrap()), + cx, + ) { + Poll::Pending => {} + Poll::Ready(Ok(void)) => match void {}, + Poll::Ready(Err(_)) => *inbound_substream = None, + }, + + _ => (), + } + } + + Poll::Pending + } +} diff --git a/src/p2p/notifications/messages.rs b/src/p2p/notifications/messages.rs new file mode 100644 index 0000000..080385a --- /dev/null +++ b/src/p2p/notifications/messages.rs @@ -0,0 +1,76 @@ +#![allow(dead_code)] + +use codec::{Decode, Encode, Input, Output}; +use hex::FromHexError; + +pub type BlockHash = primitive_types::H256; +pub type BlockNumber = u32; + +#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] +pub struct BlockAnnouncesHandshake { + pub roles: u8, + pub best_number: BlockNumber, + pub best_hash: BlockHash, + pub genesis_hash: BlockHash, +} + +impl BlockAnnouncesHandshake { + pub fn from_genesis(genesis_hash: BlockHash) -> Self { + Self { + roles: 4, + best_number: 0, + best_hash: genesis_hash, + genesis_hash, + } + } + + pub fn from_hex_genesis(genesis_hash: &str) -> Result { + let raw_bytes = hex::decode(genesis_hash.trim_start_matches("0x"))?; + let genesis_hash = primitive_types::H256::from_slice(raw_bytes.as_slice()); + Ok(Self::from_genesis(genesis_hash)) + } +} + +mod role_bytes { + pub const FULL_NODE: u8 = 0b_0000_0001; + pub const LIGHT_NODE: u8 = 0b_0000_0010; + pub const AUTHORITY: u8 = 0b_0000_0100; +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ProtocolRole { + FullNode, + LightNode, + Authority, +} + +impl ProtocolRole { + pub fn encoded(&self) -> u8 { + match self { + ProtocolRole::FullNode => role_bytes::FULL_NODE, + ProtocolRole::LightNode => role_bytes::LIGHT_NODE, + ProtocolRole::Authority => role_bytes::AUTHORITY, + } + } +} + +impl Encode for ProtocolRole { + fn encode_to(&self, dest: &mut T) { + dest.push_byte(self.encoded()) + } +} + +impl codec::Decode for ProtocolRole { + fn decode(input: &mut I) -> Result { + let byte = input.read_byte()?; + + let role = match byte { + role_bytes::FULL_NODE => ProtocolRole::FullNode, + role_bytes::LIGHT_NODE => ProtocolRole::LightNode, + role_bytes::AUTHORITY => ProtocolRole::Authority, + _ => return Err(codec::Error::from("Invalid bytes")), + }; + + Ok(role) + } +} diff --git a/src/p2p/notifications/mod.rs b/src/p2p/notifications/mod.rs new file mode 100644 index 0000000..1d858c8 --- /dev/null +++ b/src/p2p/notifications/mod.rs @@ -0,0 +1,4 @@ +pub mod behaviour; +pub mod handler; +pub mod messages; +pub mod upgrades; diff --git a/src/p2p/notifications/upgrades/combine_upgrades.rs b/src/p2p/notifications/upgrades/combine_upgrades.rs new file mode 100644 index 0000000..441fdc6 --- /dev/null +++ b/src/p2p/notifications/upgrades/combine_upgrades.rs @@ -0,0 +1,97 @@ +use futures::prelude::*; +use libp2p::core::upgrade::{InboundUpgrade, UpgradeInfo}; +use std::{ + pin::Pin, + task::{Context, Poll}, + vec, +}; + +#[derive(Debug, Clone)] +pub struct CombineUpgrades(pub Vec); + +impl From> for CombineUpgrades { + fn from(list: Vec) -> Self { + Self(list) + } +} + +impl UpgradeInfo for CombineUpgrades { + type Info = ProtocolResponse; + type InfoIter = vec::IntoIter; + + fn protocol_info(&self) -> Self::InfoIter { + self.0 + .iter() + .enumerate() + .flat_map(|(index, protocol)| { + protocol + .protocol_info() + .into_iter() + .map(move |data| ProtocolResponse { data, index }) + }) + .collect::>() + .into_iter() + } +} + +impl InboundUpgrade for CombineUpgrades +where + T: InboundUpgrade, +{ + type Output = ProtocolResponse; + type Error = ProtocolResponse; + type Future = FutureProtocolResponse; + + fn upgrade_inbound(mut self, sock: C, info: Self::Info) -> Self::Future { + // Negociated only once. + let protocol = self.0.remove(info.index); + let future = protocol.upgrade_inbound(sock, info.data); + + FutureProtocolResponse { + data: future, + index: info.index, + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct ProtocolResponse { + pub data: T, + pub index: usize, +} + +impl> AsRef for ProtocolResponse { + fn as_ref(&self) -> &str { + self.data.as_ref() + } +} + +#[pin_project::pin_project] +pub struct FutureProtocolResponse { + #[pin] + data: T, + index: usize, +} + +impl Future for FutureProtocolResponse +where + T: Future>, +{ + type Output = Result, ProtocolResponse>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.project(); + + match Future::poll(this.data, cx) { + Poll::Ready(Ok(value)) => Poll::Ready(Ok(ProtocolResponse { + data: value, + index: *this.index, + })), + Poll::Ready(Err(error)) => Poll::Ready(Err(ProtocolResponse { + data: error, + index: *this.index, + })), + Poll::Pending => Poll::Pending, + } + } +} diff --git a/src/p2p/notifications/upgrades/handshake.rs b/src/p2p/notifications/upgrades/handshake.rs new file mode 100644 index 0000000..50447cd --- /dev/null +++ b/src/p2p/notifications/upgrades/handshake.rs @@ -0,0 +1,454 @@ +#![allow(dead_code)] + +use asynchronous_codec::Framed; +use bytes::BytesMut; +use futures::prelude::*; +use libp2p::core::{upgrade, InboundUpgrade, OutboundUpgrade, UpgradeInfo}; +use unsigned_varint::codec::UviBytes; + +const LOG_TARGET: &str = "ghost-walker-upgrades"; + +use std::{ + convert::Infallible, + io, mem, + pin::Pin, + task::{Context, Poll}, + vec, +}; + +const MAX_HANDSHAKE_SIZE: usize = 1024; + +#[derive(Debug, Clone)] +pub struct HandshakeInbound { + pub name: String, +} + +#[pin_project::pin_project] +pub struct HandshakeInboundSubstream { + #[pin] + socket: Framed>>>, + state: HandshakeInboundSubstreamState, + negotiated_name: String, +} + +#[derive(Debug)] +pub enum HandshakeInboundSubstreamState { + Waiting, + Sending(Vec), + Flush, + Done, + NeedsClose, + FullyClosed, +} + +#[derive(Debug, Clone)] +pub struct HandshakeOutbound { + pub name: String, + pub handshake: Vec, +} + +#[pin_project::pin_project] +pub struct HandshakeOutboundSubstream { + #[pin] + socket: Framed>>>, +} + +impl HandshakeInbound { + pub fn new(name: impl Into) -> Self { + Self { name: name.into() } + } +} + +impl UpgradeInfo for HandshakeInbound { + type Info = String; + type InfoIter = vec::IntoIter; + + fn protocol_info(&self) -> Self::InfoIter { + vec![self.name.clone()].into_iter() + } +} + +impl InboundUpgrade for HandshakeInbound +where + TSubstream: AsyncRead + AsyncWrite + Unpin + Send + 'static, +{ + type Output = HandshakeInboundOpen; + type Future = Pin> + Send>>; + type Error = HandshakeError; + + fn upgrade_inbound(self, mut socket: TSubstream, negotiated_name: Self::Info) -> Self::Future { + tracing::debug!( + target: LOG_TARGET, + "HandshakeInbound name={:?} current_name={:?}", + negotiated_name, + self.name + ); + + Box::pin(async move { + let handshake_len = unsigned_varint::aio::read_usize(&mut socket).await?; + tracing::trace!( + target: LOG_TARGET, + "HandshakeInbound length={:?} name={:?}", + handshake_len, + negotiated_name + ); + + if handshake_len > MAX_HANDSHAKE_SIZE { + return Err(HandshakeError::TooLarge { + requested: handshake_len, + max: MAX_HANDSHAKE_SIZE, + }); + } + + let mut handshake = vec![0u8; handshake_len]; + if !handshake.is_empty() { + socket.read_exact(&mut handshake).await?; + } + + tracing::trace!( + target: LOG_TARGET, + "HandshakeInbound received handshake={:?} name={:?}", + handshake, + negotiated_name + ); + + let mut codec: UviBytes>> = UviBytes::default(); + codec.set_max_len(usize::MAX); + + let substream = HandshakeInboundSubstream { + socket: Framed::new(socket, codec), + state: HandshakeInboundSubstreamState::Waiting, + negotiated_name, + }; + + Ok(HandshakeInboundOpen { + handshake, + substream, + }) + }) + } +} + +pub struct HandshakeInboundOpen { + pub handshake: Vec, + pub substream: HandshakeInboundSubstream, +} + +impl HandshakeInboundSubstream +where + TSubstream: AsyncRead + AsyncWrite + Unpin, +{ + pub fn set_handshake(&mut self, handshake: Vec) { + match &self.state { + HandshakeInboundSubstreamState::Waiting => (), + _ => return, + }; + + self.state = HandshakeInboundSubstreamState::Sending(handshake); + } + + pub fn poll_process( + self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll> { + let mut this = self.project(); + + loop { + let state = mem::replace(this.state, HandshakeInboundSubstreamState::Done); + + match state { + HandshakeInboundSubstreamState::Sending(handshake) => { + match Sink::poll_ready(this.socket.as_mut(), cx) { + Poll::Ready(_) => { + tracing::trace!(target: LOG_TARGET, "HandshakeInboundSubstream: poll_process: Sink is ready start sending name={:?}", this.negotiated_name); + + *this.state = HandshakeInboundSubstreamState::Flush; + + match Sink::start_send(this.socket.as_mut(), io::Cursor::new(handshake)) + { + Ok(()) => {} + Err(err) => { + tracing::error!(target: LOG_TARGET, "HandshakeInboundSubstream: poll_process: Failed to start sending name={:?} error={:?}", this.negotiated_name, err); + + return Poll::Ready(Err(err)); + } + } + } + Poll::Pending => { + *this.state = HandshakeInboundSubstreamState::Sending(handshake); + return Poll::Pending; + } + } + } + + HandshakeInboundSubstreamState::Flush => { + tracing::trace!( + target: LOG_TARGET, + "HandshakeInboundSubstream: poll_process: poll_flush name={:?}", + this.negotiated_name + ); + match Sink::poll_flush(this.socket.as_mut(), cx)? { + Poll::Ready(()) => *this.state = HandshakeInboundSubstreamState::Done, + Poll::Pending => { + *this.state = HandshakeInboundSubstreamState::Flush; + return Poll::Pending; + } + } + } + + state => { + *this.state = state; + return Poll::Pending; + } + } + } + } +} + +impl Stream for HandshakeInboundSubstream +where + TSubstream: AsyncRead + AsyncWrite + Unpin, +{ + type Item = Result; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let mut this = self.project(); + + loop { + let state = mem::replace(this.state, HandshakeInboundSubstreamState::Done); + + tracing::trace!( + target: LOG_TARGET, + "HandshakeInboundSubstream: poll_next: state={:?} name={:?}", + state, + this.negotiated_name + ); + + match state { + HandshakeInboundSubstreamState::Waiting => { + *this.state = HandshakeInboundSubstreamState::Waiting; + return Poll::Pending; + } + HandshakeInboundSubstreamState::Sending(handshake) => { + match Sink::poll_ready(this.socket.as_mut(), cx) { + Poll::Ready(_) => { + *this.state = HandshakeInboundSubstreamState::Flush; + + match Sink::start_send(this.socket.as_mut(), io::Cursor::new(handshake)) + { + Ok(()) => (), + Err(err) => { + tracing::trace!( + target: LOG_TARGET, + "HandshakeInboundSubstream: Cannot send handshake name={:?}", + this.negotiated_name + ); + + return Poll::Ready(Some(Err(err))); + } + } + } + Poll::Pending => { + *this.state = HandshakeInboundSubstreamState::Sending(handshake); + return Poll::Pending; + } + } + } + HandshakeInboundSubstreamState::Flush => { + match Sink::poll_flush(this.socket.as_mut(), cx)? { + Poll::Ready(()) => *this.state = HandshakeInboundSubstreamState::Done, + Poll::Pending => { + *this.state = HandshakeInboundSubstreamState::Flush; + return Poll::Pending; + } + } + } + HandshakeInboundSubstreamState::Done => { + match Stream::poll_next(this.socket.as_mut(), cx) { + Poll::Ready(None) => { + tracing::trace!( + target: LOG_TARGET, + "HandshakeInboundSubstream: poll_next: Closing in response to peer name={:?}", + this.negotiated_name + ); + *this.state = HandshakeInboundSubstreamState::NeedsClose + } + Poll::Ready(Some(result)) => { + *this.state = HandshakeInboundSubstreamState::Done; + return Poll::Ready(Some(result)); + } + Poll::Pending => { + *this.state = HandshakeInboundSubstreamState::Done; + return Poll::Pending; + } + } + } + HandshakeInboundSubstreamState::NeedsClose => { + match Sink::poll_close(this.socket.as_mut(), cx)? { + Poll::Ready(()) => { + tracing::trace!( + target: LOG_TARGET, + "HandshakeInboundSubstream: poll_close: fully clsoed name={:?}", + this.negotiated_name + ); + *this.state = HandshakeInboundSubstreamState::FullyClosed + } + Poll::Pending => { + *this.state = HandshakeInboundSubstreamState::NeedsClose; + return Poll::Pending; + } + } + } + HandshakeInboundSubstreamState::FullyClosed => return Poll::Ready(None), + } + } + } +} + +impl HandshakeOutbound { + pub fn new(name: impl Into, handshake: impl Into>) -> Self { + Self { + name: name.into(), + handshake: handshake.into(), + } + } +} + +impl UpgradeInfo for HandshakeOutbound { + type Info = String; + type InfoIter = vec::IntoIter; + + fn protocol_info(&self) -> Self::InfoIter { + vec![self.name.clone()].into_iter() + } +} + +impl OutboundUpgrade for HandshakeOutbound +where + TSubstream: AsyncRead + AsyncWrite + Unpin + Send + 'static, +{ + type Output = HandshakeOutboundOpen; + type Future = Pin> + Send>>; + type Error = HandshakeError; + + fn upgrade_outbound(self, mut socket: TSubstream, negotiated_name: Self::Info) -> Self::Future { + tracing::debug!( + target: LOG_TARGET, + "HandshakeOutbound name={:?} current_name={:?}", + negotiated_name, + self.name + ); + + Box::pin(async move { + tracing::trace!( + target: LOG_TARGET, + "HandshakeOutbound prepare to write handshake={:?} name={:?}", + self.handshake, + negotiated_name + ); + + upgrade::write_length_prefixed(&mut socket, &self.handshake).await?; + + tracing::trace!( + target: LOG_TARGET, + "HandshakeOutbound prepare to read handshake length name={:?}", + negotiated_name + ); + + let handshake_len = unsigned_varint::aio::read_usize(&mut socket).await?; + + tracing::trace!( + target: LOG_TARGET, + "HandshakeOutbound handshake len={:?} name={:?}", + handshake_len, + negotiated_name + ); + + if handshake_len > MAX_HANDSHAKE_SIZE { + return Err(HandshakeError::TooLarge { + requested: handshake_len, + max: MAX_HANDSHAKE_SIZE, + }); + } + + let mut handshake = vec![0u8; handshake_len]; + if !handshake.is_empty() { + socket.read_exact(&mut handshake).await?; + } + + let mut codec = UviBytes::default(); + codec.set_max_len(usize::MAX); + + Ok(HandshakeOutboundOpen { + handshake, + substream: HandshakeOutboundSubstream { + socket: Framed::new(socket, codec), + }, + }) + }) + } +} + +pub struct HandshakeOutboundOpen { + pub handshake: Vec, + pub substream: HandshakeOutboundSubstream, +} + +impl Sink> for HandshakeOutboundSubstream +where + TSubstream: AsyncRead + AsyncWrite + Unpin, +{ + type Error = std::io::Error; + + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let mut this = self.project(); + Sink::poll_ready(this.socket.as_mut(), cx) + } + + fn start_send(self: Pin<&mut Self>, item: Vec) -> Result<(), Self::Error> { + let mut this = self.project(); + Sink::start_send(this.socket.as_mut(), io::Cursor::new(item)) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let mut this = self.project(); + Sink::poll_flush(this.socket.as_mut(), cx) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let mut this = self.project(); + Sink::poll_close(this.socket.as_mut(), cx) + } +} + +#[derive(Debug, thiserror::Error)] +pub enum HandshakeError { + #[error(transparent)] + Io(#[from] io::Error), + #[error("Initial message or handshake was too large: {requested}")] + TooLarge { + requested: usize, + max: usize, + }, + #[error(transparent)] + VarintDecode(#[from] unsigned_varint::decode::Error), +} + +impl From for HandshakeError { + fn from(err: unsigned_varint::io::ReadError) -> Self { + match err { + unsigned_varint::io::ReadError::Io(err) => Self::Io(err), + unsigned_varint::io::ReadError::Decode(err) => Self::VarintDecode(err), + _ => { + tracing::warn!(target: LOG_TARGET, "Unrecognized varint decoding error"); + Self::Io(From::from(io::ErrorKind::InvalidData)) + } + } + } +} + +#[derive(Debug, thiserror::Error)] +pub enum HandshakeOutboundError { + #[error(transparent)] + Io(#[from] io::Error), +} diff --git a/src/p2p/notifications/upgrades/mod.rs b/src/p2p/notifications/upgrades/mod.rs new file mode 100644 index 0000000..e2bf6c2 --- /dev/null +++ b/src/p2p/notifications/upgrades/mod.rs @@ -0,0 +1,2 @@ +pub mod handshake; +pub mod combine_upgrades; diff --git a/src/p2p/peer_behaviour.rs b/src/p2p/peer_behaviour.rs new file mode 100644 index 0000000..9940084 --- /dev/null +++ b/src/p2p/peer_behaviour.rs @@ -0,0 +1,438 @@ +use std::{ + collections::{HashMap, HashSet}, + task::{Context, Poll}, +}; + +use either::Either; +use libp2p::{ + core::{ConnectedPoint, Endpoint}, + identify::{Behaviour as Identify, Config as IdentifyConfig, Event as IdentifyEvent, Info as IdentifyInfo}, + ping::{Behaviour as Ping, Config as PingConfig}, + swarm::{ + behaviour::{ + AddressChange, ConnectionClosed, ConnectionEstablished, DialFailure, + FromSwarm, ListenFailure, + }, + ConnectionDenied, ConnectionHandler, ConnectionHandlerSelect, ConnectionId, + NetworkBehaviour, PollParameters, THandler, THandlerInEvent, THandlerOutEvent, + ToSwarm, + }, + identity::PublicKey, + Multiaddr, PeerId, +}; + +pub const AGENT: &str = "ghost-walker-agent"; +const LOG_TARGET: &str = "ghost-walker-peer-behaviour"; + +pub struct PeerBehaviour { + ping: Ping, + identify: Identify, + details: HashMap, + external_addresses: HashSet, +} + +impl PeerBehaviour { + pub fn new(local_public_key: PublicKey) -> PeerBehaviour { + let identify_config = IdentifyConfig::new("/substrate/1.0".to_string(), local_public_key) + .with_agent_version(AGENT.to_string()) + // Do not cache peer info. + .with_cache_size(0); + let identify = Identify::new(identify_config); + + Self { + ping: Ping::new(PingConfig::new()), + identify, + details: HashMap::default(), + external_addresses: HashSet::default(), + } + } +} + +struct NodeDetails { + pub connections: Vec, + pub protocol_version: Option, + pub agent_version: Option, + pub protocols: HashSet, +} + +impl NodeDetails { + pub fn new(connection: ConnectedPoint) -> NodeDetails { + const INITIAL_CAPACITY: usize = 16; + + let mut connections = Vec::with_capacity(INITIAL_CAPACITY); + connections.push(connection); + + NodeDetails { + connections, + protocol_version: None, + agent_version: None, + protocols: HashSet::new(), + } + } +} + +#[derive(Debug)] +pub enum PeerInfoEvent { + Identified { + peer_id: PeerId, + info: IdentifyInfo, + }, +} + +impl NetworkBehaviour for PeerBehaviour { + type ConnectionHandler = ConnectionHandlerSelect< + ::ConnectionHandler, + ::ConnectionHandler, + >; + type ToSwarm = PeerInfoEvent; + + fn handle_pending_inbound_connection( + &mut self, + connection_id: ConnectionId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result<(), ConnectionDenied> { + self.ping + .handle_pending_inbound_connection(connection_id, local_addr, remote_addr)?; + self.identify + .handle_pending_inbound_connection(connection_id, local_addr, remote_addr) + } + + fn handle_pending_outbound_connection( + &mut self, + _connection_id: ConnectionId, + _maybe_peer: Option, + _addresses: &[Multiaddr], + _effective_role: Endpoint, + ) -> Result, ConnectionDenied> { + // Multiaddr is returned by other protocols. + Ok(Vec::new()) + } + + fn handle_established_inbound_connection( + &mut self, + connection_id: ConnectionId, + peer: PeerId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result, ConnectionDenied> { + let ping_handler = self.ping.handle_established_inbound_connection( + connection_id, + peer, + local_addr, + remote_addr, + )?; + let identify_handler = self.identify.handle_established_inbound_connection( + connection_id, + peer, + local_addr, + remote_addr, + )?; + Ok(ping_handler.select(identify_handler)) + } + + fn handle_established_outbound_connection( + &mut self, + connection_id: ConnectionId, + peer: PeerId, + addr: &Multiaddr, + role_override: Endpoint, + ) -> Result, ConnectionDenied> { + let ping_handler = self.ping.handle_established_outbound_connection( + connection_id, + peer, + addr, + role_override, + )?; + let identify_handler = self.identify.handle_established_outbound_connection( + connection_id, + peer, + addr, + role_override, + )?; + Ok(ping_handler.select(identify_handler)) + } + + fn on_swarm_event(&mut self, event: FromSwarm) { + match event { + FromSwarm::ConnectionEstablished( + e @ ConnectionEstablished { + peer_id, endpoint, .. + }, + ) => { + self.ping + .on_swarm_event(FromSwarm::ConnectionEstablished(e)); + self.identify + .on_swarm_event(FromSwarm::ConnectionEstablished(e)); + + self.details + .entry(peer_id) + .and_modify(|details| { + details.connections.push(endpoint.clone()); + }) + .or_insert_with(|| NodeDetails::new(endpoint.clone())); + } + FromSwarm::ConnectionClosed(ConnectionClosed { + peer_id, + connection_id, + endpoint, + handler, + remaining_established, + }) => { + let (ping_handler, identity_handler) = handler.into_inner(); + self.ping + .on_swarm_event(FromSwarm::ConnectionClosed(ConnectionClosed { + peer_id, + connection_id, + endpoint, + handler: ping_handler, + remaining_established, + })); + self.identify + .on_swarm_event(FromSwarm::ConnectionClosed(ConnectionClosed { + peer_id, + connection_id, + endpoint, + handler: identity_handler, + remaining_established, + })); + + if let Some(node) = self.details.get_mut(&peer_id) { + node.connections.retain(|conn| conn != endpoint) + } + } + FromSwarm::DialFailure(DialFailure { + peer_id, + error, + connection_id, + }) => { + self.ping + .on_swarm_event(FromSwarm::DialFailure(DialFailure { + peer_id, + error, + connection_id, + })); + self.identify + .on_swarm_event(FromSwarm::DialFailure(DialFailure { + peer_id, + error, + connection_id, + })); + } + FromSwarm::ListenerClosed(e) => { + self.ping.on_swarm_event(FromSwarm::ListenerClosed(e)); + self.identify.on_swarm_event(FromSwarm::ListenerClosed(e)); + } + FromSwarm::ListenFailure(ListenFailure { + local_addr, + send_back_addr, + error, + connection_id, + }) => { + self.ping + .on_swarm_event(FromSwarm::ListenFailure(ListenFailure { + local_addr, + send_back_addr, + error, + connection_id, + })); + self.identify + .on_swarm_event(FromSwarm::ListenFailure(ListenFailure { + local_addr, + send_back_addr, + error, + connection_id, + })); + } + FromSwarm::ListenerError(e) => { + self.ping.on_swarm_event(FromSwarm::ListenerError(e)); + self.identify.on_swarm_event(FromSwarm::ListenerError(e)); + } + FromSwarm::NewListener(e) => { + self.ping.on_swarm_event(FromSwarm::NewListener(e)); + self.identify.on_swarm_event(FromSwarm::NewListener(e)); + } + FromSwarm::ExpiredListenAddr(e) => { + self.ping.on_swarm_event(FromSwarm::ExpiredListenAddr(e)); + self.identify + .on_swarm_event(FromSwarm::ExpiredListenAddr(e)); + } + FromSwarm::AddressChange( + e @ AddressChange { + peer_id, old, new, .. + }, + ) => { + self.ping.on_swarm_event(FromSwarm::AddressChange(e)); + self.identify.on_swarm_event(FromSwarm::AddressChange(e)); + + self.details.entry(peer_id).and_modify(|details| { + details + .connections + .iter_mut() + .find(|conn| conn == &old) + .map(|conn| *conn = new.clone()); + }); + } + FromSwarm::NewListenAddr(e) => { + self.ping.on_swarm_event(FromSwarm::NewListenAddr(e)); + self.identify.on_swarm_event(FromSwarm::NewListenAddr(e)); + } + FromSwarm::NewExternalAddrCandidate(e) => { + self.ping + .on_swarm_event(FromSwarm::NewExternalAddrCandidate(e)); + self.identify + .on_swarm_event(FromSwarm::NewExternalAddrCandidate(e)); + } + FromSwarm::ExternalAddrConfirmed(e) => { + self.ping + .on_swarm_event(FromSwarm::ExternalAddrConfirmed(e)); + self.identify + .on_swarm_event(FromSwarm::ExternalAddrConfirmed(e)); + + self.external_addresses.insert(e.addr.clone()); + } + FromSwarm::ExternalAddrExpired(e) => { + self.external_addresses.remove(e.addr); + } + } + } + + fn on_connection_handler_event( + &mut self, + peer_id: PeerId, + connection_id: ConnectionId, + event: THandlerOutEvent, + ) { + match event { + Either::Left(event) => { + self.ping + .on_connection_handler_event(peer_id, connection_id, event) + } + Either::Right(event) => { + self.identify + .on_connection_handler_event(peer_id, connection_id, event) + } + } + } + + fn poll( + &mut self, + cx: &mut Context, + params: &mut impl PollParameters, + ) -> Poll>> { + loop { + match self.ping.poll(cx, params) { + Poll::Pending => break, + Poll::Ready(ToSwarm::GenerateEvent(ev)) => { + tracing::debug!(target: LOG_TARGET, + "PingEvent peer_id={:?} connection_id={:?} result {:?}", + ev.peer, + ev.connection, + ev.result + ); + } + Poll::Ready(ToSwarm::Dial { opts }) => return Poll::Ready(ToSwarm::Dial { opts }), + Poll::Ready(ToSwarm::NotifyHandler { + peer_id, + handler, + event, + }) => { + return Poll::Ready(ToSwarm::NotifyHandler { + peer_id, + handler, + event: Either::Left(event), + }) + } + Poll::Ready(ToSwarm::CloseConnection { + peer_id, + connection, + }) => { + return Poll::Ready(ToSwarm::CloseConnection { + peer_id, + connection, + }) + } + Poll::Ready(ToSwarm::ListenOn { opts }) => { + return Poll::Ready(ToSwarm::ListenOn { opts }) + } + Poll::Ready(ToSwarm::RemoveListener { id }) => { + return Poll::Ready(ToSwarm::RemoveListener { id }) + } + Poll::Ready(ToSwarm::ExternalAddrExpired(address)) => { + return Poll::Ready(ToSwarm::ExternalAddrExpired(address)) + } + Poll::Ready(ToSwarm::ExternalAddrConfirmed(address)) => { + return Poll::Ready(ToSwarm::ExternalAddrConfirmed(address)) + } + Poll::Ready(ToSwarm::NewExternalAddrCandidate(address)) => { + return Poll::Ready(ToSwarm::NewExternalAddrCandidate(address)) + } + } + } + + loop { + match self.identify.poll(cx, params) { + Poll::Pending => break, + Poll::Ready(ToSwarm::GenerateEvent(event)) => match event { + IdentifyEvent::Received { peer_id, info, .. } => { + self.details.entry(peer_id).and_modify(|details| { + details.agent_version = Some(info.agent_version.clone()); + details.protocol_version = Some(info.protocol_version.clone()); + details.protocols = info + .protocols + .iter() + .map(|proto| proto.to_string()) + .collect(); + }); + + let event = PeerInfoEvent::Identified { peer_id, info }; + return Poll::Ready(ToSwarm::GenerateEvent(event)); + } + IdentifyEvent::Error { peer_id, error } => { + tracing::debug!(target: LOG_TARGET, "Identification with peer={:?} error={}", peer_id, error) + } + IdentifyEvent::Pushed { .. } => {} + IdentifyEvent::Sent { .. } => {} + }, + Poll::Ready(ToSwarm::Dial { opts }) => return Poll::Ready(ToSwarm::Dial { opts }), + Poll::Ready(ToSwarm::NotifyHandler { + peer_id, + handler, + event, + }) => { + return Poll::Ready(ToSwarm::NotifyHandler { + peer_id, + handler, + event: Either::Right(event), + }) + } + Poll::Ready(ToSwarm::ListenOn { opts }) => { + return Poll::Ready(ToSwarm::ListenOn { opts }) + } + Poll::Ready(ToSwarm::RemoveListener { id }) => { + return Poll::Ready(ToSwarm::RemoveListener { id }) + } + Poll::Ready(ToSwarm::ExternalAddrExpired(address)) => { + return Poll::Ready(ToSwarm::ExternalAddrExpired(address)) + } + Poll::Ready(ToSwarm::ExternalAddrConfirmed(address)) => { + return Poll::Ready(ToSwarm::ExternalAddrConfirmed(address)) + } + Poll::Ready(ToSwarm::NewExternalAddrCandidate(address)) => { + return Poll::Ready(ToSwarm::NewExternalAddrCandidate(address)) + } + Poll::Ready(ToSwarm::CloseConnection { + peer_id, + connection, + }) => { + return Poll::Ready(ToSwarm::CloseConnection { + peer_id, + connection, + }) + } + } + } + + Poll::Pending + } +} diff --git a/src/p2p/transport.rs b/src/p2p/transport.rs new file mode 100644 index 0000000..d549d99 --- /dev/null +++ b/src/p2p/transport.rs @@ -0,0 +1,73 @@ +#![allow(dead_code)] + +use libp2p::{ + core::{muxing::StreamMuxerBox, transport::Boxed, upgrade}, + dns, identity, noise, tcp, websocket, PeerId, Transport, +}; +use std::time::Duration; + +pub const KIB: usize = 1024; +pub const MIB: usize = 1024 * KIB; + +pub struct TransportBuilder { + timeout: Duration, + yamux_window_size: u32, + yamux_maximum_buffer_size: usize, +} + +impl TransportBuilder { + pub fn new() -> TransportBuilder { + TransportBuilder { + timeout: Duration::from_secs(20), + yamux_window_size: 256 * (KIB as u32), + yamux_maximum_buffer_size: MIB, + } + } + + pub fn timeout(mut self, timeout: Duration) -> Self { + self.timeout = timeout; + self + } + + pub fn yamux_window_size(mut self, yamux_window_size: u32) -> Self { + self.yamux_window_size = yamux_window_size; + self + } + + pub fn yamux_maximum_buffer_size(mut self, yamux_maximum_buffer_size: usize) -> Self { + self.yamux_maximum_buffer_size = yamux_maximum_buffer_size; + self + } + + pub fn build(self, keypair: identity::Keypair) -> Boxed<(PeerId, StreamMuxerBox)> { + let tcp_config = tcp::Config::new().nodelay(true); + let tcp_trans = tcp::tokio::Transport::new(tcp_config.clone()); + let dns = dns::tokio::Transport::system(tcp_trans).expect("Can construct DNS; qed"); + + let tcp_trans = tcp::tokio::Transport::new(tcp_config); + let dns_for_wss = dns::tokio::Transport::system(tcp_trans) + .expect("Valid config provided; qed"); + + let transport = websocket::WsConfig::new(dns_for_wss).or_transport(dns); + + let authentication_config = + noise::Config::new(&keypair).expect("Can create noise config; qed"); + + let multiplexing_config = { + let mut yamux_config = libp2p::yamux::Config::default(); + + yamux_config.set_window_update_mode(libp2p::yamux::WindowUpdateMode::on_read()); + yamux_config.set_max_buffer_size(self.yamux_maximum_buffer_size); + yamux_config.set_receive_window_size(self.yamux_window_size); + + yamux_config + }; + + transport + .upgrade(upgrade::Version::V1Lazy) + .authenticate(authentication_config) + .multiplex(multiplexing_config) + .timeout(self.timeout) + .boxed() + } +} diff --git a/src/walker.rs b/src/walker.rs new file mode 100644 index 0000000..32b22b9 --- /dev/null +++ b/src/walker.rs @@ -0,0 +1,312 @@ +use std::{ + collections::{hash_map::Entry, HashMap, HashSet}, + time::Duration, + error::Error, +}; + +use futures::StreamExt; +use codec::Decode; +use ip_network::IpNetwork; +use primitive_types::H256; +use libp2p::{ + identify::Info, identity, kad::{ + Event as KademliaEvent, GetClosestPeersError, GetClosestPeersOk, + QueryId, QueryResult, + }, + multiaddr::Protocol, + swarm::{Config as SwarmConfig, SwarmEvent}, + Multiaddr, PeerId, Swarm +}; + +use crate::{ + locator::Locator, + p2p::{notifications::behaviour::NotificationsToSwarm, BehaviourEvent}, +}; + +use super::p2p::{ + discovery::DiscoveryBuilder, + peer_behaviour::{PeerInfoEvent, PeerBehaviour}, + transport::{TransportBuilder, MIB}, + notifications::{ + behaviour::{Notifications, ProtocolsData}, + messages::ProtocolRole, + }, + Behaviour, +}; + +pub struct Walker { + genesis: String, + timeout: Duration, + swarm: Swarm, + queries: HashSet, + discovered_with_address: HashMap>, + peer_details: HashMap, + peer_role: HashMap, + dialed_peers: HashMap, +} + +impl Walker { + pub fn new() -> WalkerBuilder { + WalkerBuilder::default() + } + + fn insert_queries(&mut self, num: usize) { + for _ in 0..num { + self.queries.insert( + self.swarm + .behaviour_mut() + .discovery + .get_closest_peers(PeerId::random())); + } + } + + fn dialed_peer(&mut self, peer_id: Option) { + let Some(peer_id) = peer_id else { return }; + + self.dialed_peers + .entry(peer_id) + .and_modify(|num| *num += 1) + .or_insert(0); + } + + async fn inner_walk(&mut self) { + self.insert_queries(128); + let mut previous_tracing_time = std::time::Instant::now(); + + loop { + let event = self.swarm.select_next_some().await; + + match event { + SwarmEvent::Dialing { peer_id, .. } => self.dialed_peer(peer_id), + SwarmEvent::Behaviour(BehaviourEvent::Discovery(event)) => match event { + KademliaEvent::OutboundQueryProgressed { + id, + result: QueryResult::GetClosestPeers(result), + .. + } => { + self.queries.remove(&id); + + let peers = match result { + Ok(GetClosestPeersOk { peers, ..}) => peers, + Err(GetClosestPeersError::Timeout { peers, .. }) => peers + }; + let num_discovered = peers.len(); + + let now = std::time::Instant::now(); + if now.duration_since(previous_tracing_time) > Duration::from_secs(10) { + previous_tracing_time = now; + tracing::info!("...Discovery in progress last query #{num_discovered}"); + } + + if self.queries.is_empty() { + self.insert_queries(128); + } + } + KademliaEvent::RoutingUpdated { peer, addresses, .. } => { + match self.discovered_with_address.entry(peer) { + Entry::Occupied(mut occupied) => { + occupied.get_mut().extend(addresses.into_vec()); + }, + Entry::Vacant(vacant) => { + vacant.insert(addresses.iter().cloned().collect()); + }, + }; + } + KademliaEvent::RoutablePeer { peer, address } | KademliaEvent::PendingRoutablePeer { peer, address } => { + match self.discovered_with_address.entry(peer) { + Entry::Occupied(mut occupied) => { + occupied.get_mut().insert(address); + }, + Entry::Vacant(vacant) => { + let mut set = HashSet::new(); + set.insert(address); + vacant.insert(set); + }, + }; + } + _ => (), + }, + SwarmEvent::Behaviour(BehaviourEvent::PeerInfo(info_event)) => match info_event { + PeerInfoEvent::Identified { peer_id, info } => { + tracing::debug!("Identified peer_id={:?} info={:?}", peer_id, info); + self.peer_details.insert(peer_id, info); + } + }, + SwarmEvent::Behaviour(BehaviourEvent::Notifications( + NotificationsToSwarm::CustomProtocolOpen { + peer_id, + index, + received_handshake, + inbound, + .. + } + )) => { + if let Ok(role) = ProtocolRole::decode(&mut &received_handshake[..]) { + tracing::debug!("Identified peer_id={:?} role={:?}", peer_id, role); + self.peer_role.insert(peer_id, role); + } + + tracing::debug!( + "Protocol open peer={:?} index={:?} handshake={:?} inbound={:?}", + peer_id, + index, + received_handshake, + inbound, + ); + } + _ => (), + } + } + } + + fn is_public_adress(&self, addr: &Multiaddr) -> bool { + let ip = match addr.iter().next() { + Some(Protocol::Ip4(ip)) => IpNetwork::from(ip), + Some(Protocol::Ip6(ip)) => IpNetwork::from(ip), + Some(Protocol::Dns(_)) | Some(Protocol::Dns4(_)) | Some(Protocol::Dns6(_)) => return true, + _ => return false, + }; + ip.is_global() + } + + pub async fn walk_around(&mut self) -> Result<(), Box> { + let _ = tokio::time::timeout(self.timeout, self.inner_walk()).await; + + println!("Number of dialed peers: {}", self.dialed_peers.len()); + println!("Total peers discovered: {}", self.discovered_with_address.len()); + println!("Peers with local iden.: {}", self.peer_details.len()); + + let infos: HashMap<_, _> = self + .peer_details + .iter() + .filter(|(_, info)| { + info.protocols + .iter() + .any(|stream_proto| stream_proto.as_ref().contains(&self.genesis)) + }) + .collect(); + println!("Support correct genesis {}: {}", &self.genesis, infos.len()); + + let peers_with_public: HashMap<_, _> = infos + .iter() + .filter(|(_, info)| info.listen_addrs + .iter() + .any(|a| self.is_public_adress(&a))) + .collect(); + println!("Peers with public address: {}", peers_with_public.len()); + println!("Peers with private address: {}", infos.len() - peers_with_public.len()); + println!("Peers with associated role: {}", self.peer_role.len()); + + Locator::new(&infos).locate_peers().await; + + Ok(()) + } +} + +pub struct WalkerBuilder { + genesis: String, + bootnodes: Vec, + timeout: Duration, +} + +impl Default for WalkerBuilder { + fn default() -> Self { + Self { + genesis: Default::default(), + bootnodes: Default::default(), + timeout: Default::default(), + } + } +} + +impl WalkerBuilder { + fn create_swarm(&self) -> Result, Box> { + let local_key = identity::Keypair::generate_ed25519(); + let local_peer_id = PeerId::from(local_key.public()); + tracing::info!("Local peer Id {:?}", local_peer_id); + + let bootnodes = self.bootnodes + .iter() + .map(|bootnode| { + let parts: Vec<_> = bootnode.split('/').collect(); + let peer = parts.last().expect("Bootnode peer should be valid; qed"); + let multiaddress: Multiaddr = bootnode + .parse() + .expect("Bootnode multiaddress should be valid; qed"); + let peer_id: PeerId = peer + .parse() + .expect("Bootnode should have valid peer ID; qed"); + + tracing::info!("Bootnode peer={:?}", peer_id); + (peer_id, multiaddress) + }) + .collect::>(); + + let protocol_data = ProtocolsData { + genesis_hash: H256::from_slice(hex::decode(self.genesis.clone())?.as_slice()), + node_role: ProtocolRole::FullNode, + }; + + let mut swarm: Swarm = { + let transport = TransportBuilder::new() + .yamux_maximum_buffer_size(256 * MIB) + .build(local_key.clone()); + + let discovery = DiscoveryBuilder::new() + .record_ttl(Some(Duration::from_secs(0))) + .provider_ttl(Some(Duration::from_secs(0))) + .query_timeout(Duration::from_secs(5 * 60)) + .build(local_peer_id, &self.genesis); + + let peer_info = PeerBehaviour::new(local_key.public()); + let notifications = Notifications::new(protocol_data); + + let behavior = Behaviour { + notifications, + peer_info, + discovery, + }; + + let config = SwarmConfig::with_tokio_executor(); + Swarm::new(transport, behavior, local_peer_id, config) + }; + + for (peer, multiaddress) in &bootnodes { + swarm + .behaviour_mut() + .discovery + .add_address(peer, multiaddress.clone()); + } + + Ok(swarm) + } + + pub fn with_genesis(mut self, genesis: String) -> Self { + self.genesis = genesis.trim_start_matches("0x").to_string(); + self + } + + pub fn with_bootnodes(mut self, bootnodes: Vec) -> Self { + self.bootnodes = bootnodes; + self + } + + pub fn with_timeout(mut self, timeout: Duration) -> Self { + self.timeout = timeout; + self + } + + pub fn build(&self) -> Result> { + let swarm = self.create_swarm()?; + Ok(Walker { + swarm, + genesis: self.genesis.clone(), + timeout: self.timeout, + queries: HashSet::with_capacity(1024), + discovered_with_address: HashMap::with_capacity(1024), + peer_details: HashMap::with_capacity(1024), + peer_role: HashMap::with_capacity(1024), + dialed_peers: HashMap::with_capacity(1024), + }) + } +}