diff --git a/Cargo.lock b/Cargo.lock index 51cd0b8..e78a417 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -204,12 +204,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "base32" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "022dfe9eb35f19ebbcb51e0b40a5ab759f46ad60cadf7297e0bd085afb50e076" - [[package]] name = "base64" version = "0.22.1" @@ -289,9 +283,9 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96eb4cdd6cf1b31d671e9efe75c5d1ec614776856cefbe109ca373554a6d514f" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" dependencies = [ "hybrid-array", ] @@ -351,6 +345,12 @@ dependencies = [ "shlex", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cfg-if" version = "1.0.4" @@ -363,6 +363,41 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures 0.2.17", +] + +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20 0.9.1", + "cipher", + "poly1305", + "zeroize", +] + [[package]] name = "chrono" version = "0.4.44" @@ -383,6 +418,7 @@ checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common 0.1.7", "inout", + "zeroize", ] [[package]] @@ -425,6 +461,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" +[[package]] +name = "cmov" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f88a43d011fc4a6876cb7344703e297c71dda42494fee094d5f7c76bf13f746" + [[package]] name = "cobs" version = "0.3.0" @@ -440,6 +482,16 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "compact_str" version = "0.9.0" @@ -454,6 +506,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "const-oid" version = "0.10.2" @@ -485,6 +543,16 @@ dependencies = [ "tracing", ] +[[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" version = "0.10.1" @@ -598,6 +666,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", + "rand_core 0.6.4", "typenum", ] @@ -630,17 +699,42 @@ dependencies = [ ] [[package]] -name = "curve25519-dalek" -version = "5.0.0-pre.1" +name = "ctutils" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f9200d1d13637f15a6acb71e758f64624048d85b31a5fdbfd8eca1e2687d0b7" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", "cpufeatures 0.2.17", "curve25519-dalek-derive", - "digest 0.11.0-rc.10", - "fiat-crypto", - "rand_core 0.9.5", + "digest 0.10.7", + "fiat-crypto 0.2.9", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "5.0.0-pre.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335f1947f241137a14106b6f5acc5918a5ede29c9d71d3f2cb1678d5075d9fc3" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "curve25519-dalek-derive", + "digest 0.11.2", + "fiat-crypto 0.3.0", + "rand_core 0.10.1", "rustc_version", "serde", "subtle", @@ -733,19 +827,49 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +[[package]] +name = "data-encoding-macro" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8142a83c17aa9461d637e649271eae18bf2edd00e91f2e105df36c3c16355bdb" +dependencies = [ + "data-encoding", + "data-encoding-macro-internal", +] + +[[package]] +name = "data-encoding-macro-internal" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab67060fc6b8ef687992d439ca0fa36e7ed17e9a0b16b25b601e8757df720de" +dependencies = [ + "data-encoding", + "syn 2.0.117", +] + [[package]] name = "deltae" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5729f5117e208430e437df2f4843f5e5952997175992d1414f94c57d61e270b4" +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid 0.9.6", + "zeroize", +] + [[package]] name = "der" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b" dependencies = [ - "const-oid", + "const-oid 0.10.2", "pem-rfc7468", "zeroize", ] @@ -827,16 +951,17 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", "crypto-common 0.1.7", + "subtle", ] [[package]] name = "digest" -version = "0.11.0-rc.10" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa94b64bfc6549e6e4b5a3216f22593224174083da7a90db47e951c4fb31725" +checksum = "4850db49bf08e663084f7fb5c87d202ef91a3907271aff24a94eb97ff039153c" dependencies = [ - "block-buffer 0.11.0", - "const-oid", + "block-buffer 0.12.0", + "const-oid 0.10.2", "crypto-common 0.2.1", ] @@ -886,9 +1011,9 @@ dependencies = [ [[package]] name = "dlopen2" -version = "0.5.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b4f5f101177ff01b8ec4ecc81eead416a8aa42819a2869311b3420fa114ffa" +checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4" dependencies = [ "libc", "once_cell", @@ -906,7 +1031,7 @@ dependencies = [ [[package]] name = "donglora-bridge" -version = "1.0.1" +version = "2.0.0" dependencies = [ "anyhow", "blake3", @@ -919,13 +1044,12 @@ dependencies = [ "hex", "iroh", "iroh-gossip", + "iroh-gossip-rendezvous", "jiff", - "mainline", "proptest", "rand 0.9.4", "ratatui", "serde", - "sha2 0.10.9", "tempfile", "tokio", "tokio-util", @@ -933,7 +1057,6 @@ dependencies = [ "tracing", "tracing-appender", "tracing-subscriber", - "zeroize", ] [[package]] @@ -967,29 +1090,53 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8 0.10.2", + "signature 2.2.0", +] + [[package]] name = "ed25519" version = "3.0.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6e914c7c52decb085cea910552e24c63ac019e3ab8bf001ff736da9a9d9d890" dependencies = [ - "pkcs8", + "pkcs8 0.11.0-rc.11", "serde", - "signature", + "signature 3.0.0-rc.10", ] [[package]] name = "ed25519-dalek" -version = "3.0.0-pre.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad207ed88a133091f83224265eac21109930db09bedcad05d5252f2af2de20a1" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" dependencies = [ - "curve25519-dalek", - "ed25519", - "rand_core 0.9.5", + "curve25519-dalek 4.1.3", + "ed25519 2.2.3", "serde", - "sha2 0.11.0-rc.2", - "signature", + "sha2 0.10.9", + "subtle", + "zeroize", +] + +[[package]] +name = "ed25519-dalek" +version = "3.0.0-pre.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053618a4c3d3bc24f188aa660ae75a46eeab74ef07fb415c61431e5e7cd4749b" +dependencies = [ + "curve25519-dalek 5.0.0-pre.6", + "ed25519 3.0.0-rc.4", + "rand_core 0.10.1", + "serde", + "sha2 0.11.0-rc.5", + "signature 3.0.0-rc.10", "subtle", "zeroize", ] @@ -1012,18 +1159,6 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" -[[package]] -name = "enum-as-inner" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "enum-assoc" version = "1.3.0" @@ -1070,24 +1205,18 @@ dependencies = [ "regex", ] -[[package]] -name = "fastbloom" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7f34442dbe69c60fe8eaf58a8cafff81a1f278816d8ab4db255b3bef4ac3c4" -dependencies = [ - "getrandom 0.3.4", - "libm", - "rand 0.9.4", - "siphasher", -] - [[package]] name = "fastrand" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "fiat-crypto" version = "0.3.0" @@ -1339,11 +1468,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", - "js-sys", "libc", "r-efi 5.3.0", "wasip2", - "wasm-bindgen", ] [[package]] @@ -1356,6 +1483,7 @@ dependencies = [ "js-sys", "libc", "r-efi 6.0.0", + "rand_core 0.10.1", "wasip2", "wasip3", "wasm-bindgen", @@ -1477,26 +1605,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] -name = "hickory-proto" -version = "0.25.2" +name = "hickory-net" +version = "0.26.0-beta.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502" +checksum = "1e232f503c4cfe3f4ea6594971255ecab9f6a0080c4c8e0e17630cc701322aa4" dependencies = [ "async-trait", "bytes", "cfg-if", "data-encoding", - "enum-as-inner", "futures-channel", "futures-io", "futures-util", "h2", + "hickory-proto", "http", "idna", "ipnet", - "once_cell", - "rand 0.9.4", - "ring", + "jni 0.22.4", + "rand 0.10.1", "rustls", "thiserror 2.0.18", "tinyvec", @@ -1507,28 +1634,71 @@ dependencies = [ ] [[package]] -name = "hickory-resolver" -version = "0.25.2" +name = "hickory-proto" +version = "0.26.0-beta.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc62a9a99b0bfb44d2ab95a7208ac952d31060efc16241c87eaf36406fecf87a" +checksum = "fcca12171ce774c549f35510be702f4da00ef12ca486f0f2acb2ee96f2f5ca0f" +dependencies = [ + "data-encoding", + "idna", + "ipnet", + "jni 0.22.4", + "once_cell", + "prefix-trie", + "rand 0.10.1", + "ring", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "url", +] + +[[package]] +name = "hickory-resolver" +version = "0.26.0-beta.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7d2c928fa078e6640f26cf1b537b212e1688829c3944780025c7084e8bbbf6" dependencies = [ "cfg-if", "futures-util", + "hickory-net", "hickory-proto", "ipconfig", + "ipnet", + "jni 0.22.4", "moka", + "ndk-context", "once_cell", "parking_lot", - "rand 0.9.4", + "rand 0.10.1", "resolv-conf", "rustls", "smallvec", + "system-configuration", "thiserror 2.0.18", "tokio", "tokio-rustls", "tracing", ] +[[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 0.10.7", +] + [[package]] name = "http" version = "1.4.0" @@ -1619,7 +1789,6 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots", ] [[package]] @@ -1792,11 +1961,10 @@ dependencies = [ [[package]] name = "igd-next" -version = "0.16.2" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516893339c97f6011282d5825ac94fc1c7aad5cad26bdc2d0cee068c0bf97f97" +checksum = "bac9a3c8278f43b4cd8463380f4a25653ac843e5b177e1d3eaf849cc9ba10d4d" dependencies = [ - "async-trait", "attohttpc", "bytes", "futures", @@ -1805,7 +1973,7 @@ dependencies = [ "hyper", "hyper-util", "log", - "rand 0.9.4", + "rand 0.10.1", "tokio", "url", "xmltree", @@ -1882,6 +2050,9 @@ name = "ipnet" version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" +dependencies = [ + "serde", +] [[package]] name = "iri-string" @@ -1895,22 +2066,25 @@ dependencies = [ [[package]] name = "iroh" -version = "0.97.0" +version = "0.98.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb56e7e4b0ec7fba7efa6a236b016a52b5d927d50244aceb9e20566159b1a32" +checksum = "9382a37668c84823e94b52eee462b3133ca7252a28de5f619a989d48b69cb30b" dependencies = [ "backon", + "blake3", "bytes", "cfg_aliases", + "ctutils", "data-encoding", "derive_more", - "ed25519-dalek", + "ed25519-dalek 3.0.0-pre.6", "futures-util", - "getrandom 0.3.4", + "getrandom 0.4.2", "hickory-resolver", "http", "ipnet", "iroh-base", + "iroh-dns", "iroh-metrics", "iroh-relay", "n0-error", @@ -1922,11 +2096,10 @@ dependencies = [ "noq-udp", "papaya", "pin-project", - "pkarr", - "pkcs8", + "pkcs8 0.11.0-rc.11", "portable-atomic", "portmapper", - "rand 0.9.4", + "rand 0.10.1", "reqwest", "rustc-hash", "rustls", @@ -1935,7 +2108,6 @@ dependencies = [ "serde", "smallvec", "strum 0.28.0", - "sync_wrapper", "time", "tokio", "tokio-stream", @@ -1948,35 +2120,52 @@ dependencies = [ [[package]] name = "iroh-base" -version = "0.97.0" +version = "0.98.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55a354e3396b62c14717ee807dfee9a7f43f6dad47e4ac0fd1d49f1ffad14ef0" +checksum = "738865784637830fb14204ebd3047922db83bc1816a59027af29579b9c27bd99" dependencies = [ - "curve25519-dalek", + "curve25519-dalek 5.0.0-pre.6", "data-encoding", + "data-encoding-macro", "derive_more", - "digest 0.11.0-rc.10", - "ed25519-dalek", + "digest 0.11.2", + "ed25519-dalek 3.0.0-pre.6", + "getrandom 0.4.2", "n0-error", - "rand_core 0.9.5", + "rand 0.10.1", "serde", - "sha2 0.11.0-rc.2", + "sha2 0.11.0-rc.5", "url", "zeroize", "zeroize_derive", ] [[package]] -name = "iroh-gossip" -version = "0.97.0" +name = "iroh-dns" +version = "0.98.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4db5b64f3cb0a0c8b68b57888acd4cefcd2f0774f1a132d2a498cbb2a92fbc55" +checksum = "ca474630d1e62ddef83149db6babe6a1055d901df9054349d31b22df99811b92" +dependencies = [ + "derive_more", + "iroh-base", + "n0-error", + "n0-future", + "simple-dns", + "strum 0.28.0", +] + +[[package]] +name = "iroh-gossip" +version = "0.98.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b349a9ab58e3b56cf41df693bc1812add192ad70ce7c8d0dbdc7d0319d71b11f" dependencies = [ "blake3", "bytes", + "constant_time_eq", "data-encoding", "derive_more", - "ed25519-dalek", + "ed25519-dalek 3.0.0-pre.6", "futures-concurrency", "futures-lite", "futures-util", @@ -1989,13 +2178,37 @@ dependencies = [ "n0-error", "n0-future", "postcard", - "rand 0.9.4", + "rand 0.10.1", "serde", "tokio", "tokio-util", "tracing", ] +[[package]] +name = "iroh-gossip-rendezvous" +version = "0.1.0" +dependencies = [ + "async-trait", + "blake3", + "bytes", + "chacha20poly1305", + "ed25519-dalek 2.2.0", + "futures-lite", + "hkdf", + "iroh", + "iroh-gossip", + "mainline", + "mutants", + "rand 0.9.4", + "sha2 0.10.9", + "thiserror 2.0.18", + "tokio", + "tokio-util", + "tracing", + "zeroize", +] + [[package]] name = "iroh-metrics" version = "0.38.3" @@ -2026,22 +2239,23 @@ dependencies = [ [[package]] name = "iroh-relay" -version = "0.97.0" +version = "0.98.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d786b260cadfe82ae0b6a9e372e8c78949096a06c857d1c3521355cefced0f55" +checksum = "4aa6e9a7277bfbb439739c52b57eb5f9288030983928412022b8e94a43d4d838" dependencies = [ "blake3", "bytes", "cfg_aliases", "data-encoding", "derive_more", - "getrandom 0.3.4", + "getrandom 0.4.2", "hickory-resolver", "http", "http-body-util", "hyper", "hyper-util", "iroh-base", + "iroh-dns", "iroh-metrics", "lru", "n0-error", @@ -2050,9 +2264,8 @@ dependencies = [ "noq-proto", "num_enum", "pin-project", - "pkarr", "postcard", - "rand 0.9.4", + "rand 0.10.1", "reqwest", "rustls", "rustls-pki-types", @@ -2068,14 +2281,13 @@ dependencies = [ "vergen-gitcl", "webpki-roots", "ws_stream_wasm", - "z32", ] [[package]] name = "irpc" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f47b7c52662d673df377b5ac40c121c7ff56eb764e520fae6543686132f7957" +checksum = "26bacc8d71f54f16cb5ae82745cfca440ad8ecd09b4480d415b8d9dc78146432" dependencies = [ "futures-util", "irpc-derive", @@ -2089,9 +2301,9 @@ dependencies = [ [[package]] name = "irpc-derive" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c1a4b460634aeed6dc01236a0047867de70e30562d91a0ad031dcb3ac33fb4" +checksum = "4651422b9d7af09fa1437a5fabbd9e074162b502a1af7f5bae8b439eaf3e049f" dependencies = [ "proc-macro2", "quote", @@ -2160,6 +2372,80 @@ dependencies = [ "jiff-tzdb", ] +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys 0.3.1", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys 0.4.1", + "log", + "simd_cesu8", + "thiserror 2.0.18", + "walkdir", + "windows-link", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.117", +] + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.117", +] + [[package]] name = "js-sys" version = "0.3.94" @@ -2207,12 +2493,6 @@ version = "0.2.184" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" -[[package]] -name = "libm" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" - [[package]] name = "libredox" version = "0.1.15" @@ -2326,7 +2606,7 @@ dependencies = [ "crc", "document-features", "dyn-clone", - "ed25519-dalek", + "ed25519-dalek 3.0.0-pre.6", "flume", "futures-lite", "getrandom 0.3.4", @@ -2417,6 +2697,12 @@ dependencies = [ "uuid", ] +[[package]] +name = "mutants" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add0ac067452ff1aca8c5002111bd6b1c895baee6e45fcbc44e0193aea17be56" + [[package]] name = "n0-error" version = "0.1.3" @@ -2472,10 +2758,16 @@ dependencies = [ ] [[package]] -name = "netdev" -version = "0.40.1" +name = "ndk-context" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b0a0096d9613ee878dba89bbe595f079d373e3f1960d882e4f2f78ff9c30a0a" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "netdev" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e30af1a5073b82356d9317c18226826370b4288eba2f71c7e84e18bae51b3847" dependencies = [ "block2", "dispatch2", @@ -2484,13 +2776,13 @@ dependencies = [ "libc", "mac-addr", "netlink-packet-core", - "netlink-packet-route", + "netlink-packet-route 0.29.0", "netlink-sys", "objc2-core-foundation", "objc2-system-configuration", "once_cell", "plist", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2514,6 +2806,18 @@ dependencies = [ "netlink-packet-core", ] +[[package]] +name = "netlink-packet-route" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be8919612f6028ab4eacbbfe1234a9a43e3722c6e0915e7ff519066991905092" +dependencies = [ + "bitflags 2.11.0", + "libc", + "log", + "netlink-packet-core", +] + [[package]] name = "netlink-proto" version = "0.12.0" @@ -2543,9 +2847,9 @@ dependencies = [ [[package]] name = "netwatch" -version = "0.15.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b1b27babe89ef9f2237bc6c028bea24fa84163a1b6f8f17ff93573ebd7d861f" +checksum = "6fc0d4b4134425d9834e591b1a6f807ea365c6d941d738942215564af5f28a97" dependencies = [ "atomic-waker", "bytes", @@ -2558,7 +2862,7 @@ dependencies = [ "n0-watcher", "netdev", "netlink-packet-core", - "netlink-packet-route", + "netlink-packet-route 0.30.0", "netlink-proto", "netlink-sys", "noq-udp", @@ -2613,12 +2917,13 @@ dependencies = [ [[package]] name = "noq" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df966fb44ac763bc86da97fa6c811c54ae82ef656575949f93c6dae0c9f09bf" +checksum = "4b969bd157c3bd3bab239a1a8b14f67f2033fa012770367fcbd5b42d71ae3548" dependencies = [ "bytes", "cfg_aliases", + "derive_more", "noq-proto", "noq-udp", "pin-project-lite", @@ -2634,19 +2939,18 @@ dependencies = [ [[package]] name = "noq-proto" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c61b72abd670eebc05b5cf720e077b04a3ef3354bc7bc19f1c3524cb424db7b" +checksum = "cdec6f5039d98ee5377b2f532d495a555eb664c53161b1b5780dcaeac678b60e" dependencies = [ "aes-gcm", "bytes", "derive_more", "enum-assoc", - "fastbloom", - "getrandom 0.3.4", + "getrandom 0.4.2", "identity-hash", "lru-slab", - "rand 0.9.4", + "rand 0.10.1", "ring", "rustc-hash", "rustls", @@ -2661,9 +2965,9 @@ dependencies = [ [[package]] name = "noq-udp" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb9be4fedd6b98f3ba82ccd3506f4d0219fb723c3f97c67e12fe1494aa020e44" +checksum = "ee91b05f4f3353290936ba1f3233518868fb4e2da99cb4c90d1f8cebb064e527" dependencies = [ "cfg_aliases", "libc", @@ -2672,21 +2976,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "ntimestamp" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50f94c405726d3e0095e89e72f75ce7f6587b94a8bd8dc8054b73f65c0fd68c" -dependencies = [ - "base32", - "document-features", - "getrandom 0.2.17", - "httpdate", - "js-sys", - "once_cell", - "serde", -] - [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -2828,6 +3117,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + [[package]] name = "option-ext" version = "0.2.0" @@ -3035,22 +3330,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] -name = "pkarr" -version = "5.0.4" +name = "pkcs8" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7bfb9143bbba379f246211eb68074d78db9cc048e4c5701f3b0e6cb1ec67ca2" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "base32", - "bytes", - "cfg_aliases", - "document-features", - "ed25519-dalek", - "getrandom 0.4.2", - "ntimestamp", - "self_cell", - "serde", - "simple-dns", - "thiserror 2.0.18", + "der 0.7.10", + "spki 0.7.3", ] [[package]] @@ -3059,8 +3345,8 @@ version = "0.11.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12922b6296c06eb741b02d7b5161e3aaa22864af38dfa025a1a3ba3f68c84577" dependencies = [ - "der", - "spki", + "der 0.8.0", + "spki 0.8.0", ] [[package]] @@ -3076,6 +3362,17 @@ dependencies = [ "time", ] +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures 0.2.17", + "opaque-debug", + "universal-hash", +] + [[package]] name = "polyval" version = "0.6.2" @@ -3108,9 +3405,9 @@ dependencies = [ [[package]] name = "portmapper" -version = "0.15.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74748bc706fa6b6aebac6bbe0bbe0de806b384cb5c557ea974f771360a4e3858" +checksum = "a145e62ddd9aecc9c7b1a3c84cea2a803386c7f4da7795bf9f0d50d90dc52549" dependencies = [ "base64", "bytes", @@ -3124,7 +3421,7 @@ dependencies = [ "n0-error", "netwatch", "num_enum", - "rand 0.9.4", + "rand 0.10.1", "serde", "smallvec", "socket2", @@ -3185,6 +3482,17 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prefix-trie" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23370be78b7e5bcbb0cab4a02047eb040279a693c78daad04c2c5f1c24a83503" +dependencies = [ + "either", + "ipnet", + "num-traits", +] + [[package]] name = "prettyplease" version = "0.2.37" @@ -3247,61 +3555,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "quinn" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror 2.0.18", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" -dependencies = [ - "bytes", - "getrandom 0.3.4", - "lru-slab", - "rand 0.9.4", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.18", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.60.2", -] - [[package]] name = "quote" version = "1.0.45" @@ -3342,6 +3595,17 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "rand" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +dependencies = [ + "chacha20 0.10.0", + "getrandom 0.4.2", + "rand_core 0.10.1", +] + [[package]] name = "rand_chacha" version = "0.9.0" @@ -3357,6 +3621,9 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] [[package]] name = "rand_core" @@ -3367,6 +3634,12 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + [[package]] name = "rand_xorshift" version = "0.4.0" @@ -3512,9 +3785,9 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "reqwest" -version = "0.12.28" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" dependencies = [ "base64", "bytes", @@ -3530,12 +3803,9 @@ dependencies = [ "log", "percent-encoding", "pin-project-lite", - "quinn", "rustls", "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", + "rustls-platform-verifier", "sync_wrapper", "tokio", "tokio-rustls", @@ -3548,7 +3818,6 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots", ] [[package]] @@ -3614,6 +3883,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pki-types" version = "1.14.0" @@ -3624,6 +3905,33 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni 0.21.1", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + [[package]] name = "rustls-webpki" version = "0.103.13" @@ -3659,6 +3967,24 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -3671,6 +3997,29 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.11.0", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "seize" version = "0.5.1" @@ -3681,12 +4030,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "self_cell" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" - [[package]] name = "semver" version = "1.0.28" @@ -3771,18 +4114,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - [[package]] name = "serialport" version = "4.9.0" @@ -3791,7 +4122,7 @@ checksum = "a4d91116f97173694f1642263b2ff837f80d933aa837e2314969f6728f661df3" dependencies = [ "bitflags 2.11.0", "cfg-if", - "core-foundation", + "core-foundation 0.10.1", "core-foundation-sys", "io-kit-sys", "mach2", @@ -3820,13 +4151,13 @@ dependencies = [ [[package]] name = "sha2" -version = "0.11.0-rc.2" +version = "0.11.0-rc.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1e3878ab0f98e35b2df35fe53201d088299b41a6bb63e3e34dada2ac4abd924" +checksum = "7c5f3b1e2dc8aad28310d8410bd4d7e180eca65fca176c52ab00d364475d0024" dependencies = [ "cfg-if", "cpufeatures 0.2.17", - "digest 0.11.0-rc.10", + "digest 0.11.2", ] [[package]] @@ -3875,12 +4206,31 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "signature" version = "3.0.0-rc.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f1880df446116126965eeec169136b2e0251dba37c6223bcc819569550edea3" +[[package]] +name = "simd_cesu8" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] + [[package]] name = "simdutf8" version = "0.1.5" @@ -3889,9 +4239,9 @@ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" [[package]] name = "simple-dns" -version = "0.11.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df350943049174c4ae8ced56c604e28270258faec12a6a48637a7655287c9ce0" +checksum = "dee851d0e5e7af3721faea1843e8015e820a234f81fda3dea9247e15bac9a86a" dependencies = [ "bitflags 2.11.0", ] @@ -3956,6 +4306,16 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der 0.7.10", +] + [[package]] name = "spki" version = "0.8.0" @@ -3963,7 +4323,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d9efca8738c78ee9484207732f728b1ef517bbb1833d6fc0879ca898a522f6f" dependencies = [ "base64ct", - "der", + "der 0.8.0", ] [[package]] @@ -4074,6 +4434,27 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags 2.11.0", + "core-foundation 0.9.4", + "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 = "tagptr" version = "0.2.0" @@ -4344,20 +4725,21 @@ dependencies = [ [[package]] name = "tokio-websockets" -version = "0.12.3" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1b6348ebfaaecd771cecb69e832961d277f59845d4220a584701f72728152b7" +checksum = "dad543404f98bfc969aeb71994105c592acfc6c43323fddcd016bb208d1c65cb" dependencies = [ "base64", "bytes", "futures-core", "futures-sink", - "getrandom 0.3.4", + "getrandom 0.4.2", "http", "httparse", - "rand 0.9.4", + "rand 0.10.1", "ring", "rustls-pki-types", + "sha1_smol", "simdutf8", "tokio", "tokio-rustls", @@ -4739,6 +5121,16 @@ dependencies = [ "libc", ] +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -4851,9 +5243,9 @@ dependencies = [ [[package]] name = "wasm-streams" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" dependencies = [ "futures-util", "js-sys", @@ -4894,6 +5286,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-root-certs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "webpki-roots" version = "1.0.6" @@ -4997,6 +5398,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -5115,6 +5525,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -5124,24 +5543,6 @@ 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-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - [[package]] name = "windows-sys" version = "0.61.2" @@ -5151,6 +5552,21 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -5160,30 +5576,13 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 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-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - [[package]] name = "windows-threading" version = "0.2.1" @@ -5193,6 +5592,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -5200,10 +5605,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" +name = "windows_aarch64_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" @@ -5212,10 +5617,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" +name = "windows_i686_gnu" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" @@ -5223,12 +5628,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" @@ -5236,10 +5635,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" +name = "windows_i686_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" @@ -5248,10 +5647,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] -name = "windows_i686_msvc" -version = "0.53.1" +name = "windows_x86_64_gnu" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" @@ -5260,10 +5659,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" +name = "windows_x86_64_gnullvm" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" @@ -5272,10 +5671,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" +name = "windows_x86_64_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" @@ -5283,12 +5682,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - [[package]] name = "winnow" version = "1.0.1" @@ -5464,12 +5857,6 @@ dependencies = [ "synstructure", ] -[[package]] -name = "z32" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2164e798d9e3d84ee2c91139ace54638059a3b23e361f5c11781c2c6459bde0f" - [[package]] name = "zerocopy" version = "0.8.48" diff --git a/Cargo.toml b/Cargo.toml index 2f3b674..48d5226 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,10 +14,10 @@ rust-version = "1.94.1" exclude = [".jj/", ".github/"] [dependencies] -donglora-client = "1.0.0" -iroh = "0.97" -iroh-gossip = "0.97" -mainline = { version = "6", features = ["async"] } +donglora-client = "1" +iroh = "0.98" +iroh-gossip = "0.98" +iroh-gossip-rendezvous = { version = "0.1", path = "../../iroh-gossip-rendezvous" } tokio = { version = "1", features = ["full"] } clap = { version = "4", features = ["derive"] } serde = { version = "1", features = ["derive"] } @@ -34,10 +34,8 @@ bytes = "1" futures-lite = "2" rand = "0.9" hex = "0.4.3" -sha2 = "0.10" tokio-util = { version = "0.7.18", features = ["rt"] } jiff = "0.2.23" -zeroize = "1" [dev-dependencies] proptest = "1" diff --git a/dht-partition-healing.md b/dht-partition-healing.md new file mode 100644 index 0000000..6052c6c --- /dev/null +++ b/dht-partition-healing.md @@ -0,0 +1,11 @@ +# Moved + +The DHT partition-healing algorithm specification is now maintained as part +of the `iroh-gossip-rendezvous` crate: + +**** + +The bridge pins a specific `iroh-gossip-rendezvous` version; the spec that +matches whatever version you're running is in +`~/.cargo/registry/.../iroh-gossip-rendezvous-*/PROTOCOL.md` or, for path +dependencies, at `../iroh-gossip-rendezvous/PROTOCOL.md`. diff --git a/src/gossip.rs b/src/gossip.rs index b65fcfe..5a77d53 100644 --- a/src/gossip.rs +++ b/src/gossip.rs @@ -1,140 +1,28 @@ -//! iroh-gossip swarm management and mainline DHT peer discovery. +//! Bridge-side glue over the `iroh-gossip-rendezvous` crate. //! -//! Derives cryptographic keys from a shared passphrase, manages the gossip -//! subscription, and maintains a DHT heartbeat for zero-configuration peer -//! bootstrapping. +//! Starts the rendezvous, spawns a task that decodes incoming gossip events +//! into [`GossipEvent`]s the router understands, and a broadcaster task that +//! encodes outgoing [`GossipFrame`]s onto the iroh-gossip sender. use std::collections::HashSet; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::{Arc, Mutex}; -use std::time::{Duration, Instant}; +use std::sync::Arc; use anyhow::{Context, Result}; use bytes::Bytes; -use futures_lite::StreamExt; -use iroh::endpoint::Incoming; -use iroh::{Endpoint, PublicKey, SecretKey}; -use iroh_gossip::TopicId; +use iroh::{PublicKey, SecretKey}; use iroh_gossip::api::Event; -use iroh_gossip::net::{GOSSIP_ALPN, Gossip as GossipActor}; -use mainline::async_dht::AsyncDht; -use mainline::{Dht, MutableItem, SigningKey}; -use sha2::{Digest, Sha256}; +use iroh_gossip_rendezvous::Rendezvous; +pub use iroh_gossip_rendezvous::{DhtStatus, RendezvousState}; use tokio::sync::mpsc; use tokio_util::sync::CancellationToken; -use tracing::{debug, error, info, warn}; +use tracing::{debug, error, warn}; use crate::packet::GossipFrame; -/// Maximum number of peers to store in a DHT record. -/// 28 × 32 bytes = 896 bytes raw. Bencoded: 28 × ~36 = ~1008. -/// Conservative cap to stay under BEP44's 1000-byte value limit. -const MAX_DHT_PEERS: usize = 27; - -/// DHT heartbeat interval (base, before jitter). Each bridge periodically -/// publishes `[self] + live_neighbors` as a blind overwrite. Any live bridge -/// publishing at this cadence guarantees that the DHT record is overwritten -/// with a fresh snapshot of a real, connected sub-swarm within this window — -/// so stale entries from crashed / restarted peers age out within one cycle. -const DHT_HEARTBEAT_SECS: u64 = 60; - -/// Maximum random jitter added to heartbeat. Prevents synchronized publish -/// storms when multiple bridges start simultaneously. -const DHT_HEARTBEAT_JITTER_SECS: u64 = 15; - -/// DHT read interval while we have no neighbors. Stays aggressive so two -/// freshly-started bridges find each other quickly. -const DHT_LONELY_READ_SECS: u64 = 3; - -/// How long to try dialing DHT-discovered peers before we give up and publish -/// ourselves alone. If we connect to at least one peer in this window we -/// publish `[self, neighbors...]` right away. If we don't, we publish `[self]` -/// so newcomers find us — but we delay this so we don't overwrite a -/// potentially useful DHT record with just our own lonely entry. -const DHT_BOOTSTRAP_GRACE_SECS: u64 = 10; - -// ── Key derivation ─────────────────────────────────────────────── - -/// Cryptographic keys derived from a shared passphrase. -pub struct PassphraseKeys { - pub topic_id: TopicId, - pub dht_signing_key: SigningKey, - pub dht_salt: [u8; 20], -} - -impl PassphraseKeys { - /// Derive all keys from a passphrase. - /// - /// Intermediate buffers containing the passphrase are zeroized after use to - /// minimize time sensitive material spends in memory. - #[must_use] - pub fn derive(passphrase: &str) -> Self { - // Application-specific prefix to avoid DHT collisions with other apps. - const APP_PREFIX: &[u8] = b"donglora-bridge:"; - - // Helper: build a zeroized buffer of [prefix | suffix | passphrase]. - let keyed_buf = |suffix: &[u8]| -> Vec { - let mut buf = Vec::with_capacity(APP_PREFIX.len() + suffix.len() + passphrase.len()); - buf.extend_from_slice(APP_PREFIX); - buf.extend_from_slice(suffix); - buf.extend_from_slice(passphrase.as_bytes()); - buf - }; - - // Topic ID: blake3 hash of prefixed passphrase. - let mut buf = keyed_buf(b""); - let topic_bytes = *blake3::hash(&buf).as_bytes(); - let topic_id = TopicId::from_bytes(topic_bytes); - zeroize::Zeroize::zeroize(&mut buf); - - // DHT signing key: ed25519 from SHA-256 of prefixed passphrase + "sign:". - let mut buf = keyed_buf(b"sign:"); - let sign_seed = Sha256::digest(&buf); - let seed_bytes: [u8; 32] = sign_seed.into(); - let dht_signing_key = SigningKey::from_bytes(&seed_bytes); - zeroize::Zeroize::zeroize(&mut buf); - - // DHT salt: first 20 bytes of SHA-256 of prefixed passphrase + "salt:". - let mut buf = keyed_buf(b"salt:"); - let salt_hash = Sha256::digest(&buf); - let mut dht_salt = [0u8; 20]; - dht_salt.copy_from_slice(&salt_hash[..20]); - zeroize::Zeroize::zeroize(&mut buf); - - Self { topic_id, dht_signing_key, dht_salt } - } -} - -// ── Swarm state (for TUI) ─────────────────────────────────────── - -/// Observable state of the gossip swarm. -#[derive(Debug, Clone)] -pub struct SwarmState { - pub topic_hash: String, - pub neighbor_count: usize, - pub dht_status: DhtStatus, - pub last_dht_publish: Option, -} - -/// Status of the mainline DHT connection. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum DhtStatus { - Bootstrapping, - Ready, - PublishFailed, -} - -impl std::fmt::Display for DhtStatus { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Bootstrapping => write!(f, "Bootstrapping"), - Self::Ready => write!(f, "Ready"), - Self::PublishFailed => write!(f, "Failed"), - } - } -} - -// ── Events to router ──────────────────────────────────────────── +/// App salt for the donglora-bridge rendezvous surface. Versioned so a +/// future breaking change gets a fresh DHT meeting place — v1 and v2 +/// bridges do not see each other. +pub const APP_SALT: &str = "donglora-bridge/v2"; /// Events from the gossip swarm to the router. pub enum GossipEvent { @@ -144,212 +32,89 @@ pub enum GossipEvent { NeighborChanged(usize), } -// ── Gossip handle ─────────────────────────────────────────────── - -/// Handle to the gossip swarm. Manages iroh endpoint, gossip subscription, and DHT. +/// Handle held by the rest of the bridge. Owns the `Rendezvous` that in turn +/// owns all DHT + gossip background work. Dropping this stops everything. pub struct Gossip { - endpoint: Endpoint, - actor: GossipActor, - // std::sync::Mutex (not tokio::sync::Mutex) because locks are never held across - // .await points. This avoids async-aware mutex overhead for brief critical sections. - state: Arc>, - neighbors: Arc>>, - dht_signing_key: SigningKey, - dht_salt: [u8; 20], + rendezvous: Rendezvous, cancel: CancellationToken, } impl Gossip { - /// Create and start the gossip network. - /// - /// Spawns background tasks for connection acceptance, gossip event forwarding, - /// gossip broadcasting, and DHT heartbeat. + /// Spin up the gossip swarm + DHT rendezvous. /// /// Returns `(handle, event_rx, frame_tx)`: /// - `event_rx`: channel of events from the swarm to the router /// - `frame_tx`: channel for the router to send frames to broadcast pub async fn new( secret_key: SecretKey, - keys: &PassphraseKeys, + passphrase: &str, ) -> Result<(Self, mpsc::Receiver, mpsc::Sender)> { let cancel = CancellationToken::new(); - // Build iroh endpoint with n0 discovery. - let endpoint = Endpoint::builder(iroh::endpoint::presets::N0) + let rendezvous = Rendezvous::builder() + .passphrase(passphrase) + .app_salt(APP_SALT) .secret_key(secret_key) - .alpns(vec![GOSSIP_ALPN.to_vec()]) - .bind() + .build() .await - .context("binding iroh endpoint")?; + .context("starting iroh-gossip rendezvous")?; - let my_id = endpoint.id(); - info!("iroh endpoint bound, id: {}", my_id.fmt_short()); + let my_id = rendezvous.node_id(); + tracing::info!("iroh endpoint bound, id: {}", my_id.fmt_short()); + tracing::info!("topic: {}", hex::encode(&rendezvous.topic_id().as_bytes()[..8])); - // Build gossip actor. - let actor = GossipActor::builder().spawn(endpoint.clone()); - - // Bootstrap from DHT. - let bootstrap_peers = dht_get_peers(keys).await; - if bootstrap_peers.is_empty() { - info!("no peers found in DHT — we are the first node"); - } else { - info!("found {} peer(s) in DHT", bootstrap_peers.len()); - } - - // Subscribe to gossip topic. - let topic = actor.subscribe(keys.topic_id, bootstrap_peers).await.context("subscribing to gossip topic")?; - - let (sender, receiver) = topic.split(); - - // Initial swarm state. - let topic_hash = hex::encode(&keys.topic_id.as_bytes()[..8]); - let state = Arc::new(Mutex::new(SwarmState { - topic_hash, - neighbor_count: 0, - dht_status: DhtStatus::Bootstrapping, - last_dht_publish: None, - })); - let neighbor_count = Arc::new(AtomicUsize::new(0)); - let neighbors: Arc>> = Arc::new(Mutex::new(HashSet::new())); - - // Channels. let (event_tx, event_rx) = mpsc::channel::(256); let (frame_tx, frame_rx) = mpsc::channel::(256); - let handle = Self { - endpoint: endpoint.clone(), - actor: actor.clone(), - state: state.clone(), - neighbors: neighbors.clone(), - dht_signing_key: keys.dht_signing_key.clone(), - dht_salt: keys.dht_salt, - cancel: cancel.clone(), - }; + // Receiver task: drain the rendezvous event stream, translate into + // router-side `GossipEvent`s. + spawn_receiver(rendezvous.subscribe(), event_tx, cancel.clone()); - // Spawn accept loop. - spawn_accept_loop(endpoint.clone(), actor.clone(), cancel.clone()); + // Broadcaster task: pull encoded frames from the router, hand them + // to the iroh-gossip sender. + spawn_broadcaster(rendezvous.sender().clone(), frame_rx, cancel.clone()); - // Spawn gossip receiver task. - spawn_receiver(receiver, event_tx, state.clone(), neighbor_count.clone(), neighbors.clone(), cancel.clone()); - - // Clone sender for DHT heartbeat to feed discovered peers via join_peers. - let dht_sender = sender.clone(); - - // Spawn gossip broadcaster task. - spawn_broadcaster(sender, frame_rx, cancel.clone()); - - // Spawn DHT heartbeat. - spawn_dht_heartbeat( - my_id, - keys.dht_signing_key.clone(), - keys.dht_salt, - dht_sender, - neighbors, - state, - neighbor_count, - cancel, - ); - - // Try to join (non-blocking — fire and forget with timeout). - // The receiver task handles events regardless. - - Ok((handle, event_rx, frame_tx)) + Ok((Self { rendezvous, cancel }, event_rx, frame_tx)) } - /// Snapshot of swarm state for TUI. + /// Snapshot of rendezvous state for the TUI. #[must_use] - pub fn swarm_state(&self) -> SwarmState { - self.state.lock().unwrap_or_else(std::sync::PoisonError::into_inner).clone() + pub fn state(&self) -> RendezvousState { + self.rendezvous.state() } - /// Graceful shutdown. Publishes neighbors (without ourselves) to DHT - /// so other nodes can still bootstrap from live peers. + /// Graceful shutdown. Cancels our own glue tasks, then the rendezvous. + /// Idempotent. pub async fn shutdown(&self) { - // Publish neighbors-only to DHT before shutting down. - let neighbor_ids: Vec<[u8; 32]> = self - .neighbors - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .iter() - .map(|id| *id.as_bytes()) - .collect(); - - if let Ok(dht) = Dht::client() { - let dht = dht.as_async(); - let value = encode_peer_list(&neighbor_ids); - let seq = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_secs().cast_signed()) - .unwrap_or(0); - let item = MutableItem::new(self.dht_signing_key.clone(), &value, seq, Some(self.dht_salt.as_ref())); - match dht.put_mutable(item, None).await { - Ok(_) => info!("shutdown: published {} neighbor(s) to DHT (removed self)", neighbor_ids.len()), - Err(e) => debug!("shutdown: DHT publish failed: {e:#}"), - } - } - self.cancel.cancel(); - let _ = self.actor.shutdown().await; - self.endpoint.close().await; + self.rendezvous.shutdown().await; } } -// ── Background tasks ──────────────────────────────────────────── +// ── Internal tasks ────────────────────────────────────────────────────── -/// Accept incoming iroh connections and forward gossip ALPN to the gossip actor. -fn spawn_accept_loop(endpoint: Endpoint, gossip: GossipActor, cancel: CancellationToken) { - tokio::spawn(async move { - loop { - let incoming = tokio::select! { - incoming = endpoint.accept() => incoming, - () = cancel.cancelled() => break, - }; - let Some(incoming) = incoming else { break }; - let gossip = gossip.clone(); - tokio::spawn(handle_incoming(incoming, gossip)); - } - debug!("accept loop stopped"); - }); -} - -async fn handle_incoming(incoming: Incoming, gossip: GossipActor) { - let Ok(conn) = incoming.await else { return }; - let alpn = conn.alpn(); - if *alpn == *GOSSIP_ALPN { - if let Err(e) = gossip.handle_connection(conn).await { - debug!("gossip handle_connection error: {e:#}"); - } - } else { - debug!("unexpected ALPN: {:?}", String::from_utf8_lossy(alpn)); - } -} - -/// Forward gossip events to the router via channel. fn spawn_receiver( - mut receiver: iroh_gossip::api::GossipReceiver, + mut rx: tokio::sync::broadcast::Receiver, event_tx: mpsc::Sender, - state: Arc>, - neighbor_count: Arc, - neighbors: Arc>>, cancel: CancellationToken, ) { tokio::spawn(async move { - // Log a hard warning if the receiver stream ever ends — this is the - // "silent one-way gossip" failure mode. Track why it exited so the - // log tells us whether it was a stream-end (None), an error - // (Some(Err)), or a cancel. + // Track our own neighbor set so we can send accurate counts to the + // router (the rendezvous crate tracks the same set internally for + // its `state()`; we duplicate here to avoid a cross-crate handle). + let mut neighbors: HashSet = HashSet::new(); let exit_reason: &'static str = loop { let event = tokio::select! { - event = receiver.next() => event, + ev = rx.recv() => ev, () = cancel.cancelled() => break "cancelled", }; let event = match event { - None => break "stream ended (receiver closed)", - Some(Err(e)) => { - error!("gossip receiver error: {e:#}"); - break "receiver error"; + Ok(ev) => ev, + Err(tokio::sync::broadcast::error::RecvError::Closed) => break "broadcast closed", + Err(tokio::sync::broadcast::error::RecvError::Lagged(n)) => { + warn!("gossip receiver lagged — dropped {n} events"); + continue; } - Some(Ok(ev)) => ev, }; match event { Event::Received(msg) => { @@ -359,48 +124,31 @@ fn spawn_receiver( debug!("received malformed gossip frame ({} bytes)", msg.content.len()); } } - // Neighbor count is tracked in both AtomicUsize (for lock-free reads in DHT - // heartbeat) and SwarmState (for TUI snapshots). Brief inconsistency between - // the two is acceptable — the TUI polls at 250ms intervals. Event::NeighborUp(id) => { - let count = neighbor_count.fetch_add(1, Ordering::Relaxed) + 1; - info!("neighbor up: {} (total: {count})", id.fmt_short()); - if let Ok(mut n) = neighbors.lock() { - n.insert(id); - } - if let Ok(mut s) = state.lock() { - s.neighbor_count = count; - } - let _ = event_tx.send(GossipEvent::NeighborChanged(count)).await; + neighbors.insert(id); + tracing::info!("neighbor up: {} (total: {})", id.fmt_short(), neighbors.len()); + let _ = event_tx.send(GossipEvent::NeighborChanged(neighbors.len())).await; } Event::NeighborDown(id) => { - let count = neighbor_count.fetch_sub(1, Ordering::Relaxed).saturating_sub(1); - info!("neighbor down: {} (total: {count})", id.fmt_short()); - if let Ok(mut n) = neighbors.lock() { - n.remove(&id); - } - if let Ok(mut s) = state.lock() { - s.neighbor_count = count; - } - let _ = event_tx.send(GossipEvent::NeighborChanged(count)).await; + neighbors.remove(&id); + tracing::info!("neighbor down: {} (total: {})", id.fmt_short(), neighbors.len()); + let _ = event_tx.send(GossipEvent::NeighborChanged(neighbors.len())).await; } Event::Lagged => { warn!("gossip receiver lagged — some messages were dropped"); } } }; - // A shutdown cancel is expected; any other exit means we silently - // stopped receiving gossip. That's the "one-way bridge" bug — - // broadcast still works because spawn_broadcaster is independent. if exit_reason == "cancelled" { debug!("gossip receiver stopped ({exit_reason})"); } else { - error!("gossip receiver STOPPED ({exit_reason}) — this bridge will no longer receive from the swarm",); + error!( + "gossip receiver STOPPED ({exit_reason}) — this bridge will no longer receive from the swarm", + ); } }); } -/// Read frames from the router and broadcast them to the swarm. fn spawn_broadcaster( sender: iroh_gossip::api::GossipSender, mut frame_rx: mpsc::Receiver, @@ -422,371 +170,11 @@ fn spawn_broadcaster( }); } -/// Periodically publish `[self] + live_neighbors` to the DHT and read it to -/// discover new peers. -/// -/// Strategy — deliberately simple: -/// 1. **Startup**: read DHT, try to join discovered peers. Do NOT publish -/// yet — we don't know if any of them are still alive, and publishing -/// `[self]` alone would overwrite a potentially useful record. -/// 2. **Bootstrap grace** (10 s): keep reading fast (every -/// `DHT_LONELY_READ_SECS`) looking for peers to connect to. If a -/// `NeighborUp` fires inside this window, publish immediately with the -/// live neighbor set. If nothing shows up, publish `[self]` so anyone -/// else joining finds us. -/// 3. **Steady state**: publish `[self, neighbors...]` every -/// `DHT_HEARTBEAT_SECS` (+ jitter), a blind overwrite. Any live bridge -/// doing this naturally ages out crashed/stale peers from the DHT -/// record within one cycle. -/// 4. **Re-read cadence**: fast (`DHT_LONELY_READ_SECS`) while -/// `neighbor_count == 0`, relaxed (= heartbeat interval) once -/// connected. -#[allow(clippy::too_many_arguments)] -fn spawn_dht_heartbeat( - my_id: PublicKey, - signing_key: SigningKey, - salt: [u8; 20], - gossip_sender: iroh_gossip::api::GossipSender, - neighbors: Arc>>, - state: Arc>, - neighbor_count: Arc, - cancel: CancellationToken, -) { - tokio::spawn(async move { - let dht = match Dht::client() { - Ok(d) => d.as_async(), - Err(e) => { - warn!("failed to create DHT client: {e:#}"); - return; - } - }; - - let pub_key_bytes = signing_key.verifying_key().to_bytes(); - let mut last_publish: Option = None; - let mut was_alone = true; - let started = Instant::now(); - - // Step 1: read the DHT to find peers to dial. - dht_read_and_join(&dht, &my_id, &pub_key_bytes, &salt, &gossip_sender, &state).await; - - loop { - let alone = neighbor_count.load(Ordering::Relaxed) == 0; - - // Transition: alone → connected. Publish immediately with live - // neighbors so anyone who was about to dial us-as-lonely finds - // the full picture. - if was_alone && !alone { - info!("first neighbor joined — publishing to DHT"); - dht_publish(&dht, &my_id, &signing_key, &salt, &neighbors, &state).await; - last_publish = Some(Instant::now()); - } - was_alone = alone; - - // Sleep: fast reads while lonely, relaxed once connected. - let sleep_dur = if alone { - Duration::from_secs(DHT_LONELY_READ_SECS) - } else { - let jitter = rand::random::() % DHT_HEARTBEAT_JITTER_SECS.max(1); - Duration::from_secs(DHT_HEARTBEAT_SECS + jitter) - }; - - tokio::select! { - () = tokio::time::sleep(sleep_dur) => {} - () = cancel.cancelled() => break, - } - - // Always read — cheap, keeps fresh peers coming in. - dht_read_and_join(&dht, &my_id, &pub_key_bytes, &salt, &gossip_sender, &state).await; - - // Steady-state: publish on every heartbeat tick once we've - // published at least once. Pre-first-publish, hold off until - // either a neighbor appears OR the bootstrap grace elapses — - // this avoids wiping a potentially-useful DHT record with - // just our lonely [self] entry on startup. - let should_publish = last_publish.map_or_else( - || !alone || started.elapsed() >= Duration::from_secs(DHT_BOOTSTRAP_GRACE_SECS), - |t| t.elapsed() >= Duration::from_secs(DHT_HEARTBEAT_SECS), - ); - if should_publish { - dht_publish(&dht, &my_id, &signing_key, &salt, &neighbors, &state).await; - last_publish = Some(Instant::now()); - } - } - debug!("DHT heartbeat stopped"); - }); -} - -/// Read the DHT and feed any discovered peers into the gossip swarm. -async fn dht_read_and_join( - dht: &AsyncDht, - my_id: &PublicKey, - pub_key_bytes: &[u8; 32], - salt: &[u8; 20], - gossip_sender: &iroh_gossip::api::GossipSender, - state: &Arc>, -) { - if let Some(record) = dht.get_mutable_most_recent(pub_key_bytes, Some(salt)).await { - let peers = decode_peer_list(record.value()); - let new_peers: Vec<_> = peers.into_iter().filter(|id| *id != *my_id).collect(); - if !new_peers.is_empty() { - info!("DHT discovered {} peer(s), feeding to gossip", new_peers.len()); - if let Err(e) = gossip_sender.join_peers(new_peers).await { - debug!("join_peers error: {e:#}"); - } - } - if let Ok(mut s) = state.lock() { - s.dht_status = DhtStatus::Ready; - } - } -} - -/// Publish `[self] + live_neighbors` to the DHT as a blind overwrite. -/// -/// No merge with existing DHT content. Stale peers age out naturally -/// because every live bridge publishes a snapshot of its actual live -/// connections within `DHT_HEARTBEAT_SECS`, and higher-seq records win. -async fn dht_publish( - dht: &AsyncDht, - my_id: &PublicKey, - signing_key: &SigningKey, - salt: &[u8; 20], - neighbors: &Arc>>, - state: &Arc>, -) { - let mut peer_list: Vec<[u8; 32]> = Vec::with_capacity(MAX_DHT_PEERS); - peer_list.push(*my_id.as_bytes()); - - // Snapshot current live neighbors — those we have an active gossip - // connection to right now. No inherited junk from prior DHT content. - if let Ok(n) = neighbors.lock() { - for id in n.iter() { - if peer_list.len() >= MAX_DHT_PEERS { - break; - } - peer_list.push(*id.as_bytes()); - } - } - - let value = encode_peer_list(&peer_list); - let seq = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_secs().cast_signed()) - .unwrap_or(0); - - let item = MutableItem::new(signing_key.clone(), &value, seq, Some(salt.as_ref())); - match dht.put_mutable(item, None).await { - Ok(_) => { - debug!("published to DHT (seq={seq}, peers={})", peer_list.len()); - if let Ok(mut s) = state.lock() { - s.dht_status = DhtStatus::Ready; - s.last_dht_publish = Some(Instant::now()); - } - } - Err(e) => { - warn!("DHT publish failed: {e:#}"); - if let Ok(mut s) = state.lock() { - s.dht_status = DhtStatus::PublishFailed; - } - } - } -} - -// ── DHT helpers ───────────────────────────────────────────────── - -/// Query the DHT for existing peers (blocking bootstrap, called once on startup). -async fn dht_get_peers(keys: &PassphraseKeys) -> Vec { - let dht = match Dht::client() { - Ok(d) => d.as_async(), - Err(e) => { - warn!("failed to create DHT client for bootstrap: {e:#}"); - return vec![]; - } - }; - - let pub_key_bytes = keys.dht_signing_key.verifying_key().to_bytes(); - dht.get_mutable_most_recent(&pub_key_bytes, Some(&keys.dht_salt)).await.map_or_else( - || { - debug!("DHT bootstrap: no existing record"); - vec![] - }, - |record| { - let peers = decode_peer_list(record.value()); - debug!("DHT bootstrap: found {} peer(s)", peers.len()); - peers - }, - ) -} - -/// Encode a list of 32-byte peer IDs into bytes for DHT storage. -/// Format: `[count: u16 LE] [id_0: 32B] [id_1: 32B] ...` -#[must_use] -pub fn encode_peer_list(peers: &[[u8; 32]]) -> Vec { - #[allow(clippy::cast_possible_truncation)] // MAX_DHT_PEERS (27) fits in u16 - let count = peers.len().min(MAX_DHT_PEERS) as u16; - let mut buf = Vec::with_capacity(2 + (count as usize) * 32); - buf.extend_from_slice(&count.to_le_bytes()); - for id in peers.iter().take(MAX_DHT_PEERS) { - buf.extend_from_slice(id); - } - buf -} - -/// Decode a peer list from DHT record value. -#[must_use] -pub fn decode_peer_list(data: &[u8]) -> Vec { - if data.len() < 2 { - return vec![]; - } - let count = u16::from_le_bytes([data[0], data[1]]) as usize; - let mut peers = Vec::with_capacity(count); - let mut offset = 2; - for _ in 0..count { - if offset + 32 > data.len() { - break; - } - let bytes: [u8; 32] = match data[offset..offset + 32].try_into() { - Ok(b) => b, - Err(_) => break, - }; - if let Ok(pk) = PublicKey::from_bytes(&bytes) { - peers.push(pk); - } - offset += 32; - } - peers -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn passphrase_keys_deterministic() { - let k1 = PassphraseKeys::derive("test-passphrase"); - let k2 = PassphraseKeys::derive("test-passphrase"); - assert_eq!(k1.topic_id, k2.topic_id); - assert_eq!(k1.dht_signing_key.to_bytes(), k2.dht_signing_key.to_bytes()); - assert_eq!(k1.dht_salt, k2.dht_salt); - } - - #[test] - fn passphrase_keys_different() { - let k1 = PassphraseKeys::derive("alpha"); - let k2 = PassphraseKeys::derive("beta"); - assert_ne!(k1.topic_id, k2.topic_id); - assert_ne!(k1.dht_signing_key.to_bytes(), k2.dht_signing_key.to_bytes()); - } - - #[test] - fn peer_list_roundtrip() { - // Generate valid ed25519 public keys. - let keys: Vec<_> = (0..3) - .map(|_| { - let sk = iroh::SecretKey::generate(&mut rand::rng()); - *sk.public().as_bytes() - }) - .collect(); - let encoded = encode_peer_list(&keys); - let decoded = decode_peer_list(&encoded); - assert_eq!(decoded.len(), 3); - for (i, key) in keys.iter().enumerate() { - assert_eq!(*decoded[i].as_bytes(), *key); - } - } - - #[test] - fn peer_list_empty() { - let encoded = encode_peer_list(&[]); - let decoded = decode_peer_list(&encoded); - assert!(decoded.is_empty()); - } - - #[test] - fn peer_list_truncated_data() { - // Only 1 byte — too short for count. - assert!(decode_peer_list(&[0]).is_empty()); - // Count says 2 but only 1 peer present. - let mut data = vec![2, 0]; // count = 2 - data.extend_from_slice(&[0xAA; 32]); // only 1 peer - let decoded = decode_peer_list(&data); - assert_eq!(decoded.len(), 1); - } - - #[test] - fn dht_status_display() { - assert_eq!(format!("{}", DhtStatus::Bootstrapping), "Bootstrapping"); - assert_eq!(format!("{}", DhtStatus::Ready), "Ready"); - assert_eq!(format!("{}", DhtStatus::PublishFailed), "Failed"); - } - - #[test] - fn decode_peer_list_exact_boundary() { - // Build data with count=2 and exactly 2 + 32 + 32 = 66 bytes. - // Both peers should decode successfully. - let sk0 = iroh::SecretKey::generate(&mut rand::rng()); - let sk1 = iroh::SecretKey::generate(&mut rand::rng()); - let id0 = *sk0.public().as_bytes(); - let id1 = *sk1.public().as_bytes(); - - let mut data = vec![2, 0]; // count = 2 (u16 LE) - data.extend_from_slice(&id0); - data.extend_from_slice(&id1); - assert_eq!(data.len(), 66); - - let decoded = decode_peer_list(&data); - assert_eq!(decoded.len(), 2); - assert_eq!(*decoded[0].as_bytes(), id0); - assert_eq!(*decoded[1].as_bytes(), id1); - } - - #[test] - fn decode_peer_list_one_byte_short() { - // Build data with count=2 but only 2 + 32 + 31 = 65 bytes. - // First peer decodes, second is truncated and skipped. - let sk0 = iroh::SecretKey::generate(&mut rand::rng()); - let sk1 = iroh::SecretKey::generate(&mut rand::rng()); - let id0 = *sk0.public().as_bytes(); - let id1 = *sk1.public().as_bytes(); - - let mut data = vec![2, 0]; // count = 2 (u16 LE) - data.extend_from_slice(&id0); - data.extend_from_slice(&id1[..31]); // only 31 bytes of second peer - assert_eq!(data.len(), 65); - - let decoded = decode_peer_list(&data); - assert_eq!(decoded.len(), 1); - assert_eq!(*decoded[0].as_bytes(), id0); - } - - #[test] - fn decode_peer_list_count_header_only() { - // Data with exactly 2 bytes (count=0). Must not be rejected by the length check. - // Kills mutant: `data.len() < 2` → `data.len() <= 2`. - let data = [0u8, 0]; // count = 0 - let decoded = decode_peer_list(&data); - assert!(decoded.is_empty()); - - // Data with 2 bytes but count=1 and no peer data — decodes 0 (truncated). - let data = [1u8, 0]; // count = 1 but no peer bytes - let decoded = decode_peer_list(&data); - assert!(decoded.is_empty()); - } - - mod proptests { - use super::*; - use proptest::prelude::*; - - proptest! { - #[test] - fn peer_list_roundtrip_prop(count in 0usize..=30) { - let keys: Vec<[u8; 32]> = (0..count) - .map(|_| *iroh::SecretKey::generate(&mut rand::rng()).public().as_bytes()) - .collect(); - let encoded = encode_peer_list(&keys); - let decoded = decode_peer_list(&encoded); - let expected = count.min(MAX_DHT_PEERS); - prop_assert_eq!(decoded.len(), expected); - } - } +// Shared via Arc for the TUI. +impl Gossip { + /// Wrap in `Arc` for sharing with the TUI task. + #[must_use] + pub fn into_arc(self) -> Arc { + Arc::new(self) } } diff --git a/src/main.rs b/src/main.rs index e30e991..9a37068 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,6 @@ use tokio::sync::mpsc; use tokio_util::sync::CancellationToken; use tracing::{info, warn}; -use crate::gossip::PassphraseKeys; use crate::rate_limit::RateLimiter; use crate::router::Stats; @@ -66,7 +65,7 @@ async fn main() -> Result<()> { setup_logging(cli.log_only, &cfg); // Ephemeral identity — fresh keypair each launch. - let secret_key = SecretKey::generate(&mut rand::rng()); + let secret_key = SecretKey::generate(); let node_id = secret_key.public(); info!("donglora-bridge starting, ephemeral id: {}", node_id.fmt_short()); @@ -74,10 +73,6 @@ async fn main() -> Result<()> { let radio_config = cfg.radio.to_lora_config()?; let retry_policy = cfg.tx.to_retry_policy()?; - // Derive passphrase keys. - let keys = PassphraseKeys::derive(&cfg.bridge.passphrase); - info!("topic: {}", hex::encode(&keys.topic_id.as_bytes()[..8])); - // Create rate limiter. let cr_denom: u8 = match radio_config.cr { donglora_client::LoRaCodingRate::Cr4_5 => 5, @@ -94,8 +89,9 @@ async fn main() -> Result<()> { ); info!("rate limiter: {:.1} pps", rate_limiter.rate_pps()); - // Create gossip network. - let (swarm, gossip_event_rx, gossip_frame_tx) = gossip::Gossip::new(secret_key, &keys).await?; + // Create gossip network + DHT rendezvous. + let (swarm, gossip_event_rx, gossip_frame_tx) = + gossip::Gossip::new(secret_key, &cfg.bridge.passphrase).await?; let swarm = Arc::new(swarm); // Start radio. diff --git a/src/packet.rs b/src/packet.rs index 56d5aa5..d917e82 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -144,7 +144,7 @@ mod tests { #[test] fn gossip_frame_roundtrip() { - let key = iroh::SecretKey::generate(&mut rand::rng()); + let key = iroh::SecretKey::generate(); let frame = GossipFrame::new(&key.public(), -85, 8, vec![1, 2, 3, 4, 5]); let encoded = frame.encode(); let decoded = GossipFrame::decode(&encoded).expect("decode should succeed"); @@ -235,7 +235,7 @@ mod tests { snr in -32i16..=32i16, payload in proptest::collection::vec(any::(), 0..256), ) { - let key = iroh::SecretKey::generate(&mut rand::rng()); + let key = iroh::SecretKey::generate(); let frame = GossipFrame::new(&key.public(), rssi, snr, payload.clone()); let encoded = frame.encode(); let decoded = GossipFrame::decode(&encoded).unwrap(); diff --git a/src/tui.rs b/src/tui.rs index 3ef59bb..528fd85 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -21,7 +21,7 @@ use ratatui::widgets::{Block, Borders, Clear, Paragraph, Sparkline}; use tokio::sync::mpsc; use tokio_util::sync::CancellationToken; -use crate::gossip::Gossip; +use crate::gossip::{DhtStatus, Gossip, RendezvousState}; use crate::radio::ConfigSource; use crate::router::{ PacketAction, PacketDirection, PacketLogEntry, RadioConfigInfo, Stats, StatsSnapshot, TxRetryState, @@ -210,7 +210,7 @@ fn tui_loop( let snap = stats.snapshot(); let log_slice = log_entries.make_contiguous(); series.update(&snap, log_slice); - let swarm_state = gossip.swarm_state(); + let swarm_state = gossip.state(); let config_info = config_watch.borrow().clone(); terminal.draw(|frame| { draw_ui(frame, frame.area(), &config_info, &snap, &swarm_state, log_slice, start_time, series); @@ -241,7 +241,7 @@ fn draw_ui( area: Rect, config_info: &RadioConfigInfo, stats: &StatsSnapshot, - swarm_state: &crate::gossip::SwarmState, + swarm_state: &RendezvousState, log_entries: &[PacketLogEntry], start_time: Instant, series: &TimeSeries, @@ -316,19 +316,20 @@ fn status_pill(label: &str, ok: bool) -> Span<'static> { // ── Network panel (was Swarm) ──────────────────────────────────── -fn draw_network(frame: &mut ratatui::Frame, area: Rect, state: &crate::gossip::SwarmState) { +fn draw_network(frame: &mut ratatui::Frame, area: Rect, state: &RendezvousState) { let block = panel_block(" Network "); let dht_color = match state.dht_status { - crate::gossip::DhtStatus::Ready => Color::Green, - crate::gossip::DhtStatus::Bootstrapping => C_RATE_DROP, - crate::gossip::DhtStatus::PublishFailed => C_QUEUE_DROP, + DhtStatus::Ready => Color::Green, + DhtStatus::Bootstrapping => C_RATE_DROP, + DhtStatus::PublishFailing => C_QUEUE_DROP, }; let neighbor_color = if state.neighbor_count > 0 { Color::Green } else { C_RATE_DROP }; - let last_pub = state.last_dht_publish.map_or_else(|| "-".into(), |t| format!("{}s ago", t.elapsed().as_secs())); + let last_pub = state.last_publish.map_or_else(|| "-".into(), |t| format!("{}s ago", t.elapsed().as_secs())); + let topic_hash = hex::encode(&state.topic_id.as_bytes()[..8]); let lines = vec![ - kv_line("ID", &state.topic_hash, C_ACCENT), + kv_line("ID", &topic_hash, C_ACCENT), kv_line("Peers", &format!("{}", state.neighbor_count), neighbor_color), kv_line("DHT", &format!("{}", state.dht_status), dht_color), kv_line("Published", &last_pub, C_LABEL),