diff --git a/iroh-automerge/Cargo.lock b/iroh-automerge/Cargo.lock index 7a3b913..d47d537 100644 --- a/iroh-automerge/Cargo.lock +++ b/iroh-automerge/Cargo.lock @@ -28,6 +28,18 @@ dependencies = [ "generic-array", ] +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -37,6 +49,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -256,6 +274,61 @@ dependencies = [ "uuid", ] +[[package]] +name = "axum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.3.1", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "backoff" version = "0.4.0" @@ -282,6 +355,23 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "bao-tree" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f7a89a8ee5889d2593ae422ce6e1bb03e48a0e8a16e4fa0882dfcbe7e182ef" +dependencies = [ + "bytes", + "futures-lite 2.3.0", + "genawaiter", + "iroh-blake3", + "iroh-io", + "positioned-io", + "range-collections", + "self_cell", + "smallvec", +] + [[package]] name = "base16ct" version = "0.2.0" @@ -306,6 +396,21 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "binary-merge" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597bb81c80a54b6a4381b23faba8d7774b144c94cbd1d6fe3f1329bd776554ab" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -371,6 +476,9 @@ name = "bytes" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +dependencies = [ + "serde", +] [[package]] name = "cc" @@ -403,8 +511,10 @@ checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", + "js-sys", "num-traits", "serde", + "wasm-bindgen", "windows-targets 0.52.5", ] @@ -847,6 +957,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "educe" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "either" version = "1.13.0" @@ -899,6 +1021,19 @@ dependencies = [ "syn 2.0.68", ] +[[package]] +name = "enum-ordinalize" +version = "3.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.68", +] + [[package]] name = "enumflags2" version = "0.7.10" @@ -940,6 +1075,16 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76a5aa24577083f8190ad401e376b55887c7cd9083ae95d83ceec5d28ea78125" +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "event-listener" version = "4.0.3" @@ -1189,6 +1334,37 @@ dependencies = [ "byteorder", ] +[[package]] +name = "genawaiter" +version = "0.99.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86bd0361bcbde39b13475e6e36cb24c329964aa2611be285289d1e4b751c1a0" +dependencies = [ + "futures-core", + "genawaiter-macro", + "genawaiter-proc-macro", + "proc-macro-hack", +] + +[[package]] +name = "genawaiter-macro" +version = "0.99.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b32dfe1fdfc0bbde1f22a5da25355514b5e450c33a6af6770884c8750aedfbc" + +[[package]] +name = "genawaiter-proc-macro" +version = "0.99.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784f84eebc366e15251c4a8c3acee82a6a6f427949776ecb88377362a9621738" +dependencies = [ + "proc-macro-error", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1289,6 +1465,19 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown", +] [[package]] name = "heapless" @@ -1674,6 +1863,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "inplace-vec-builder" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf64c2edc8226891a71f127587a2861b132d2b942310843814d5001d99a1d307" +dependencies = [ + "smallvec", +] + [[package]] name = "instant" version = "0.1.13" @@ -1701,6 +1899,48 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "iroh" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c2abfb692f6b0539aca300ebc96a12cd515387ee92f662b0358489a1bbf435" +dependencies = [ + "anyhow", + "bao-tree", + "bytes", + "derive_more", + "flume", + "futures-buffered", + "futures-lite 2.3.0", + "futures-util", + "genawaiter", + "hex", + "iroh-base", + "iroh-blobs", + "iroh-docs", + "iroh-gossip", + "iroh-io", + "iroh-metrics", + "iroh-net", + "iroh-quinn", + "num_cpus", + "parking_lot", + "portable-atomic", + "postcard", + "quic-rpc", + "rand", + "ref-cast", + "serde", + "strum 0.25.0", + "tempfile", + "thiserror", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", + "walkdir", +] + [[package]] name = "iroh-automerge" version = "0.1.0" @@ -1708,11 +1948,11 @@ dependencies = [ "anyhow", "automerge", "clap", - "iroh-net", + "iroh", "postcard", "serde", "tokio", - "url", + "tracing-subscriber", ] [[package]] @@ -1734,6 +1974,7 @@ dependencies = [ "postcard", "rand", "rand_core", + "redb 2.1.1", "serde", "serde-error", "ssh-key", @@ -1756,6 +1997,124 @@ dependencies = [ "constant_time_eq", ] +[[package]] +name = "iroh-blobs" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed4dbc80349ff769c0bc0f634f747130de2e3e201ff52b6266ca5459e8ebbb44" +dependencies = [ + "anyhow", + "bao-tree", + "bytes", + "chrono", + "derive_more", + "flume", + "futures-buffered", + "futures-lite 2.3.0", + "genawaiter", + "hashlink", + "hex", + "iroh-base", + "iroh-io", + "iroh-metrics", + "iroh-net", + "num_cpus", + "parking_lot", + "postcard", + "rand", + "range-collections", + "redb 1.5.1", + "redb 2.1.1", + "reflink-copy", + "self_cell", + "serde", + "smallvec", + "tempfile", + "thiserror", + "tokio", + "tokio-util", + "tracing", + "tracing-futures", +] + +[[package]] +name = "iroh-docs" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2fd3285684b39ded7b44cf8f655b61bf0df5689823f0b9ecf9b26be3c343853" +dependencies = [ + "anyhow", + "bytes", + "derive_more", + "ed25519-dalek", + "flume", + "futures-buffered", + "futures-lite 2.3.0", + "futures-util", + "hex", + "iroh-base", + "iroh-blake3", + "iroh-blobs", + "iroh-gossip", + "iroh-metrics", + "iroh-net", + "lru", + "num_enum", + "postcard", + "rand", + "rand_core", + "redb 1.5.1", + "redb 2.1.1", + "self_cell", + "serde", + "strum 0.25.0", + "tempfile", + "thiserror", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", +] + +[[package]] +name = "iroh-gossip" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de2dcd328c13e90bb86deebd76cf5e922caca31f5cc8bbfeeda52657c94f145b" +dependencies = [ + "anyhow", + "bytes", + "derive_more", + "ed25519-dalek", + "futures-lite 2.3.0", + "genawaiter", + "indexmap", + "iroh-base", + "iroh-blake3", + "iroh-metrics", + "iroh-net", + "postcard", + "rand", + "rand_core", + "serde", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "iroh-io" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d1047ad5ca29ab4ff316b6830d86e7ea52cea54325e4d4a849692e1274b498" +dependencies = [ + "bytes", + "futures-lite 2.3.0", + "pin-project", + "smallvec", + "tokio", +] + [[package]] name = "iroh-metrics" version = "0.19.0" @@ -1785,6 +2144,7 @@ checksum = "2ff7e91d529120256fa2f74026aebd201ba0743b8ab2a8c486c39b17ed606c99" dependencies = [ "aead", "anyhow", + "axum", "backoff", "base64 0.22.1", "bytes", @@ -1834,7 +2194,7 @@ dependencies = [ "serde", "smallvec", "socket2", - "strum", + "strum 0.26.3", "stun-rs", "surge-ping", "thiserror", @@ -1964,6 +2324,12 @@ 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 = "lock_api" version = "0.4.12" @@ -1980,6 +2346,15 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "lru" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +dependencies = [ + "hashbrown", +] + [[package]] name = "lru-cache" version = "0.1.2" @@ -1995,6 +2370,21 @@ 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 = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "md5" version = "0.7.0" @@ -2175,6 +2565,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" +[[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.5" @@ -2324,6 +2724,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "p256" version = "0.13.2" @@ -2596,6 +3002,16 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" +[[package]] +name = "positioned-io" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccabfeeb89c73adf4081f0dca7f8e28dbda90981a222ceea37f619e93ea6afe9" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "postcard" version = "1.0.8" @@ -2685,6 +3101,38 @@ dependencies = [ "toml_edit", ] +[[package]] +name = "proc-macro-error" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "syn-mid", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + [[package]] name = "proc-macro2" version = "1.0.86" @@ -2732,6 +3180,30 @@ dependencies = [ "winapi", ] +[[package]] +name = "quic-rpc" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "110f0fbbf7c4a694902e11d890157245801d89a18d8e9b8d9d2afd91358a6a7c" +dependencies = [ + "anyhow", + "bincode", + "derive_more", + "educe", + "flume", + "futures-lite 2.3.0", + "futures-sink", + "futures-util", + "hex", + "iroh-quinn", + "pin-project", + "serde", + "tokio", + "tokio-serde", + "tokio-util", + "tracing", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -2849,6 +3321,18 @@ dependencies = [ "rand_core", ] +[[package]] +name = "range-collections" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca9edd21e2db51000ac63eccddabba622f826e631a60be7bade9bd6a76b69537" +dependencies = [ + "binary-merge", + "inplace-vec-builder", + "ref-cast", + "smallvec", +] + [[package]] name = "raw-cpuid" version = "11.0.2" @@ -2870,6 +3354,24 @@ dependencies = [ "yasna", ] +[[package]] +name = "redb" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd7f82ecd6ba647a39dd1a7172b8a1cd9453c0adee6da20cb553d83a9a460fa5" +dependencies = [ + "libc", +] + +[[package]] +name = "redb" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6dd20d3cdeb9c7d2366a0b16b93b35b75aec15309fbeb7ce477138c9f68c8c0" +dependencies = [ + "libc", +] + [[package]] name = "redox_syscall" version = "0.5.2" @@ -2879,6 +3381,37 @@ dependencies = [ "bitflags 2.6.0", ] +[[package]] +name = "ref-cast" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "reflink-copy" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d731e7e3ebfcf422d96b8473e507d5b64790900dd5464772d38d1da9da24d3a" +dependencies = [ + "cfg-if", + "rustix", + "windows 0.57.0", +] + [[package]] name = "regex" version = "1.10.5" @@ -2887,8 +3420,17 @@ checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", +] + +[[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]] @@ -2899,7 +3441,7 @@ checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.4", ] [[package]] @@ -2908,6 +3450,12 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" +[[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.4" @@ -3101,6 +3649,19 @@ dependencies = [ "nom", ] +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "rustls" version = "0.21.12" @@ -3206,6 +3767,15 @@ dependencies = [ "cipher", ] +[[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.23" @@ -3320,6 +3890,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -3364,6 +3944,15 @@ dependencies = [ "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 = "shared_child" version = "1.0.0" @@ -3426,6 +4015,9 @@ name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] [[package]] name = "smol_str" @@ -3557,13 +4149,35 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9426b2a0c03e6cc2ea8dbc0168dbbf943f88755e409fb91bcb8f6a268305f4a" +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +dependencies = [ + "strum_macros 0.25.3", +] + [[package]] name = "strum" version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ - "strum_macros", + "strum_macros 0.26.4", +] + +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.68", ] [[package]] @@ -3647,6 +4261,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn-mid" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea305d57546cc8cd04feb14b62ec84bf17f50e3f7b12560d7bfa9265f39d9ed" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "sync_wrapper" version = "0.1.2" @@ -3730,6 +4355,18 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand 2.1.0", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "thiserror" version = "1.0.61" @@ -3750,6 +4387,16 @@ dependencies = [ "syn 2.0.68", ] +[[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.36" @@ -3875,6 +4522,33 @@ dependencies = [ "x509-parser 0.16.0", ] +[[package]] +name = "tokio-serde" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "911a61637386b789af998ee23f50aa30d5fd7edcec8d6d3dedae5e5815205466" +dependencies = [ + "bincode", + "bytes", + "educe", + "futures-core", + "futures-sink", + "pin-project", + "serde", +] + +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + [[package]] name = "tokio-util" version = "0.7.11" @@ -3884,7 +4558,10 @@ dependencies = [ "bytes", "futures-core", "futures-sink", + "futures-util", + "hashbrown", "pin-project-lite", + "slab", "tokio", ] @@ -3918,6 +4595,7 @@ dependencies = [ "tokio", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -3962,6 +4640,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[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.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -4077,6 +4795,12 @@ dependencies = [ "serde", ] +[[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.4" @@ -4089,6 +4813,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" +[[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" @@ -4229,6 +4963,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -4252,8 +4995,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ "windows-core 0.52.0", - "windows-implement", - "windows-interface", + "windows-implement 0.52.0", + "windows-interface 0.52.0", "windows-targets 0.52.5", ] @@ -4267,6 +5010,16 @@ dependencies = [ "windows-targets 0.52.5", ] +[[package]] +name = "windows" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +dependencies = [ + "windows-core 0.57.0", + "windows-targets 0.52.5", +] + [[package]] name = "windows-core" version = "0.51.1" @@ -4295,6 +5048,18 @@ dependencies = [ "windows-targets 0.52.5", ] +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement 0.57.0", + "windows-interface 0.57.0", + "windows-result", + "windows-targets 0.52.5", +] + [[package]] name = "windows-implement" version = "0.52.0" @@ -4306,6 +5071,17 @@ dependencies = [ "syn 2.0.68", ] +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", +] + [[package]] name = "windows-interface" version = "0.52.0" @@ -4317,6 +5093,17 @@ dependencies = [ "syn 2.0.68", ] +[[package]] +name = "windows-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", +] + [[package]] name = "windows-result" version = "0.1.2" @@ -4581,6 +5368,26 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edb37266251c28b03d08162174a91c3a092e3bd4f476f8205ee1c507b78b7bdc" +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "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.68", +] + [[package]] name = "zeroize" version = "1.8.1" diff --git a/iroh-automerge/Cargo.toml b/iroh-automerge/Cargo.toml index ace6bcc..33d0e67 100644 --- a/iroh-automerge/Cargo.toml +++ b/iroh-automerge/Cargo.toml @@ -7,8 +7,8 @@ edition = "2021" anyhow = "1.0.80" automerge = "0.5.7" clap = { version = "4.5.1", features = ["derive"] } -iroh-net = "0.19" +iroh = "0.19" postcard = "1.0.8" serde = { version = "1.0.197", features = ["derive"] } tokio = { version = "1.36.0", features = ["full"] } -url = "2.5.0" +tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } diff --git a/iroh-automerge/README.md b/iroh-automerge/README.md index 57c3210..d9f1f40 100644 --- a/iroh-automerge/README.md +++ b/iroh-automerge/README.md @@ -2,23 +2,54 @@ > Integration of iroh with [automerge](https://automerge.org). +## What is automerge? -## Running +Automerge is a Conflict-Free Replicated Data Type (CRDT). +It represents JSON-like data that can be efficiently synchronized between machines without needing +a single source of truth, unlike traditional databases. +This enables machine to simply gossip updates and data with each other, eventually converging to the same state on every replica. + +## What does this example do? + +This example highlights how to integrate [automerge's sync protocol] with [iroh's peer-to-peer connectivity]. + +To run this example, please open a terminal, clone the examples repository using `git clone https://github.com/n0-computer/iroh-examples` +and enter the `iroh-automerge` directory using `cd iroh-automerge`. + +First, we create one node that listens for sync requests using iroh's connections: ```sh # First Terminal > cargo run Running -Node Id: t4xj3pbb2fzkmphnu5npfznpir62i5zxacbgywf22ev5w4sjelma -https://euw1-1.derp.iroh.network./ +Node Id: lkpz2uw6jf7qahl7oo6qc46qad5ysszhtdzqyotkb3pwtd7sv3va ``` +In iroh, nodes are neither identified by domain name (like websites in the internet), nor are they identified by IP addresses. +Instead, nodes are identified by cryptographic public keys, such as the one printed using the `cargo run` command above. + +You can exchange this public key with anyone to establish a secure, encrypted connection anywhere. +In most cases this connection is even automatically upgraded to a direct peer-to-peer connection. + +We make sure to copy this public key, so we can talk to this node from somewhere else. +We also open another terminal, while keeping the original one open, so this node keeps running. + +Now, let's start another node in the second terminal that will connect to the first node: + ```sh # Second Terminal -> cargo run -- --remote-id t4xj3pbb2fzkmphnu5npfznpir62i5zxacbgywf22ev5w4sjelma --remote-relay 'https://euw1-1.derp.iroh.network./'` +> cargo run -- --remote-id t4xj3pbb2fzkmphnu5npfznpir62i5zxacbgywf22ev5w4sjelma` Running -Node Id: dhdgta6lsyya2ms66cndoxbkoiv3kgkmu4dfe2awzeqh6hsfmwlq -https://euw1-1.derp.iroh.network./ +Node Id: gcq5e7mcsvwgtxfvbu7w7rkikxhfudbqt5yvl34f47qlmsyuy7wa +> +``` + +This will connect the two nodes, have them exchnage data and finish running within a couple of seconds. + +Coming back to the first terminal, we'll see that the receiving end got all data: + +```sh +# Back on the first Terminal State key-0 => "value-0" key-1 => "value-1" @@ -26,3 +57,6 @@ key-2 => "value-2" key-3 => "value-3" key-4 => "value-4" ``` + +[automerge's sync protocol]: https://docs.rs/automerge/latest/automerge/sync/ +[iroh's peer-to-peer connectivity]: https://docs.rs/iroh/latest/iroh/net/index.html diff --git a/iroh-automerge/src/main.rs b/iroh-automerge/src/main.rs index 14dfaa9..be90ac7 100644 --- a/iroh-automerge/src/main.rs +++ b/iroh-automerge/src/main.rs @@ -1,227 +1,83 @@ -use std::ops::{Deref, DerefMut}; - -use anyhow::{ensure, Result}; -use automerge::{ - sync::{self, SyncDoc}, - transaction::Transactable, - AutoCommit, ReadDoc, -}; -use clap::Parser; -use iroh_net as inet; -use serde::{Deserialize, Serialize}; - -struct Peer { - auto_commit: AutoCommit, - sync_state: sync::State, - storage: Vec, - ep: inet::Endpoint, -} - -const ALPN: &[u8] = b"iroh/automerge/1"; -const DEFAULT_PORT: u16 = 8888; - -impl Peer { - async fn new() -> Result { - let ep = inet::Endpoint::builder() - .alpns(vec![ALPN.to_vec()]) - .bind(DEFAULT_PORT) - .await?; - - let addr = ep.node_addr().await?; - println!( - "Running \nNode Id: {}\n{}", - addr.node_id, - addr.info.relay_url.unwrap() - ); - - Ok(Self { - auto_commit: AutoCommit::new(), - sync_state: sync::State::new(), - storage: Vec::new(), - ep, - }) - } - - fn save(&mut self) { - self.storage = self.auto_commit.save(); - } +use std::sync::Arc; - #[allow(unused)] - async fn load(data: &[u8]) -> Result { - let ep = inet::Endpoint::builder() - .alpns(vec![ALPN.to_vec()]) - .bind(DEFAULT_PORT) - .await?; - - let auto_commit = AutoCommit::load(data)?; - Ok(Self { - auto_commit, - sync_state: sync::State::new(), - storage: data.to_vec(), - ep, - }) - } - - fn generate_sync_message(&mut self) -> Option { - self.auto_commit - .sync() - .generate_sync_message(&mut self.sync_state) - } - - fn receive_sync_message(&mut self, msg: sync::Message) -> Result<()> { - self.auto_commit - .sync() - .receive_sync_message(&mut self.sync_state, msg)?; - - self.save(); - Ok(()) - } -} +use anyhow::Result; +use automerge::{transaction::Transactable, Automerge, ReadDoc}; +use clap::Parser; +use iroh::node::{Node, ProtocolHandler}; -impl Deref for Peer { - type Target = AutoCommit; - fn deref(&self) -> &Self::Target { - &self.auto_commit - } -} +use protocol::IrohAutomergeProtocol; +use tokio::sync::mpsc; -impl DerefMut for Peer { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.auto_commit - } -} +mod protocol; #[derive(Parser)] #[command(version, about, long_about = None)] struct Cli { #[clap(long)] - remote_id: Option, - #[clap(long)] - remote_relay: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -enum Protocol { - SyncMessage(Vec), - Done, + remote_id: Option, } #[tokio::main] async fn main() -> Result<()> { - let opts = Cli::parse(); + tracing_subscriber::fmt::init(); - let mut peer1 = Peer::new().await?; - - for i in 0..5 { - peer1.put(automerge::ROOT, format!("key-{i}"), format!("value-{i}"))?; - } - - peer1.save(); + let opts = Cli::parse(); + // We set up a channel so we can subscribe to sync events from the automerge protocol + let (sync_sender, mut sync_finished) = mpsc::channel(10); + let automerge = IrohAutomergeProtocol::new(Automerge::new(), sync_sender); + let iroh = Node::memory() + .disable_docs() + .build() + .await? + .accept( + IrohAutomergeProtocol::ALPN, + Arc::clone(&automerge) as Arc, + ) + .spawn() + .await?; + + let addr = iroh.my_addr().await?; + + println!("Running\nNode Id: {}", addr.node_id,); + + // we distinguish the roles in protocol based on if the --remote-id CLI argument is present if let Some(remote_id) = opts.remote_id { - let remote_relay = opts.remote_relay.expect("missing derp"); - let node_addr = inet::NodeAddr::new(remote_id).with_relay_url(remote_relay.into()); - let conn = peer1.ep.connect(node_addr, ALPN).await?; - let (mut send, mut recv) = conn.open_bi().await?; - - let mut is_local_done = false; - loop { - let msg = match peer1.generate_sync_message() { - Some(msg) => Protocol::SyncMessage(msg.encode()), - None => Protocol::Done, - }; - - if !is_local_done { - let encoded = postcard::to_stdvec(&msg)?; - send.write_all(&(encoded.len() as u64).to_le_bytes()) - .await?; - send.write_all(&encoded).await?; - is_local_done = matches!(msg, Protocol::Done); - } - - let mut incoming_len = [0u8; 8]; - recv.read_exact(&mut incoming_len).await?; - let len = u64::from_le_bytes(incoming_len); - - let mut buffer = vec![0u8; len as usize]; - recv.read_exact(&mut buffer).await?; - let msg: Protocol = postcard::from_bytes(&buffer)?; - - let is_remote_done = matches!(msg, Protocol::Done); - - // process incominng message - if let Protocol::SyncMessage(sync_msg) = msg { - let sync_msg = sync::Message::decode(&sync_msg)?; - peer1.receive_sync_message(sync_msg)?; - } - - if is_remote_done && is_local_done { - // both sides are done - break; - } + // on the provider side: + + // Put some data in the document to sync + let mut doc = automerge.fork_doc().await; + let mut t = doc.transaction(); + for i in 0..5 { + t.put(automerge::ROOT, format!("key-{i}"), format!("value-{i}"))?; } + t.commit(); + automerge.merge_doc(&mut doc).await?; + + // connect to the other node + let node_addr = iroh::net::NodeAddr::new(remote_id); + let conn = iroh + .endpoint() + .connect(node_addr, IrohAutomergeProtocol::ALPN) + .await?; - send.finish().await?; + // initiate a sync session over an iroh-net direct connection + automerge.initiate_sync(conn).await?; } else { - let mut conn = peer1 - .ep - .accept() - .await - .ok_or_else(|| anyhow::anyhow!("no connection"))?; - - let alpn = conn.alpn().await?; - let conn = conn.await?; - - ensure!(alpn == ALPN, "invalid alpn"); - - let (mut send, mut recv) = conn.accept_bi().await?; - - let mut is_local_done = false; - loop { - let mut incoming_len = [0u8; 8]; - recv.read_exact(&mut incoming_len).await?; - let len = u64::from_le_bytes(incoming_len); - - let mut buffer = vec![0u8; len as usize]; - recv.read_exact(&mut buffer).await?; - let msg: Protocol = postcard::from_bytes(&buffer)?; - - let is_remote_done = matches!(msg, Protocol::Done); - - // process incominng message - if let Protocol::SyncMessage(sync_msg) = msg { - let sync_msg = sync::Message::decode(&sync_msg)?; - peer1.receive_sync_message(sync_msg)?; - } - - let msg = match peer1.generate_sync_message() { - Some(msg) => Protocol::SyncMessage(msg.encode()), - None => Protocol::Done, - }; - - if !is_local_done { - let encoded = postcard::to_stdvec(&msg)?; - send.write_all(&(encoded.len() as u64).to_le_bytes()) - .await?; - send.write_all(&encoded).await?; - is_local_done = matches!(msg, Protocol::Done); - } - - if is_remote_done && is_local_done { - // both sides are done - break; - } + // on the receiver side: + + // wait for the first sync to finish + let doc = sync_finished.recv().await.unwrap(); + println!("State"); + let keys: Vec<_> = doc.keys(automerge::ROOT).collect(); + for key in keys { + let (value, _) = doc.get(automerge::ROOT, &key)?.unwrap(); + println!("{} => {}", key, value); } - - send.finish().await?; } - println!("State"); - let keys: Vec<_> = peer1.keys(automerge::ROOT).collect(); - for key in keys { - let (value, _) = peer1.get(automerge::ROOT, &key)?.unwrap(); - println!("{} => {}", key, value); - } + // finally shut down + iroh.shutdown().await?; Ok(()) } diff --git a/iroh-automerge/src/protocol.rs b/iroh-automerge/src/protocol.rs new file mode 100644 index 0000000..333dbc6 --- /dev/null +++ b/iroh-automerge/src/protocol.rs @@ -0,0 +1,165 @@ +use std::{future::Future, pin::Pin, sync::Arc}; + +use anyhow::Result; +use automerge::{ + sync::{self, SyncDoc}, + Automerge, +}; +use iroh::{ + net::endpoint::{RecvStream, SendStream}, + node::ProtocolHandler, +}; +use serde::{Deserialize, Serialize}; +use tokio::sync::{mpsc, Mutex}; + +#[derive(Debug)] +pub struct IrohAutomergeProtocol { + inner: Mutex, + sync_finished: mpsc::Sender, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +enum Protocol { + SyncMessage(Vec), + Done, +} + +impl IrohAutomergeProtocol { + pub const ALPN: &'static [u8] = b"iroh/automerge/1"; + + pub fn new(doc: Automerge, sync_finished: mpsc::Sender) -> Arc { + Arc::new(Self { + inner: Mutex::new(doc), + sync_finished, + }) + } + + pub async fn fork_doc(&self) -> Automerge { + let automerge = self.inner.lock().await; + automerge.fork() + } + + pub async fn merge_doc(&self, doc: &mut Automerge) -> Result<()> { + let mut automerge = self.inner.lock().await; + automerge.merge(doc)?; + Ok(()) + } + + async fn send_msg(msg: Protocol, send: &mut SendStream) -> Result<()> { + let encoded = postcard::to_stdvec(&msg)?; + send.write_all(&(encoded.len() as u64).to_le_bytes()) + .await?; + send.write_all(&encoded).await?; + Ok(()) + } + + async fn recv_msg(recv: &mut RecvStream) -> Result { + let mut incoming_len = [0u8; 8]; + recv.read_exact(&mut incoming_len).await?; + let len = u64::from_le_bytes(incoming_len); + + let mut buffer = vec![0u8; len as usize]; + recv.read_exact(&mut buffer).await?; + let msg: Protocol = postcard::from_bytes(&buffer)?; + Ok(msg) + } + + pub async fn initiate_sync( + self: Arc, + conn: iroh::net::endpoint::Connection, + ) -> Result<()> { + let (mut send, mut recv) = conn.open_bi().await?; + + let mut doc = self.fork_doc().await; + let mut sync_state = sync::State::new(); + + let mut is_local_done = false; + loop { + let msg = match doc.generate_sync_message(&mut sync_state) { + Some(msg) => Protocol::SyncMessage(msg.encode()), + None => Protocol::Done, + }; + + if !is_local_done { + is_local_done = matches!(msg, Protocol::Done); + Self::send_msg(msg, &mut send).await?; + } + + let msg = Self::recv_msg(&mut recv).await?; + let is_remote_done = matches!(msg, Protocol::Done); + + // process incoming message + if let Protocol::SyncMessage(sync_msg) = msg { + let sync_msg = sync::Message::decode(&sync_msg)?; + doc.receive_sync_message(&mut sync_state, sync_msg)?; + self.merge_doc(&mut doc).await?; + } + + if is_remote_done && is_local_done { + // both sides are done + break; + } + } + + send.finish().await?; + + Ok(()) + } + + pub async fn respond_sync( + self: Arc, + conn: iroh::net::endpoint::Connecting, + ) -> Result<()> { + let (mut send, mut recv) = conn.await?.accept_bi().await?; + + let mut doc = self.fork_doc().await; + let mut sync_state = sync::State::new(); + + let mut is_local_done = false; + loop { + let msg = Self::recv_msg(&mut recv).await?; + let is_remote_done = matches!(msg, Protocol::Done); + + // process incoming message + if let Protocol::SyncMessage(sync_msg) = msg { + let sync_msg = sync::Message::decode(&sync_msg)?; + doc.receive_sync_message(&mut sync_state, sync_msg)?; + self.merge_doc(&mut doc).await?; + } + + let msg = match doc.generate_sync_message(&mut sync_state) { + Some(msg) => Protocol::SyncMessage(msg.encode()), + None => Protocol::Done, + }; + + if !is_local_done { + is_local_done = matches!(msg, Protocol::Done); + Self::send_msg(msg, &mut send).await?; + } + + if is_remote_done && is_local_done { + // both sides are done + break; + } + } + + send.finish().await?; + + Ok(()) + } +} + +impl ProtocolHandler for IrohAutomergeProtocol { + fn accept( + self: Arc, + conn: iroh::net::endpoint::Connecting, + ) -> Pin> + Send + 'static>> { + Box::pin(async move { + Arc::clone(&self).respond_sync(conn).await?; + + self.sync_finished.send(self.fork_doc().await).await?; + + Ok(()) + }) + } +}