diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 00000000..226dec96 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,3 @@ +[target.wasm32-unknown-unknown] +runner = "wasm-bindgen-test-runner" +rustflags = ['--cfg', 'getrandom_backend="wasm_js"'] diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index f87f6428..3ebee452 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -2,7 +2,7 @@ name: CI on: pull_request: - types: [ 'labeled', 'unlabeled', 'opened', 'synchronize', 'reopened' ] + types: ["labeled", "unlabeled", "opened", "synchronize", "reopened"] merge_group: push: branches: @@ -24,7 +24,7 @@ jobs: tests: name: CI Test Suite if: "github.event_name != 'pull_request' || ! contains(github.event.pull_request.labels.*.name, 'flaky-test')" - uses: './.github/workflows/tests.yaml' + uses: "./.github/workflows/tests.yaml" cross_build: name: Cross Build Only @@ -35,8 +35,8 @@ jobs: fail-fast: false matrix: target: - # cross tests are currently broken vor armv7 and aarch64 - # see https://github.com/cross-rs/cross/issues/1311 + # cross tests are currently broken vor armv7 and aarch64 + # see https://github.com/cross-rs/cross/issues/1311 # - armv7-linux-androideabi # - aarch64-linux-android # Freebsd execution fails in cross @@ -45,29 +45,29 @@ jobs: # Netbsd execution fails to link in cross # - x86_64-unknown-netbsd steps: - - name: Checkout - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Install rust stable - uses: dtolnay/rust-toolchain@stable - - - name: Cleanup Docker - continue-on-error: true - run: | - docker kill $(docker ps -q) - - # See https://github.com/cross-rs/cross/issues/1222 - - uses: taiki-e/install-action@cross - - - name: build - # cross tests are currently broken vor armv7 and aarch64 - # see https://github.com/cross-rs/cross/issues/1311. So on - # those platforms we only build but do not run tests. - run: cross build --all --target ${{ matrix.target }} - env: - RUST_LOG: ${{ runner.debug && 'TRACE' || 'DEBUG'}} + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install rust stable + uses: dtolnay/rust-toolchain@stable + + - name: Cleanup Docker + continue-on-error: true + run: | + docker kill $(docker ps -q) + + # See https://github.com/cross-rs/cross/issues/1222 + - uses: taiki-e/install-action@cross + + - name: build + # cross tests are currently broken vor armv7 and aarch64 + # see https://github.com/cross-rs/cross/issues/1311. So on + # those platforms we only build but do not run tests. + run: cross build --all --target ${{ matrix.target }} + env: + RUST_LOG: ${{ runner.debug && 'TRACE' || 'DEBUG'}} android_build: name: Android Build Only @@ -82,38 +82,38 @@ jobs: - aarch64-linux-android - armv7-linux-androideabi steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set up Rust - uses: dtolnay/rust-toolchain@stable - with: - target: ${{ matrix.target }} - - name: Install rustup target - run: rustup target add ${{ matrix.target }} - - - name: Setup Java - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: '17' - - - name: Setup Android SDK - uses: android-actions/setup-android@v3 - - - name: Setup Android NDK - uses: arqu/setup-ndk@main - id: setup-ndk - with: - ndk-version: r23 - add-to-path: true - - - name: Build - env: - ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} - run: | - cargo install --version 3.5.4 cargo-ndk - cargo ndk --target ${{ matrix.target }} build + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Rust + uses: dtolnay/rust-toolchain@stable + with: + target: ${{ matrix.target }} + - name: Install rustup target + run: rustup target add ${{ matrix.target }} + + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: "temurin" + java-version: "17" + + - name: Setup Android SDK + uses: android-actions/setup-android@v3 + + - name: Setup Android NDK + uses: arqu/setup-ndk@main + id: setup-ndk + with: + ndk-version: r23 + add-to-path: true + + - name: Build + env: + ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} + run: | + cargo install --version 3.5.4 cargo-ndk + cargo ndk --target ${{ matrix.target }} build cross_test: name: Cross Test @@ -126,26 +126,26 @@ jobs: target: - i686-unknown-linux-gnu steps: - - name: Checkout - uses: actions/checkout@v4 - with: - submodules: recursive + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive - - name: Install rust stable - uses: dtolnay/rust-toolchain@stable + - name: Install rust stable + uses: dtolnay/rust-toolchain@stable - - name: Cleanup Docker - continue-on-error: true - run: | - docker kill $(docker ps -q) + - name: Cleanup Docker + continue-on-error: true + run: | + docker kill $(docker ps -q) - # See https://github.com/cross-rs/cross/issues/1222 - - uses: taiki-e/install-action@cross + # See https://github.com/cross-rs/cross/issues/1222 + - uses: taiki-e/install-action@cross - - name: test - run: cross test --all --target ${{ matrix.target }} -- --test-threads=12 - env: - RUST_LOG: ${{ runner.debug && 'TRACE' || 'DEBUG' }} + - name: test + run: cross test --all --target ${{ matrix.target }} -- --test-threads=12 + env: + RUST_LOG: ${{ runner.debug && 'TRACE' || 'DEBUG' }} check_semver: runs-on: ubuntu-latest @@ -186,13 +186,13 @@ jobs: RUSTC_WRAPPER: "sccache" SCCACHE_GHA_ENABLED: "on" steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - with: - components: rustfmt - - uses: mozilla-actions/sccache-action@v0.0.9 - - uses: taiki-e/install-action@cargo-make - - run: cargo make format-check + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + - uses: mozilla-actions/sccache-action@v0.0.9 + - uses: taiki-e/install-action@cargo-make + - run: cargo make format-check check_docs: timeout-minutes: 30 @@ -219,23 +219,23 @@ jobs: RUSTC_WRAPPER: "sccache" SCCACHE_GHA_ENABLED: "on" steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - with: - components: clippy - - name: Install sccache - uses: mozilla-actions/sccache-action@v0.0.9 + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + components: clippy + - name: Install sccache + uses: mozilla-actions/sccache-action@v0.0.9 - # TODO: We have a bunch of platform-dependent code so should - # probably run this job on the full platform matrix - - name: clippy check (all features) - run: cargo clippy --workspace --all-features --all-targets --bins --tests --benches + # TODO: We have a bunch of platform-dependent code so should + # probably run this job on the full platform matrix + - name: clippy check (all features) + run: cargo clippy --workspace --all-features --all-targets --bins --tests --benches - - name: clippy check (no features) - run: cargo clippy --workspace --no-default-features --lib --bins --tests + - name: clippy check (no features) + run: cargo clippy --workspace --no-default-features --lib --bins --tests - - name: clippy check (default features) - run: cargo clippy --workspace --all-targets + - name: clippy check (default features) + run: cargo clippy --workspace --all-targets msrv: if: "github.event_name != 'pull_request' || ! contains(github.event.pull_request.labels.*.name, 'flaky-test')" @@ -246,17 +246,17 @@ jobs: RUSTC_WRAPPER: "sccache" SCCACHE_GHA_ENABLED: "on" steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ env.MSRV }} - - name: Install sccache - uses: mozilla-actions/sccache-action@v0.0.9 - - - name: Check MSRV all features - continue-on-error: true - run: | - cargo +$MSRV check --workspace --all-targets + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.MSRV }} + - name: Install sccache + uses: mozilla-actions/sccache-action@v0.0.9 + + - name: Check MSRV all features + continue-on-error: true + run: | + cargo +$MSRV check --workspace --all-targets cargo_deny: timeout-minutes: 30 @@ -274,6 +274,43 @@ jobs: timeout-minutes: 30 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - run: pip install --user codespell[toml] - - run: codespell --ignore-words-list=ans,atmost,crate,inout,ratatui,ser,stayin,swarmin,worl --skip=CHANGELOG.md + - uses: actions/checkout@v4 + - run: pip install --user codespell[toml] + - run: codespell --ignore-words-list=ans,atmost,crate,inout,ratatui,ser,stayin,swarmin,worl --skip=CHANGELOG.md + + wasm_build: + name: Build & test wasm32 + runs-on: ubuntu-latest + env: + RUSTFLAGS: '--cfg getrandom_backend="wasm_js"' + steps: + - name: Checkout sources + uses: actions/checkout@v4 + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install stable toolchain + uses: dtolnay/rust-toolchain@stable + + - name: Add wasm target + run: rustup target add wasm32-unknown-unknown + + - name: Install wasm-tools + uses: bytecodealliance/actions/wasm-tools/setup@v1 + + - name: Install wasm-pack + uses: taiki-e/install-action@v2 + with: + tool: wasm-bindgen,wasm-pack + + - name: wasm32 build + run: cargo build --target wasm32-unknown-unknown --no-default-features + + # If the Wasm file contains any 'import "env"' declarations, then + # some non-Wasm-compatible code made it into the final code. + - name: Ensure no 'import "env"' in wasm + run: | + ! wasm-tools print --skeleton target/wasm32-unknown-unknown/debug/iroh_docs.wasm | grep 'import "env"' diff --git a/Cargo.lock b/Cargo.lock index 27d190f8..ce121aa0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -106,15 +106,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "arc-swap" -version = "1.8.0" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d03449bb8ca2cc2ef70869af31463d1ae5ccc8fa3e334b307203fbf815207e" +checksum = "f9f3647c145568cec02c42054e07bdf9a5a698e15b466fb2341bfc393cd24aa5" dependencies = [ "rustversion", ] @@ -155,7 +155,7 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "synstructure", ] @@ -167,7 +167,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -203,7 +203,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -388,9 +388,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "blake3" @@ -426,9 +426,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "byteorder" @@ -438,9 +438,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" dependencies = [ "serde", ] @@ -478,9 +478,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.54" +version = "1.2.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ "find-msvc-tools", "shlex", @@ -506,9 +506,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", "js-sys", @@ -520,9 +520,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.55" +version = "4.5.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e34525d5bbbd55da2bb745d34b36121baac88d07619a9a09cfcf4a6c0832785" +checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" dependencies = [ "clap_builder", "clap_derive", @@ -530,9 +530,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.55" +version = "4.5.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a20016a20a3da95bef50ec7238dbd09baeef4311dcdd38ec15aba69812fb61" +checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" dependencies = [ "anstream", "anstyle", @@ -549,14 +549,14 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "clap_lex" -version = "0.7.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" [[package]] name = "cobs" @@ -680,9 +680,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crypto-common" -version = "0.2.0-rc.13" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7722afd27468475c9b6063dc03a57ef2ca833816981619f8ebe64d38d207eef" +checksum = "211f05e03c7d03754740fd9e585de910a095d6b99f8bcfffdef8319fa02a8331" dependencies = [ "hybrid-array", ] @@ -713,7 +713,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -737,7 +737,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -748,7 +748,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -773,9 +773,9 @@ checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "der" -version = "0.8.0-rc.10" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c1d73e9668ea6b6a28172aa55f3ebec38507131ce179051c8033b5c6037653" +checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b" dependencies = [ "const-oid", "pem-rfc7468", @@ -798,9 +798,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.5" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", ] @@ -814,7 +814,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -835,7 +835,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -845,7 +845,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -867,7 +867,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.114", + "syn 2.0.117", "unicode-xid", ] @@ -879,9 +879,9 @@ checksum = "ab03c107fafeb3ee9f5925686dbb7a73bc76e3932abb0d2b365cb64b169cf04c" [[package]] name = "digest" -version = "0.11.0-rc.9" +version = "0.11.0-rc.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff8de092798697546237a3a701e4174fe021579faec9b854379af9bf1e31962" +checksum = "afa94b64bfc6549e6e4b5a3216f22593224174083da7a90db47e951c4fb31725" dependencies = [ "block-buffer", "const-oid", @@ -908,7 +908,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -939,9 +939,9 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "ed25519" -version = "3.0.0-rc.3" +version = "3.0.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d058004dae83c9cf58f3d81612d0296bbf0a52dd7d7b6afa30ab7228bb6119f" +checksum = "c6e914c7c52decb085cea910552e24c63ac019e3ab8bf001ff736da9a9d9d890" dependencies = [ "pkcs8", "serde", @@ -985,7 +985,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -996,7 +996,7 @@ checksum = "3ed8956bd5c1f0415200516e78ff07ec9e16415ade83c056c230d7b7ea0d55b7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1012,7 +1012,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -1062,9 +1062,9 @@ checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" [[package]] name = "find-msvc-tools" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "fixedbitset" @@ -1078,6 +1078,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foldhash" version = "0.2.0" @@ -1095,9 +1101,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", @@ -1110,9 +1116,9 @@ dependencies = [ [[package]] name = "futures-buffered" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8e0e1f38ec07ba4abbde21eed377082f17ccb988be9d988a5adbf4bafc118fd" +checksum = "4421cb78ee172b6b06080093479d3c50f058e7c81b7d577bbb8d118d551d4cd5" dependencies = [ "cordyceps", "diatomic-waker", @@ -1123,9 +1129,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", @@ -1146,15 +1152,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", @@ -1163,9 +1169,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-lite" @@ -1182,32 +1188,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-channel", "futures-core", @@ -1217,7 +1223,6 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -1294,6 +1299,19 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + [[package]] name = "gimli" version = "0.32.3" @@ -1346,6 +1364,15 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + [[package]] name = "hashbrown" version = "0.16.1" @@ -1354,7 +1381,7 @@ checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.2.0", ] [[package]] @@ -1483,9 +1510,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hybrid-array" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b41fb3dc24fe72c2e3a4685eed55917c2fb228851257f4a8f2d985da9443c3e5" +checksum = "e1b229d73f5803b562cc26e4da0396c8610a4ee209f4fac8fa4f8d709166dc45" dependencies = [ "typenum", ] @@ -1532,14 +1559,13 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ "base64", "bytes", "futures-channel", - "futures-core", "futures-util", "http", "http-body", @@ -1548,7 +1574,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.2", + "socket2 0.5.10", "tokio", "tower-service", "tracing", @@ -1659,6 +1685,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "ident_case" version = "1.0.1" @@ -1721,6 +1753,8 @@ checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", "hashbrown 0.16.1", + "serde", + "serde_core", ] [[package]] @@ -1762,9 +1796,9 @@ dependencies = [ [[package]] name = "iroh" -version = "0.96.0" +version = "0.96.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3790cc3a5ef6a89a1e30b64de54de31e692958e2dc8a37cf2831d52c76805de9" +checksum = "5236da4d5681f317ec393c8fe2b7e3d360d31c6bb40383991d0b7429ca5ad117" dependencies = [ "axum", "backon", @@ -1778,17 +1812,17 @@ dependencies = [ "hickory-resolver", "http", "igd-next", - "iroh-base 0.96.0", + "iroh-base 0.96.1", "iroh-metrics", "iroh-quinn", "iroh-quinn-proto", - "iroh-quinn-udp 0.8.0", + "iroh-quinn-udp", "iroh-relay", "n0-error", "n0-future", "n0-watcher", "netdev", - "netwatch 0.14.0", + "netwatch", "papaya", "pin-project", "pkarr", @@ -1834,17 +1868,19 @@ dependencies = [ [[package]] name = "iroh-base" -version = "0.96.0" +version = "0.96.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c3fc0440c8775bf2677a58550fcef7e544346add01bf1b163f9fc0cedd436e" +checksum = "20c99d836a1c99e037e98d1bf3ef209c3a4df97555a00ce9510eb78eccdf5567" dependencies = [ "curve25519-dalek", "data-encoding", "derive_more", + "digest", "ed25519-dalek", "n0-error", "rand_core", "serde", + "sha2", "url", "zeroize", "zeroize_derive", @@ -1867,7 +1903,7 @@ dependencies = [ "genawaiter", "hex", "iroh", - "iroh-base 0.96.0", + "iroh-base 0.96.1", "iroh-io", "iroh-metrics", "iroh-quinn", @@ -1897,6 +1933,7 @@ dependencies = [ "async-channel", "blake3", "bytes", + "cfg_aliases", "data-encoding", "derive_more", "ed25519-dalek", @@ -1955,7 +1992,7 @@ dependencies = [ "hex", "indexmap", "iroh", - "iroh-base 0.96.0", + "iroh-base 0.96.1", "iroh-metrics", "irpc", "n0-error", @@ -2010,23 +2047,23 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "iroh-quinn" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a207ea77da14b683e8d454088795b6ac38e5d5cf26ded6868b9d80392cf8c1" +checksum = "034ed21f34c657a123d39525d948c885aacba59508805e4dd67d71f022e7151b" dependencies = [ "bytes", "cfg_aliases", "iroh-quinn-proto", - "iroh-quinn-udp 0.8.0", + "iroh-quinn-udp", "pin-project-lite", "rustc-hash", "rustls", - "socket2 0.6.2", + "socket2 0.5.10", "thiserror 2.0.18", "tokio", "tokio-stream", @@ -2036,9 +2073,9 @@ dependencies = [ [[package]] name = "iroh-quinn-proto" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f66e567aa8b052c5632b8fc902fbb503891eda705553494ccddf3505257be40" +checksum = "0de99ad8adc878ee0e68509ad256152ce23b8bbe45f5539d04e179630aca40a9" dependencies = [ "bytes", "derive_more", @@ -2061,19 +2098,6 @@ dependencies = [ "web-time", ] -[[package]] -name = "iroh-quinn-udp" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91fe9ec3db6615d7ab1b303717f3b98fc40b96955a4ea25b113b1b879f7481f" -dependencies = [ - "cfg_aliases", - "libc", - "socket2 0.6.2", - "tracing", - "windows-sys 0.61.2", -] - [[package]] name = "iroh-quinn-udp" version = "0.8.0" @@ -2082,16 +2106,16 @@ checksum = "f981dadd5a072a9e0efcd24bdcc388e570073f7e51b33505ceb1ef4668c80c86" dependencies = [ "cfg_aliases", "libc", - "socket2 0.6.2", + "socket2 0.5.10", "tracing", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] name = "iroh-relay" -version = "0.96.0" +version = "0.96.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "236c6f131ce774f7cc7548f467890c313b09f7849b8d703360d6602bc8c5184c" +checksum = "cd2b63e654b9dec799a73372cdc79b529ca6c7248c0c8de7da78a02e3a46f03c" dependencies = [ "ahash", "blake3", @@ -2107,7 +2131,7 @@ dependencies = [ "http-body-util", "hyper", "hyper-util", - "iroh-base 0.96.0", + "iroh-base 0.96.1", "iroh-metrics", "iroh-quinn", "iroh-quinn-proto", @@ -2170,7 +2194,7 @@ checksum = "09cd580bf680db919cbbce6886a47314acb0e9b4f7b639acebcea5e9f485d183" dependencies = [ "data-encoding", "derive_more", - "iroh-base 0.96.0", + "iroh-base 0.96.1", "n0-error", "postcard", "serde", @@ -2206,7 +2230,7 @@ checksum = "58148196d2230183c9679431ac99b57e172000326d664e8456fa2cd27af6505a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2245,9 +2269,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" -version = "0.3.85" +version = "0.3.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +checksum = "f4eacb0641a310445a4c513f2a5e23e19952e269c6a38887254d5f837a305506" dependencies = [ "once_cell", "wasm-bindgen", @@ -2259,11 +2283,17 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libc" -version = "0.2.180" +version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" [[package]] name = "libm" @@ -2279,14 +2309,14 @@ checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ "bitflags", "libc", - "redox_syscall 0.7.0", + "redox_syscall 0.7.1", ] [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" @@ -2366,9 +2396,9 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "mime" @@ -2438,7 +2468,7 @@ checksum = "03755949235714b2b307e5ae89dd8c1c2531fb127d9b8b7b4adf9c876cd3ed18" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2464,9 +2494,9 @@ dependencies = [ [[package]] name = "n0-watcher" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba717c22ceec021ace0ff7674bf8fd60c9394605740a8201678fc1cb3a7398f6" +checksum = "38795f7932e6e9d1c6e989270ef5b3ff24ebb910e2c9d4bed2d28d8bae3007dc" dependencies = [ "derive_more", "n0-error", @@ -2482,7 +2512,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2567,42 +2597,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "netwatch" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "970729c08dbe7987d698f996c6b4945cbfdcdd6ee627df6de51d5469cec13b99" -dependencies = [ - "atomic-waker", - "bytes", - "cfg_aliases", - "derive_more", - "iroh-quinn-udp 0.7.0", - "js-sys", - "libc", - "n0-error", - "n0-future", - "n0-watcher", - "netdev", - "netlink-packet-core", - "netlink-packet-route 0.28.0", - "netlink-proto", - "netlink-sys", - "objc2-core-foundation", - "objc2-system-configuration", - "pin-project-lite", - "serde", - "socket2 0.6.2", - "time", - "tokio", - "tokio-util", - "tracing", - "web-sys", - "windows", - "windows-result", - "wmi", -] - [[package]] name = "netwatch" version = "0.14.0" @@ -2613,7 +2607,7 @@ dependencies = [ "bytes", "cfg_aliases", "derive_more", - "iroh-quinn-udp 0.8.0", + "iroh-quinn-udp", "js-sys", "libc", "n0-error", @@ -2651,9 +2645,9 @@ dependencies = [ [[package]] name = "ntapi" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c70f219e21142367c70c0b30c6a9e3a14d55b4d12a204d897fbec83a0363f081" +checksum = "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae" dependencies = [ "winapi", ] @@ -2679,7 +2673,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -2735,7 +2729,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2937,7 +2931,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2985,9 +2979,9 @@ dependencies = [ [[package]] name = "pkcs8" -version = "0.11.0-rc.10" +version = "0.11.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b226d2cc389763951db8869584fd800cbbe2962bf454e2edeb5172b31ee99774" +checksum = "12922b6296c06eb741b02d7b5161e3aaa22864af38dfa025a1a3ba3f68c84577" dependencies = [ "der", "spki", @@ -3008,15 +3002,15 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portmapper" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29fb522a166045a35b507dea30e3eb69bca1c5a53669d252744d5a0d8474ffa" +checksum = "7d2a8825353ace3285138da3378b1e21860d60351942f7aa3b99b13b41f80318" dependencies = [ "base64", "bytes", @@ -3028,7 +3022,7 @@ dependencies = [ "iroh-metrics", "libc", "n0-error", - "netwatch 0.13.0", + "netwatch", "num_enum", "rand", "serde", @@ -3074,7 +3068,7 @@ checksum = "e0232bd009a197ceec9cc881ba46f727fcd8060a2d8d6a9dde7a69030a6fe2bb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3101,6 +3095,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + [[package]] name = "proc-macro-crate" version = "3.4.0" @@ -3153,9 +3157,9 @@ dependencies = [ [[package]] name = "proptest" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" +checksum = "37566cb3fdacef14c0737f9546df7cfeadbfbc9fef10991038bf5015d0c80532" dependencies = [ "bit-set", "bit-vec", @@ -3198,7 +3202,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.6.2", + "socket2 0.5.10", "thiserror 2.0.18", "tokio", "tracing", @@ -3235,9 +3239,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.2", + "socket2 0.5.10", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -3340,9 +3344,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" +checksum = "35985aa610addc02e24fc232012c86fd11f14111180f902b67e2d5331f8ebf2b" dependencies = [ "bitflags", ] @@ -3364,7 +3368,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3381,9 +3385,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -3392,9 +3396,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" [[package]] name = "reloadable-core" @@ -3506,15 +3510,15 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -3606,7 +3610,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -3646,9 +3650,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "same-file" @@ -3682,9 +3686,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "security-framework" -version = "3.5.1" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ "bitflags", "core-foundation", @@ -3695,9 +3699,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.15.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ "core-foundation-sys", "libc", @@ -3710,7 +3714,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b55fb86dfd3a2f5f76ea78310a88f96c4ea21a3031f8d212443d56123fd0521" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -3781,7 +3785,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3848,9 +3852,9 @@ checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" [[package]] name = "sha2" -version = "0.11.0-rc.4" +version = "0.11.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7535f94fa3339fe9e5e9be6260a909e62af97f6e14b32345ccf79b92b8b81233" +checksum = "d1e3878ab0f98e35b2df35fe53201d088299b41a6bb63e3e34dada2ac4abd924" dependencies = [ "cfg-if", "cpufeatures", @@ -3884,9 +3888,9 @@ dependencies = [ [[package]] name = "signature" -version = "3.0.0-rc.9" +version = "3.0.0-rc.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad0ce3b3f8efd7406f22e2ca5d02be21cdf3b3d1d53ab141f784de8965c7c7e" +checksum = "7f1880df446116126965eeec169136b2e0251dba37c6223bcc819569550edea3" [[package]] name = "simdutf8" @@ -3911,9 +3915,9 @@ checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" [[package]] name = "slab" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" @@ -3958,7 +3962,7 @@ checksum = "c87e960f4dca2788eeb86bbdde8dd246be8948790b7618d656e68f9b720a86e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4007,7 +4011,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta-derive", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4018,7 +4022,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4049,7 +4053,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4061,7 +4065,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4083,9 +4087,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.114" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -4120,7 +4124,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4145,28 +4149,28 @@ checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" [[package]] name = "tempfile" -version = "3.24.0" +version = "3.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.1", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] name = "test-strategy" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c572af0dd6da9b0b5c1c2393c0be69f8e3b96302b20e4444c3fcf627efc31067" +checksum = "8f7fd1eb9efb36942b85a290c4201d317980fe09bc88d34dd48aaaae03075c6a" dependencies = [ "derive-ex", "proc-macro2", "quote", "structmeta", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4215,7 +4219,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4226,7 +4230,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4240,9 +4244,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.46" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9da98b7d9b7dad93488a84b8248efc35352b0b2657397d4167e7ad67e5d535e5" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "itoa", @@ -4264,9 +4268,9 @@ checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "time-macros" -version = "0.2.26" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cc610bac2dcee56805c99642447d4c5dbde4d01f752ffea0199aee1f601dc4" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" dependencies = [ "num-conv", "time-core", @@ -4321,7 +4325,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4412,9 +4416,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.11+spec-1.1.0" +version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ "indexmap", "serde_core", @@ -4448,9 +4452,9 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.6+spec-1.1.0" +version = "1.0.9+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" dependencies = [ "winnow", ] @@ -4527,7 +4531,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4571,9 +4575,9 @@ dependencies = [ [[package]] name = "tracing-test" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "557b891436fe0d5e0e363427fc7f217abf9ccd510d5136549847bdcbcd011d68" +checksum = "19a4c448db514d4f24c5ddb9f73f2ee71bfb24c526cf0c570ba142d1119e0051" dependencies = [ "tracing-core", "tracing-subscriber", @@ -4582,12 +4586,12 @@ dependencies = [ [[package]] name = "tracing-test-macro" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568" +checksum = "ad06847b7afb65c7866a36664b75c40b895e318cea4f71299f013fb22965329d" dependencies = [ "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4610,9 +4614,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-segmentation" @@ -4659,11 +4663,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.20.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f" +checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.1", "js-sys", "wasm-bindgen", ] @@ -4771,6 +4775,15 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasite" version = "0.1.0" @@ -4779,9 +4792,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.108" +version = "0.2.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +checksum = "05d7d0fce354c88b7982aec4400b3e7fcf723c32737cef571bd165f7613557ee" dependencies = [ "cfg-if", "once_cell", @@ -4792,9 +4805,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.58" +version = "0.4.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" +checksum = "ee85afca410ac4abba5b584b12e77ea225db6ee5471d0aebaae0861166f9378a" dependencies = [ "cfg-if", "futures-util", @@ -4806,9 +4819,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.108" +version = "0.2.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +checksum = "55839b71ba921e4f75b674cb16f843f4b1f3b26ddfcb3454de1cf65cc021ec0f" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4816,26 +4829,48 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.108" +version = "0.2.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +checksum = "caf2e969c2d60ff52e7e98b7392ff1588bffdd1ccd4769eba27222fd3d621571" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.108" +version = "0.2.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +checksum = "0861f0dcdf46ea819407495634953cdcc8a8c7215ab799a7a7ce366be71c7b30" dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + [[package]] name = "wasm-streams" version = "0.4.2" @@ -4849,11 +4884,23 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + [[package]] name = "web-sys" -version = "0.3.85" +version = "0.3.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +checksum = "10053fbf9a374174094915bbce141e87a6bf32ecd9a002980db4b638405e8962" dependencies = [ "js-sys", "wasm-bindgen", @@ -4866,23 +4913,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ "js-sys", + "serde", "wasm-bindgen", ] [[package]] name = "webpki-root-certs" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a29fc0408b113f68cf32637857ab740edfafdf460c326cd2afaa2d84cc05dc" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" dependencies = [ "rustls-pki-types", ] [[package]] name = "webpki-roots" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" dependencies = [ "rustls-pki-types", ] @@ -4926,7 +4974,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] @@ -4988,7 +5036,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4999,7 +5047,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -5366,12 +5414,94 @@ name = "wit-bindgen" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] name = "wmi" -version = "0.18.0" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d1d435f7745ba9ed55c43049d47b5fbd1104449beaa2afbc80a1e10a4a018" +checksum = "003e65f4934cf9449b9ce913ad822cd054a5af669d24f93db101fdb02856bb23" dependencies = [ "chrono", "futures", @@ -5409,9 +5539,9 @@ dependencies = [ [[package]] name = "x509-parser" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3e137310115a65136898d2079f003ce33331a6c4b0d51f1531d1be082b6425" +checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202" dependencies = [ "asn1-rs", "data-encoding", @@ -5468,7 +5598,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "synstructure", ] @@ -5480,22 +5610,22 @@ checksum = "2164e798d9e3d84ee2c91139ace54638059a3b23e361f5c11781c2c6459bde0f" [[package]] name = "zerocopy" -version = "0.8.35" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdea86ddd5568519879b8187e1cf04e24fce28f7fe046ceecbce472ff19a2572" +checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.35" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c15e1b46eff7c6c91195752e0eeed8ef040e391cdece7c25376957d5f15df22" +checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -5515,7 +5645,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "synstructure", ] @@ -5536,7 +5666,7 @@ checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -5569,11 +5699,11 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "zmij" -version = "1.0.17" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02aae0f83f69aafc94776e879363e9771d7ecbffe2c7fbb6c14c5e00dfe88439" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index 10b15c17..6d48a530 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,20 +31,21 @@ futures-buffered = "0.2.4" futures-lite = "2.3.0" futures-util = { version = "0.3.25" } hex = "0.4" -iroh = { version = "0.96" } -iroh-tickets = { version = "0.2"} -iroh-blobs = { version = "0.98" } -iroh-gossip = { version = "0.96", features = ["net"] } +iroh = { version = "0.96.1", default-features = false } +iroh-tickets = { version = "0.2" } +iroh-blobs = { version = "0.98", default-features = false } +iroh-gossip = { version = "0.96", features = ["net"], default-features = false } iroh-metrics = { version = "0.38", default-features = false } -irpc = { version = "0.12.0" } -n0-future = "0.3" +irpc = { version = "0.12.0", default-features = false } +n0-error = "0.1.0" +n0-future = { version = "0.3.1", features = ["serde"] } num_enum = "0.7" postcard = { version = "1", default-features = false, features = [ "alloc", "use-std", "experimental-derive", ] } -quinn = { package = "iroh-quinn", version = "0.16.0" } +quinn = { package = "iroh-quinn", version = "0.16.0", optional = true } rand = "0.9.2" redb = { version = "2.6.3" } self_cell = "1.0.3" @@ -57,11 +58,10 @@ tokio = { version = "1", features = ["sync", "rt", "time", "io-util"] } tokio-stream = { version = "0.1", features = ["sync"] } tokio-util = { version = "0.7.12", features = ["codec", "io-util", "io", "rt"] } tracing = "0.1" -n0-error = "0.1.0" [dev-dependencies] data-encoding = "2.6.0" -iroh = { version = "0.96", features = ["test-utils"] } +iroh = { version = "0.96.1", features = ["test-utils"] } nested_enum_utils = "0.2" parking_lot = "0.12.3" proptest = "1.2.0" @@ -71,12 +71,14 @@ test-strategy = "0.4" testdir = "0.7" testresult = "0.4.1" tokio = { version = "1", features = ["sync", "macros"] } -tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } tracing-test = "0.2.5" +tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } [features] -default = ["metrics"] +default = ["metrics", "rpc", "fs-store"] metrics = ["iroh-metrics/metrics", "iroh/metrics"] +rpc = ["dep:quinn", "irpc/rpc", "iroh-blobs/rpc"] +fs-store = ["iroh-blobs/fs-store"] [package.metadata.docs.rs] all-features = true @@ -94,3 +96,6 @@ missing_debug_implementations = "warn" # do. To enable for a crate set `#![cfg_attr(iroh_docsrs, # feature(doc_cfg))]` in the crate. unexpected_cfgs = { level = "warn", check-cfg = ["cfg(iroh_docsrs)"] } + +[build-dependencies] +cfg_aliases = "0.2.1" diff --git a/build.rs b/build.rs new file mode 100644 index 00000000..7aae5682 --- /dev/null +++ b/build.rs @@ -0,0 +1,9 @@ +use cfg_aliases::cfg_aliases; + +fn main() { + // Setup cfg aliases + cfg_aliases! { + // Convenience aliases + wasm_browser: { all(target_family = "wasm", target_os = "unknown") }, + } +} diff --git a/src/actor.rs b/src/actor.rs index 7b9f9241..9969832c 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -4,8 +4,6 @@ use std::{ collections::{hash_map, HashMap}, num::NonZeroU64, sync::Arc, - thread::JoinHandle, - time::Duration, }; use anyhow::{anyhow, Context, Result}; @@ -13,8 +11,11 @@ use bytes::Bytes; use futures_util::FutureExt; use iroh_blobs::Hash; use irpc::channel::mpsc; +use n0_future::{task::JoinSet, time::Duration}; use serde::{Deserialize, Serialize}; -use tokio::{sync::oneshot, task::JoinSet}; +use tokio::sync::oneshot; +#[cfg(wasm_browser)] +use tracing::Instrument; use tracing::{debug, error, error_span, trace, warn}; use crate::{ @@ -226,7 +227,11 @@ struct OpenReplica { #[derive(Debug, Clone)] pub struct SyncHandle { tx: async_channel::Sender, - join_handle: Arc>>, + #[cfg(wasm_browser)] + #[allow(unused)] + join_handle: Arc>>, + #[cfg(not(wasm_browser))] + join_handle: Arc>>, metrics: Arc, } @@ -270,17 +275,23 @@ impl SyncHandle { tasks: Default::default(), metrics: metrics.clone(), }; + + let span = error_span!("sync", %me); + #[cfg(wasm_browser)] + let join_handle = n0_future::task::spawn(actor.run_async().instrument(span)); + + #[cfg(not(wasm_browser))] let join_handle = std::thread::Builder::new() .name("sync-actor".to_string()) .spawn(move || { - let span = error_span!("sync", %me); let _enter = span.enter(); - if let Err(err) = actor.run() { + if let Err(err) = actor.run_in_thread() { error!("Sync actor closed with error: {err:?}"); } }) .expect("failed to spawn thread"); + let join_handle = Arc::new(Some(join_handle)); SyncHandle { tx: action_tx, @@ -594,14 +605,26 @@ impl SyncHandle { impl Drop for SyncHandle { fn drop(&mut self) { // this means we're dropping the last reference + #[allow(unused)] if let Some(handle) = Arc::get_mut(&mut self.join_handle) { - // this call is the reason tx can not be a tokio mpsc channel. - // we have no control about where drop is called, yet tokio send_blocking panics - // when called from inside a tokio runtime. - self.tx.send_blocking(Action::Shutdown { reply: None }).ok(); - let handle = handle.take().expect("this can only run once"); - if let Err(err) = handle.join() { - warn!(?err, "Failed to join sync actor"); + #[cfg(wasm_browser)] + { + let tx = self.tx.clone(); + n0_future::task::spawn(async move { + tx.send(Action::Shutdown { reply: None }).await.ok(); + }); + } + #[cfg(not(wasm_browser))] + { + // this call is the reason tx can not be a tokio mpsc channel. + // we have no control about where drop is called, yet tokio send_blocking panics + // when called from inside a tokio runtime. + self.tx.send_blocking(Action::Shutdown { reply: None }).ok(); + let handle = handle.take().expect("this can only run once"); + + if let Err(err) = handle.join() { + warn!(?err, "Failed to join sync actor"); + } } } } @@ -617,7 +640,8 @@ struct Actor { } impl Actor { - fn run(self) -> Result<()> { + #[cfg(not(wasm_browser))] + fn run_in_thread(self) -> Result<()> { let rt = tokio::runtime::Builder::new_current_thread() .enable_time() .build()?; @@ -628,7 +652,7 @@ impl Actor { async fn run_async(mut self) { let reply = loop { - let timeout = tokio::time::sleep(MAX_COMMIT_DELAY); + let timeout = n0_future::time::sleep(MAX_COMMIT_DELAY); tokio::pin!(timeout); let action = tokio::select! { _ = &mut timeout => { @@ -764,37 +788,46 @@ impl Actor { hash, len, reply, - } => send_reply_with(reply, self, move |this| { - let author = get_author(&mut this.store, &author)?; - let mut replica = this.states.replica(namespace, &mut this.store)?; - replica.insert(&key, &author, hash, len)?; - this.metrics.new_entries_local.inc(); - this.metrics.new_entries_local_size.inc_by(len); - Ok(()) - }), + } => { + send_reply_with_async(reply, self, async move |this| { + let author = get_author(&mut this.store, &author)?; + let mut replica = this.states.replica(namespace, &mut this.store)?; + replica.insert(&key, &author, hash, len).await?; + this.metrics.new_entries_local.inc(); + this.metrics.new_entries_local_size.inc_by(len); + Ok(()) + }) + .await + } ReplicaAction::DeletePrefix { author, key, reply } => { - send_reply_with(reply, self, |this| { + send_reply_with_async(reply, self, async |this| { let author = get_author(&mut this.store, &author)?; let mut replica = this.states.replica(namespace, &mut this.store)?; - let res = replica.delete_prefix(&key, &author)?; + let res = replica.delete_prefix(&key, &author).await?; Ok(res) }) + .await } ReplicaAction::InsertRemote { entry, from, content_status, reply, - } => send_reply_with(reply, self, move |this| { - let mut replica = this - .states - .replica_if_syncing(&namespace, &mut this.store)?; - let len = entry.content_len(); - replica.insert_remote_entry(entry, from, content_status)?; - this.metrics.new_entries_remote.inc(); - this.metrics.new_entries_remote_size.inc_by(len); - Ok(()) - }), + } => { + send_reply_with_async(reply, self, async move |this| { + let mut replica = this + .states + .replica_if_syncing(&namespace, &mut this.store)?; + let len = entry.content_len(); + replica + .insert_remote_entry(entry, from, content_status) + .await?; + this.metrics.new_entries_remote.inc(); + this.metrics.new_entries_remote_size.inc_by(len); + Ok(()) + }) + .await + } ReplicaAction::SyncInitialMessage { reply } => { send_reply_with(reply, self, move |this| { @@ -1049,6 +1082,14 @@ fn send_reply_with( sender.send(f(this)).map_err(send_reply_error) } +async fn send_reply_with_async( + sender: oneshot::Sender>, + this: &mut Actor, + f: impl AsyncFnOnce(&mut Actor) -> Result, +) -> Result<(), SendReplyError> { + sender.send(f(this).await).map_err(send_reply_error) +} + fn send_reply_error(_err: T) -> SendReplyError { SendReplyError } diff --git a/src/api.rs b/src/api.rs index fc5e9c07..b0b3ac45 100644 --- a/src/api.rs +++ b/src/api.rs @@ -4,7 +4,6 @@ use std::{ future::Future, - net::SocketAddr, path::Path, pin::Pin, sync::{ @@ -14,18 +13,14 @@ use std::{ task::{ready, Poll}, }; -use anyhow::{Context, Result}; +use anyhow::Result; use bytes::Bytes; use iroh::EndpointAddr; use iroh_blobs::{ api::blobs::{AddPathOptions, AddProgressItem, ExportMode, ExportOptions, ExportProgress}, Hash, }; -use irpc::rpc::Handler; -use n0_future::{ - task::{self, AbortOnDropHandle}, - FutureExt, Stream, StreamExt, -}; +use n0_future::{FutureExt, Stream, StreamExt}; use self::{ actor::RpcActor, @@ -67,19 +62,25 @@ impl DocsApi { } /// Connect to a remote docs service - pub fn connect(endpoint: quinn::Endpoint, addr: SocketAddr) -> Result { + #[cfg(feature = "rpc")] + pub fn connect(endpoint: quinn::Endpoint, addr: std::net::SocketAddr) -> Result { Ok(DocsApi { inner: Client::quinn(endpoint, addr), }) } /// Listen for incoming RPC connections - pub fn listen(&self, endpoint: quinn::Endpoint) -> Result> { + #[cfg(feature = "rpc")] + pub fn listen( + &self, + endpoint: quinn::Endpoint, + ) -> Result> { + use anyhow::Context; let local = self .inner .as_local() .context("cannot listen on remote API")?; - let handler: Handler = Arc::new(move |msg, _rx, tx| { + let handler: irpc::rpc::Handler = Arc::new(move |msg, _rx, tx| { let local = local.clone(); Box::pin(async move { match msg { @@ -114,8 +115,8 @@ impl DocsApi { } }) }); - let join_handle = task::spawn(irpc::rpc::listen(endpoint, handler)); - Ok(AbortOnDropHandle::new(join_handle)) + let join_handle = n0_future::task::spawn(irpc::rpc::listen(endpoint, handler)); + Ok(n0_future::task::AbortOnDropHandle::new(join_handle)) } /// Creates a new document author. diff --git a/src/api/actor.rs b/src/api/actor.rs index 5a8fce4b..9d365c10 100644 --- a/src/api/actor.rs +++ b/src/api/actor.rs @@ -429,7 +429,7 @@ impl RpcActor { return; } }; - tokio::task::spawn(async move { + n0_future::task::spawn(async move { loop { tokio::select! { msg = stream.next() => { diff --git a/src/api/protocol.rs b/src/api/protocol.rs index 1d991048..34e1a40b 100644 --- a/src/api/protocol.rs +++ b/src/api/protocol.rs @@ -304,7 +304,7 @@ pub struct AuthorDeleteResponse; // Use the macro to generate both the DocsProtocol and DocsMessage enums // plus implement Channels for each type -#[rpc_requests(message = DocsMessage)] +#[rpc_requests(message = DocsMessage, rpc_feature = "rpc")] #[derive(Serialize, Deserialize, Debug)] pub enum DocsProtocol { #[rpc(tx = oneshot::Sender>)] diff --git a/src/engine.rs b/src/engine.rs index 22831d41..3a3833da 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2,13 +2,9 @@ //! //! [`crate::Replica`] is also called documents here. -use std::{ - path::PathBuf, - str::FromStr, - sync::{Arc, RwLock}, -}; +use std::sync::{Arc, RwLock}; -use anyhow::{bail, Context, Result}; +use anyhow::{bail, Result}; use futures_lite::{Stream, StreamExt}; use iroh::{Endpoint, EndpointAddr, PublicKey}; use iroh_blobs::{ @@ -17,9 +13,9 @@ use iroh_blobs::{ Hash, }; use iroh_gossip::net::Gossip; +use n0_future::task::AbortOnDropHandle; use serde::{Deserialize, Serialize}; use tokio::sync::{mpsc, oneshot}; -use tokio_util::task::AbortOnDropHandle; use tracing::{debug, error, error_span, Instrument}; use self::live::{LiveActor, ToLiveActor}; @@ -128,7 +124,7 @@ impl Engine { live_actor_tx.clone(), sync.metrics().clone(), ); - let actor_handle = tokio::task::spawn( + let actor_handle = n0_future::task::spawn( async move { if let Err(err) = actor.run().await { error!("sync actor failed: {err:?}"); @@ -355,7 +351,8 @@ pub enum DefaultAuthorStorage { /// Memory storage. Mem, /// File based persistent storage. - Persistent(PathBuf), + #[cfg(feature = "fs-store")] + Persistent(std::path::PathBuf), } impl DefaultAuthorStorage { @@ -373,7 +370,11 @@ impl DefaultAuthorStorage { docs_store.import_author(author).await?; Ok(author_id) } + #[cfg(feature = "fs-store")] Self::Persistent(ref path) => { + use std::str::FromStr; + + use anyhow::Context; if path.exists() { let data = tokio::fs::read_to_string(path).await.with_context(|| { format!( @@ -407,12 +408,14 @@ impl DefaultAuthorStorage { } /// Save a new default author. - pub async fn persist(&self, author_id: AuthorId) -> anyhow::Result<()> { + pub async fn persist(&self, #[allow(unused)] author_id: AuthorId) -> anyhow::Result<()> { match self { Self::Mem => { // persistence is not possible for the mem storage so this is a noop. } + #[cfg(feature = "fs-store")] Self::Persistent(ref path) => { + use anyhow::Context; tokio::fs::write(path, author_id.to_string()) .await .with_context(|| { diff --git a/src/engine/gossip.rs b/src/engine/gossip.rs index bb892287..bfaf0324 100644 --- a/src/engine/gossip.rs +++ b/src/engine/gossip.rs @@ -9,10 +9,8 @@ use iroh_gossip::{ api::{Event, GossipReceiver, GossipSender, JoinOptions}, net::Gossip, }; -use tokio::{ - sync::mpsc, - task::{AbortHandle, JoinSet}, -}; +use n0_future::task::{AbortHandle, JoinSet}; +use tokio::sync::mpsc; use tracing::{debug, instrument, warn}; use super::live::{Op, ToLiveActor}; diff --git a/src/engine/live.rs b/src/engine/live.rs index 17ab2212..c4a4806a 100644 --- a/src/engine/live.rs +++ b/src/engine/live.rs @@ -3,7 +3,6 @@ use std::{ collections::{HashMap, HashSet}, sync::Arc, - time::SystemTime, }; use anyhow::{Context, Result}; @@ -18,11 +17,9 @@ use iroh_blobs::{ Hash, HashAndFormat, }; use iroh_gossip::net::Gossip; +use n0_future::{task::JoinSet, time::SystemTime}; use serde::{Deserialize, Serialize}; -use tokio::{ - sync::{self, mpsc, oneshot}, - task::JoinSet, -}; +use tokio::sync::{self, mpsc, oneshot}; use tracing::{debug, error, info, instrument, trace, warn, Instrument, Span}; // use super::gossip::{GossipActor, ToGossipActor}; diff --git a/src/engine/state.rs b/src/engine/state.rs index c210610c..6e040d26 100644 --- a/src/engine/state.rs +++ b/src/engine/state.rs @@ -1,10 +1,8 @@ -use std::{ - collections::BTreeMap, - time::{Instant, SystemTime}, -}; +use std::collections::BTreeMap; use anyhow::Result; use iroh::EndpointId; +use n0_future::time::{Instant, SystemTime}; use serde::{Deserialize, Serialize}; use tracing::{debug, warn}; diff --git a/src/net.rs b/src/net.rs index 9c4757f7..771284e0 100644 --- a/src/net.rs +++ b/src/net.rs @@ -1,11 +1,9 @@ //! Network implementation of the iroh-docs protocol -use std::{ - future::Future, - time::{Duration, Instant}, -}; +use std::future::Future; use iroh::{Endpoint, EndpointAddr, PublicKey}; +use n0_future::time::{Duration, Instant}; use serde::{Deserialize, Serialize}; use tracing::{debug, error_span, trace, Instrument}; diff --git a/src/net/codec.rs b/src/net/codec.rs index 81dbc0ef..c2ed08b5 100644 --- a/src/net/codec.rs +++ b/src/net/codec.rs @@ -322,6 +322,7 @@ mod tests { let alice_replica_id = alice_replica.id(); alice_replica .hash_and_insert("hello bob", &author, "from alice") + .await .unwrap(); let mut bob_store = store::Store::memory(); @@ -329,6 +330,7 @@ mod tests { let bob_replica_id = bob_replica.id(); bob_replica .hash_and_insert("hello alice", &author, "from bob") + .await .unwrap(); assert_eq!( @@ -429,6 +431,7 @@ mod tests { #[tokio::test] #[traced_test] + #[cfg(feature = "fs-store")] async fn test_sync_many_authors_fs() -> Result<()> { let tmpdir = tempfile::tempdir()?; let alice_store = store::fs::Store::persistent(tmpdir.path().join("a.db"))?; @@ -438,9 +441,9 @@ mod tests { type Message = (AuthorId, Vec, Hash); - fn insert_messages( + async fn insert_messages( mut rng: impl CryptoRng, - replica: &mut crate::sync::Replica, + replica: &mut crate::sync::Replica<'_>, num_authors: usize, msgs_per_author: usize, key_value_fn: impl Fn(&AuthorId, usize) -> (String, String), @@ -453,7 +456,10 @@ mod tests { for i in 0..msgs_per_author { for author in authors.iter() { let (key, value) = key_value_fn(&author.id(), i); - let hash = replica.hash_and_insert(key.clone(), author, value).unwrap(); + let hash = replica + .hash_and_insert(key.clone(), author, value) + .await + .unwrap(); res.push((author.id(), key.as_bytes().to_vec(), hash)); } } @@ -509,7 +515,8 @@ mod tests { format!("from alice by {author}: {i}"), ) }, - ); + ) + .await; all_messages.extend_from_slice(&alice_messages); let mut bob_replica = bob_store.new_replica(namespace.clone()).unwrap(); @@ -524,7 +531,8 @@ mod tests { format!("from bob by {author}: {i}"), ) }, - ); + ) + .await; all_messages.extend_from_slice(&bob_messages); all_messages.sort(); @@ -622,6 +630,7 @@ mod tests { #[tokio::test] #[traced_test] + #[cfg(feature = "fs-store")] async fn test_sync_timestamps_fs() -> Result<()> { let tmpdir = tempfile::tempdir()?; let alice_store = store::fs::Store::persistent(tmpdir.path().join("a.db"))?; @@ -646,10 +655,12 @@ mod tests { // Insert into alice let hash_alice = alice_replica .hash_and_insert(&key, &author, &value_alice) + .await .unwrap(); // Insert into bob let hash_bob = bob_replica .hash_and_insert(&key, &author, &value_bob) + .await .unwrap(); assert_eq!( diff --git a/src/protocol.rs b/src/protocol.rs index e07568ea..08ebb8bf 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -1,6 +1,6 @@ //! [`ProtocolHandler`] implementation for the docs [`Engine`]. -use std::{path::PathBuf, sync::Arc}; +use std::sync::Arc; use anyhow::Result; use iroh::{endpoint::Connection, protocol::ProtocolHandler, Endpoint}; @@ -13,6 +13,14 @@ use crate::{ store::Store, }; +#[derive(Default, Debug)] +enum Storage { + #[default] + Memory, + #[cfg(feature = "fs-store")] + Persistent(std::path::PathBuf), +} + /// Docs protocol. #[derive(Debug, Clone)] pub struct Docs { @@ -28,9 +36,10 @@ impl Docs { /// Create a new [`Builder`] for the docs protocol, using a persistent replica and author storage /// in the given directory. - pub fn persistent(path: PathBuf) -> Builder { + #[cfg(feature = "fs-store")] + pub fn persistent(path: std::path::PathBuf) -> Builder { Builder { - path: Some(path), + storage: Storage::Persistent(path), protect_cb: None, } } @@ -75,7 +84,7 @@ impl ProtocolHandler for Docs { /// Builder for the docs protocol. #[derive(Debug, Default)] pub struct Builder { - path: Option, + storage: Storage, protect_cb: Option, } @@ -95,13 +104,17 @@ impl Builder { blobs: BlobsStore, gossip: Gossip, ) -> anyhow::Result { - let replica_store = match self.path { - Some(ref path) => Store::persistent(path.join("docs.redb"))?, - None => Store::memory(), + let replica_store = match &self.storage { + Storage::Memory => Store::memory(), + #[cfg(feature = "fs-store")] + Storage::Persistent(path) => Store::persistent(path.join("docs.redb"))?, }; - let author_store = match self.path { - Some(ref path) => DefaultAuthorStorage::Persistent(path.join("default-author")), - None => DefaultAuthorStorage::Mem, + let author_store = match &self.storage { + Storage::Memory => DefaultAuthorStorage::Mem, + #[cfg(feature = "fs-store")] + Storage::Persistent(path) => { + DefaultAuthorStorage::Persistent(path.join("default-author")) + } }; let downloader = blobs.downloader(&endpoint); let engine = Engine::spawn( diff --git a/src/ranger.rs b/src/ranger.rs index c520eca1..6d5ef87c 100644 --- a/src/ranger.rs +++ b/src/ranger.rs @@ -1,7 +1,7 @@ //! Implementation of Set Reconcilliation based on //! "Range-Based Set Reconciliation" by Aljoscha Meyer. -use std::{fmt::Debug, pin::Pin}; +use std::fmt::Debug; use n0_future::StreamExt; use serde::{Deserialize, Serialize}; @@ -288,6 +288,7 @@ pub trait Store: Sized { /// /// This will remove just the entry with the given key, but will not perform prefix deletion. #[cfg(test)] + #[allow(unused)] fn entry_remove(&mut self, key: &E::Key) -> Result, Self::Error>; /// Remove all entries whose key start with a prefix and for which the `predicate` callback @@ -330,11 +331,8 @@ pub trait Store: Sized { ) -> Result>, Self::Error> where F: Fn(&Self, &E, ContentStatus) -> bool, - F2: FnMut(&Self, E, ContentStatus), - F3: for<'a> Fn( - &'a E, - ) - -> Pin + Send + 'a>>, + F2: AsyncFnMut(&Self, E, ContentStatus), + F3: for<'a> AsyncFn(&'a E) -> ContentStatus, { let mut out = Vec::new(); @@ -397,7 +395,7 @@ pub trait Store: Sized { // TODO: Get rid of the clone? let outcome = self.put(entry.clone())?; if let InsertOutcome::Inserted { .. } = outcome { - on_insert_cb(self, entry, content_status); + on_insert_cb(self, entry, content_status).await; } } } @@ -1419,8 +1417,8 @@ mod tests { &Default::default(), msg, &bob_validate_cb, - |_, _, _| (), - |_| Box::pin(async move { ContentStatus::Complete }), + async |_, _, _| (), + async |_| ContentStatus::Complete, ) .await .unwrap() @@ -1431,8 +1429,8 @@ mod tests { &Default::default(), msg, &alice_validate_cb, - |_, _, _| (), - |_| Box::pin(async move { ContentStatus::Complete }), + async |_, _, _| (), + async |_| ContentStatus::Complete, ) .await .unwrap(); diff --git a/src/store/fs.rs b/src/store/fs.rs index 6a0f6c54..e05223df 100644 --- a/src/store/fs.rs +++ b/src/store/fs.rs @@ -6,15 +6,15 @@ use std::{ iter::{Chain, Flatten}, num::NonZeroU64, ops::Bound, - path::Path, }; use anyhow::{anyhow, Result}; use ed25519_dalek::{SignatureError, VerifyingKey}; use iroh_blobs::Hash; +use n0_future::time::SystemTime; use rand::CryptoRng; -use redb::{Database, DatabaseError, ReadableMultimapTable, ReadableTable}; -use tracing::{info, warn}; +use redb::{Database, ReadableMultimapTable, ReadableTable}; +use tracing::warn; use super::{ pubkeys::MemPublicKeyStore, DownloadPolicy, ImportNamespaceOutcome, OpenError, PublicKeyStore, @@ -98,16 +98,17 @@ impl Store { /// Create or open a store from a `path` to a database file. /// /// The file will be created if it does not exist, otherwise it will be opened. - pub fn persistent(path: impl AsRef) -> Result { + #[cfg(feature = "fs-store")] + pub fn persistent(path: impl AsRef) -> Result { let mut db = match Database::create(&path) { Ok(db) => db, - Err(DatabaseError::UpgradeRequired(1)) => return Err( + Err(redb::DatabaseError::UpgradeRequired(1)) => return Err( anyhow!("Opening the database failed: Upgrading from old format is no longer supported. Use iroh-docs 0.92 to perform the upgrade, then upgrade to the latest release again.") ), Err(err) => return Err(err.into()), }; match db.upgrade() { - Ok(true) => info!("Database was upgraded to redb v3 compatible format"), + Ok(true) => tracing::info!("Database was upgraded to redb v3 compatible format"), Ok(false) => {} Err(err) => warn!("Database upgrade to redb v3 compatible format failed: {err:#}"), } @@ -487,7 +488,7 @@ impl Store { let peer = &peer; let namespace = namespace.as_bytes(); // calculate nanos since UNIX_EPOCH for a time measurement - let nanos = std::time::UNIX_EPOCH + let nanos = SystemTime::UNIX_EPOCH .elapsed() .map(|duration| duration.as_nanos() as u64)?; self.modify(|tables| { @@ -984,13 +985,13 @@ fn into_entry(key: RecordsId, value: RecordsValue) -> SignedEntry { SignedEntry::new(entry_signature, entry) } -#[cfg(test)] +#[cfg(all(test, feature = "fs-store"))] mod tests { - use super::{tables::LATEST_PER_AUTHOR_TABLE, *}; + use super::*; use crate::ranger::Store as _; - #[test] - fn test_ranges() -> Result<()> { + #[tokio::test] + async fn test_ranges() -> Result<()> { let dbfile = tempfile::NamedTempFile::new()?; let mut store = Store::persistent(dbfile.path())?; @@ -1001,8 +1002,8 @@ mod tests { // test author prefix relation for all-255 keys let key1 = vec![255, 255]; let key2 = vec![255, 255, 255]; - replica.hash_and_insert(&key1, &author, b"v1")?; - replica.hash_and_insert(&key2, &author, b"v2")?; + replica.hash_and_insert(&key1, &author, b"v1").await?; + replica.hash_and_insert(&key2, &author, b"v2").await?; let res = store .get_many(namespace.id(), Query::author(author.id()).key_prefix([255]))? .collect::>>()?; @@ -1094,7 +1095,7 @@ mod tests { } fn copy_and_modify( - source: &Path, + source: &std::path::Path, modify: impl Fn(&redb::WriteTransaction) -> Result<()>, ) -> Result { let dbfile = tempfile::NamedTempFile::new()?; @@ -1107,8 +1108,10 @@ mod tests { Ok(dbfile) } - #[test] - fn test_migration_001_populate_latest_table() -> Result<()> { + #[tokio::test] + #[cfg(feature = "fs-store")] + async fn test_migration_001_populate_latest_table() -> Result<()> { + use super::tables::LATEST_PER_AUTHOR_TABLE; let dbfile = tempfile::NamedTempFile::new()?; let namespace = NamespaceSecret::new(&mut rand::rng()); @@ -1118,9 +1121,9 @@ mod tests { let author1 = store.new_author(&mut rand::rng())?; let author2 = store.new_author(&mut rand::rng())?; let mut replica = store.new_replica(namespace.clone())?; - replica.hash_and_insert(b"k1", &author1, b"v1")?; - replica.hash_and_insert(b"k2", &author2, b"v1")?; - replica.hash_and_insert(b"k3", &author1, b"v1")?; + replica.hash_and_insert(b"k1", &author1, b"v1").await?; + replica.hash_and_insert(b"k2", &author2, b"v1").await?; + replica.hash_and_insert(b"k3", &author1, b"v1").await?; let expected = store .get_latest_for_each_author(namespace.id())? @@ -1151,6 +1154,7 @@ mod tests { } #[test] + #[cfg(feature = "fs-store")] fn test_migration_004_populate_by_key_index() -> Result<()> { use redb::ReadableTableMetadata; let dbfile = tempfile::NamedTempFile::new()?; diff --git a/src/store/fs/tables.rs b/src/store/fs/tables.rs index d683d038..34744162 100644 --- a/src/store/fs/tables.rs +++ b/src/store/fs/tables.rs @@ -1,9 +1,8 @@ #![allow(missing_docs)] // Table Definitions -use std::time::Instant; - use bytes::Bytes; +use n0_future::time::Instant; use redb::{ MultimapTable, MultimapTableDefinition, ReadOnlyMultimapTable, ReadOnlyTable, ReadTransaction, Table, TableDefinition, WriteTransaction, @@ -61,7 +60,7 @@ pub type RecordsByKeyIdOwned = ([u8; 32], Bytes, [u8; 32]); /// Value: `(u64, [u8; 32])` # ([`Nanos`], &[`PeerIdBytes`]) representing the last time a peer was used. pub const NAMESPACE_PEERS_TABLE: MultimapTableDefinition<&[u8; 32], (Nanos, &PeerIdBytes)> = MultimapTableDefinition::new("sync-peers-1"); -/// Number of seconds elapsed since [`std::time::SystemTime::UNIX_EPOCH`]. Used to register the +/// Number of seconds elapsed since [`n0_future::time::SystemTime::UNIX_EPOCH`]. Used to register the /// last time a peer was useful in a document. // NOTE: resolution is nanoseconds, stored as a u64 since this covers ~500years from unix epoch, // which should be more than enough diff --git a/src/sync.rs b/src/sync.rs index ddb140b6..87d1c431 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -11,12 +11,15 @@ use std::{ fmt::Debug, ops::{Deref, DerefMut}, sync::Arc, - time::{Duration, SystemTime}, }; use bytes::{Bytes, BytesMut}; use ed25519_dalek::{Signature, SignatureError}; use iroh_blobs::Hash; +use n0_future::{ + time::{Duration, SystemTime}, + IterExt, +}; use serde::{Deserialize, Serialize}; pub use crate::heads::AuthorHeads; @@ -129,16 +132,22 @@ impl Subscribers { pub fn unsubscribe(&mut self, sender: &async_channel::Sender) { self.0.retain(|s| !same_channel(s, sender)); } - pub fn send(&mut self, event: Event) { - self.0 - .retain(|sender| sender.send_blocking(event.clone()).is_ok()) + pub async fn send(&mut self, event: Event) { + self.0 = std::mem::take(&mut self.0) + .into_iter() + .map(async |tx| tx.send(event.clone()).await.ok().map(|_| tx)) + .join_all() + .await + .into_iter() + .flatten() + .collect(); } pub fn len(&self) -> usize { self.0.len() } - pub fn send_with(&mut self, f: impl FnOnce() -> Event) { + pub async fn send_with(&mut self, f: impl FnOnce() -> Event) { if !self.0.is_empty() { - self.send(f()) + self.send(f()).await } } } @@ -361,7 +370,7 @@ where /// /// Returns the number of entries removed as a consequence of this insertion, /// or an error either if the entry failed to validate or if a store operation failed. - pub fn insert( + pub async fn insert( &mut self, key: impl AsRef<[u8]>, author: &Author, @@ -377,7 +386,7 @@ where let entry = Entry::new(id, record); let secret = self.secret_key()?; let signed_entry = entry.sign(secret, author); - self.insert_entry(signed_entry, InsertOrigin::Local) + self.insert_entry(signed_entry, InsertOrigin::Local).await } /// Delete entries that match the given `author` and key `prefix`. @@ -386,7 +395,7 @@ where /// entries whose key starts with or is equal to the given `prefix`. /// /// Returns the number of entries deleted. - pub fn delete_prefix( + pub async fn delete_prefix( &mut self, prefix: impl AsRef<[u8]>, author: &Author, @@ -395,7 +404,7 @@ where let id = RecordIdentifier::new(self.id(), author.id(), prefix); let entry = Entry::new_empty(id); let signed_entry = entry.sign(self.secret_key()?, author); - self.insert_entry(signed_entry, InsertOrigin::Local) + self.insert_entry(signed_entry, InsertOrigin::Local).await } /// Insert an entry into this replica which was received from a remote peer. @@ -405,7 +414,7 @@ where /// /// Returns the number of entries removed as a consequence of this insertion, /// or an error if the entry failed to validate or if a store operation failed. - pub fn insert_remote_entry( + pub async fn insert_remote_entry( &mut self, entry: SignedEntry, received_from: PeerIdBytes, @@ -417,13 +426,13 @@ where from: received_from, remote_content_status: content_status, }; - self.insert_entry(entry, origin) + self.insert_entry(entry, origin).await } /// Insert a signed entry into the database. /// /// Returns the number of entries removed as a consequence of this insertion. - fn insert_entry( + async fn insert_entry( &mut self, entry: SignedEntry, origin: InsertOrigin, @@ -462,7 +471,7 @@ where } }; - self.info.subscribers.send(insert_event); + self.info.subscribers.send(insert_event).await; Ok(removed_count) } @@ -471,7 +480,7 @@ where /// /// This does not store the content, just the record of it. /// Returns the calculated hash. - pub fn hash_and_insert( + pub async fn hash_and_insert( &mut self, key: impl AsRef<[u8]>, author: &Author, @@ -480,7 +489,7 @@ where self.info.ensure_open()?; let len = data.as_ref().len() as u64; let hash = Hash::new(data); - self.insert(key, author, hash, len)?; + self.insert(key, author, hash, len).await?; Ok(hash) } @@ -537,29 +546,29 @@ where validate_entry(now, store, my_namespace, entry, &origin).is_ok() }, // on_insert callback: is called when an entry was actually inserted in the store - |_store, entry, content_status| { + async |_store, entry, content_status| { // We use `send_with` to only clone the entry if we have active subscriptions. - self.info.subscribers.send_with(|| { - let should_download = download_policy.matches(entry.entry()); - Event::RemoteInsert { - from: from_peer, - namespace: my_namespace, - entry: entry.clone(), - should_download, - remote_content_status: content_status, - } - }) + self.info + .subscribers + .send_with(|| { + let should_download = download_policy.matches(entry.entry()); + Event::RemoteInsert { + from: from_peer, + namespace: my_namespace, + entry: entry.clone(), + should_download, + remote_content_status: content_status, + } + }) + .await }, // content_status callback: get content status for outgoing entries - move |entry| { - let cb = cb.clone(); - Box::pin(async move { - if let Some(cb) = cb.as_ref() { - cb(entry.content_hash()).await - } else { - ContentStatus::Missing - } - }) + async move |entry| { + if let Some(cb) = cb.as_ref() { + cb(entry.content_hash()).await + } else { + ContentStatus::Missing + } }, ) .await?; @@ -1205,23 +1214,24 @@ mod tests { store::{OpenError, Query, SortBy, SortDirection, Store}, }; - #[test] - fn test_basics_memory() -> Result<()> { + #[tokio::test] + async fn test_basics_memory() -> Result<()> { let store = store::Store::memory(); - test_basics(store)?; + test_basics(store).await?; Ok(()) } - #[test] - fn test_basics_fs() -> Result<()> { + #[tokio::test] + #[cfg(feature = "fs-store")] + async fn test_basics_fs() -> Result<()> { let dbfile = tempfile::NamedTempFile::new()?; let store = store::fs::Store::persistent(dbfile.path())?; - test_basics(store)?; + test_basics(store).await?; Ok(()) } - fn test_basics(mut store: Store) -> Result<()> { + async fn test_basics(mut store: Store) -> Result<()> { let mut rng = rand::rng(); let alice = Author::new(&mut rng); let bob = Author::new(&mut rng); @@ -1235,11 +1245,9 @@ mod tests { let mut my_replica = store.new_replica(myspace.clone())?; for i in 0..10 { - my_replica.hash_and_insert( - format!("/{i}"), - &alice, - format!("{i}: hello from alice"), - )?; + my_replica + .hash_and_insert(format!("/{i}"), &alice, format!("{i}: hello from alice")) + .await?; } for i in 0..10 { @@ -1253,13 +1261,17 @@ mod tests { // Test multiple records for the same key let mut my_replica = store.new_replica(myspace.clone())?; - my_replica.hash_and_insert("/cool/path", &alice, "round 1")?; + my_replica + .hash_and_insert("/cool/path", &alice, "round 1") + .await?; let _entry = store .get_exact(myspace.id(), alice.id(), "/cool/path", false)? .unwrap(); // Second let mut my_replica = store.new_replica(myspace.clone())?; - my_replica.hash_and_insert("/cool/path", &alice, "round 2")?; + my_replica + .hash_and_insert("/cool/path", &alice, "round 2") + .await?; let _entry = store .get_exact(myspace.id(), alice.id(), "/cool/path", false)? .unwrap(); @@ -1290,7 +1302,9 @@ mod tests { // insert record from different author let mut my_replica = store.new_replica(myspace.clone())?; - let _entry = my_replica.hash_and_insert("/cool/path", &bob, "bob round 1")?; + let _entry = my_replica + .hash_and_insert("/cool/path", &bob, "bob round 1") + .await?; // Get All by author let entries: Vec<_> = store @@ -1392,20 +1406,21 @@ mod tests { Ok(()) } - #[test] - fn test_content_hashes_iterator_memory() -> Result<()> { + #[tokio::test] + async fn test_content_hashes_iterator_memory() -> Result<()> { let store = store::Store::memory(); - test_content_hashes_iterator(store) + test_content_hashes_iterator(store).await } - #[test] - fn test_content_hashes_iterator_fs() -> Result<()> { + #[tokio::test] + #[cfg(feature = "fs-store")] + async fn test_content_hashes_iterator_fs() -> Result<()> { let dbfile = tempfile::NamedTempFile::new()?; let store = store::fs::Store::persistent(dbfile.path())?; - test_content_hashes_iterator(store) + test_content_hashes_iterator(store).await } - fn test_content_hashes_iterator(mut store: Store) -> Result<()> { + async fn test_content_hashes_iterator(mut store: Store) -> Result<()> { let mut rng = rand::rng(); let mut expected = HashSet::new(); let n_replicas = 3; @@ -1417,7 +1432,7 @@ mod tests { for j in 0..n_entries { let key = format!("{j}"); let data = format!("{i}:{j}"); - let hash = replica.hash_and_insert(key, &author, data)?; + let hash = replica.hash_and_insert(key, &author, data).await?; expected.insert(hash); } } @@ -1517,23 +1532,24 @@ mod tests { } } - #[test] - fn test_timestamps_memory() -> Result<()> { + #[tokio::test] + async fn test_timestamps_memory() -> Result<()> { let store = store::Store::memory(); - test_timestamps(store)?; + test_timestamps(store).await?; Ok(()) } - #[test] - fn test_timestamps_fs() -> Result<()> { + #[tokio::test] + #[cfg(feature = "fs-store")] + async fn test_timestamps_fs() -> Result<()> { let dbfile = tempfile::NamedTempFile::new()?; let store = store::fs::Store::persistent(dbfile.path())?; - test_timestamps(store)?; + test_timestamps(store).await?; Ok(()) } - fn test_timestamps(mut store: Store) -> Result<()> { + async fn test_timestamps(mut store: Store) -> Result<()> { let mut rng = rand_chacha::ChaCha12Rng::seed_from_u64(1); let namespace = NamespaceSecret::new(&mut rng); let _replica = store.new_replica(namespace.clone())?; @@ -1552,6 +1568,7 @@ mod tests { replica .insert_entry(entry.clone(), InsertOrigin::Local) + .await .unwrap(); store.close_replica(namespace.id()); let res = store @@ -1567,7 +1584,7 @@ mod tests { }; let mut replica = store.open_replica(&namespace.id())?; - let res = replica.insert_entry(entry2, InsertOrigin::Local); + let res = replica.insert_entry(entry2, InsertOrigin::Local).await; store.close_replica(namespace.id()); assert!(matches!(res, Err(InsertError::NewerEntryExists))); let res = store @@ -1588,6 +1605,7 @@ mod tests { } #[tokio::test] + #[cfg(feature = "fs-store")] async fn test_replica_sync_fs() -> Result<()> { let alice_dbfile = tempfile::NamedTempFile::new()?; let alice_store = store::fs::Store::persistent(alice_dbfile.path())?; @@ -1607,12 +1625,12 @@ mod tests { let myspace = NamespaceSecret::new(&mut rng); let mut alice = alice_store.new_replica(myspace.clone())?; for el in &alice_set { - alice.hash_and_insert(el, &author, el.as_bytes())?; + alice.hash_and_insert(el, &author, el.as_bytes()).await?; } let mut bob = bob_store.new_replica(myspace.clone())?; for el in &bob_set { - bob.hash_and_insert(el, &author, el.as_bytes())?; + bob.hash_and_insert(el, &author, el.as_bytes()).await?; } let (alice_out, bob_out) = sync(&mut alice, &mut bob).await?; @@ -1641,6 +1659,7 @@ mod tests { } #[tokio::test] + #[cfg(feature = "fs-store")] async fn test_replica_timestamp_sync_fs() -> Result<()> { let alice_dbfile = tempfile::NamedTempFile::new()?; let alice_store = store::fs::Store::persistent(alice_dbfile.path())?; @@ -1664,9 +1683,9 @@ mod tests { let key = b"key"; let alice_value = b"alice"; let bob_value = b"bob"; - let _alice_hash = alice.hash_and_insert(key, &author, alice_value)?; + let _alice_hash = alice.hash_and_insert(key, &author, alice_value).await?; // system time increased - sync should overwrite - let bob_hash = bob.hash_and_insert(key, &author, bob_value)?; + let bob_hash = bob.hash_and_insert(key, &author, bob_value).await?; sync(&mut alice, &mut bob).await?; assert_eq!( get_content_hash(&mut alice_store, namespace.id(), author.id(), key)?, @@ -1682,8 +1701,8 @@ mod tests { let alice_value_2 = b"alice2"; // system time increased - sync should overwrite - let _bob_hash_2 = bob.hash_and_insert(key, &author, bob_value)?; - let alice_hash_2 = alice.hash_and_insert(key, &author, alice_value_2)?; + let _bob_hash_2 = bob.hash_and_insert(key, &author, bob_value).await?; + let alice_hash_2 = alice.hash_and_insert(key, &author, alice_value_2).await?; sync(&mut alice, &mut bob).await?; assert_eq!( get_content_hash(&mut alice_store, namespace.id(), author.id(), key)?, @@ -1698,8 +1717,8 @@ mod tests { Ok(()) } - #[test] - fn test_future_timestamp() -> Result<()> { + #[tokio::test] + async fn test_future_timestamp() -> Result<()> { let mut rng = rand::rng(); let mut store = store::Store::memory(); let author = Author::new(&mut rng); @@ -1710,7 +1729,9 @@ mod tests { let t = system_time_now(); let record = Record::from_data(b"1", t); let entry0 = SignedEntry::from_parts(&namespace, &author, key, record); - replica.insert_entry(entry0.clone(), InsertOrigin::Local)?; + replica + .insert_entry(entry0.clone(), InsertOrigin::Local) + .await?; assert_eq!( get_entry(&mut store, namespace.id(), author.id(), key)?, @@ -1721,7 +1742,9 @@ mod tests { let t = system_time_now() + MAX_TIMESTAMP_FUTURE_SHIFT - 10000; let record = Record::from_data(b"2", t); let entry1 = SignedEntry::from_parts(&namespace, &author, key, record); - replica.insert_entry(entry1.clone(), InsertOrigin::Local)?; + replica + .insert_entry(entry1.clone(), InsertOrigin::Local) + .await?; assert_eq!( get_entry(&mut store, namespace.id(), author.id(), key)?, entry1 @@ -1731,7 +1754,9 @@ mod tests { let t = system_time_now() + MAX_TIMESTAMP_FUTURE_SHIFT; let record = Record::from_data(b"2", t); let entry2 = SignedEntry::from_parts(&namespace, &author, key, record); - replica.insert_entry(entry2.clone(), InsertOrigin::Local)?; + replica + .insert_entry(entry2.clone(), InsertOrigin::Local) + .await?; assert_eq!( get_entry(&mut store, namespace.id(), author.id(), key)?, entry2 @@ -1741,7 +1766,7 @@ mod tests { let t = system_time_now() + MAX_TIMESTAMP_FUTURE_SHIFT + 10000; let record = Record::from_data(b"2", t); let entry3 = SignedEntry::from_parts(&namespace, &author, key, record); - let res = replica.insert_entry(entry3, InsertOrigin::Local); + let res = replica.insert_entry(entry3, InsertOrigin::Local).await; assert!(matches!( res, Err(InsertError::Validation( @@ -1756,42 +1781,43 @@ mod tests { Ok(()) } - #[test] - fn test_insert_empty() -> Result<()> { + #[tokio::test] + async fn test_insert_empty() -> Result<()> { let mut store = store::Store::memory(); let mut rng = rand::rng(); let alice = Author::new(&mut rng); let myspace = NamespaceSecret::new(&mut rng); let mut replica = store.new_replica(myspace.clone())?; let hash = Hash::new(b""); - let res = replica.insert(b"foo", &alice, hash, 0); + let res = replica.insert(b"foo", &alice, hash, 0).await; assert!(matches!(res, Err(InsertError::EntryIsEmpty))); store.flush()?; Ok(()) } - #[test] - fn test_prefix_delete_memory() -> Result<()> { + #[tokio::test] + async fn test_prefix_delete_memory() -> Result<()> { let store = store::Store::memory(); - test_prefix_delete(store)?; + test_prefix_delete(store).await?; Ok(()) } - #[test] - fn test_prefix_delete_fs() -> Result<()> { + #[tokio::test] + #[cfg(feature = "fs-store")] + async fn test_prefix_delete_fs() -> Result<()> { let dbfile = tempfile::NamedTempFile::new()?; let store = store::fs::Store::persistent(dbfile.path())?; - test_prefix_delete(store)?; + test_prefix_delete(store).await?; Ok(()) } - fn test_prefix_delete(mut store: Store) -> Result<()> { + async fn test_prefix_delete(mut store: Store) -> Result<()> { let mut rng = rand::rng(); let alice = Author::new(&mut rng); let myspace = NamespaceSecret::new(&mut rng); let mut replica = store.new_replica(myspace.clone())?; - let hash1 = replica.hash_and_insert(b"foobar", &alice, b"hello")?; - let hash2 = replica.hash_and_insert(b"fooboo", &alice, b"world")?; + let hash1 = replica.hash_and_insert(b"foobar", &alice, b"hello").await?; + let hash2 = replica.hash_and_insert(b"fooboo", &alice, b"world").await?; // sanity checks assert_eq!( @@ -1805,7 +1831,7 @@ mod tests { // delete let mut replica = store.new_replica(myspace.clone())?; - let deleted = replica.delete_prefix(b"foo", &alice)?; + let deleted = replica.delete_prefix(b"foo", &alice).await?; assert_eq!(deleted, 2); assert_eq!( store.get_exact(myspace.id(), alice.id(), b"foobar", false)?, @@ -1832,6 +1858,7 @@ mod tests { } #[tokio::test] + #[cfg(feature = "fs-store")] async fn test_replica_sync_delete_fs() -> Result<()> { let alice_dbfile = tempfile::NamedTempFile::new()?; let alice_store = store::fs::Store::persistent(alice_dbfile.path())?; @@ -1849,12 +1876,12 @@ mod tests { let myspace = NamespaceSecret::new(&mut rng); let mut alice = alice_store.new_replica(myspace.clone())?; for el in &alice_set { - alice.hash_and_insert(el, &author, el.as_bytes())?; + alice.hash_and_insert(el, &author, el.as_bytes()).await?; } let mut bob = bob_store.new_replica(myspace.clone())?; for el in &bob_set { - bob.hash_and_insert(el, &author, el.as_bytes())?; + bob.hash_and_insert(el, &author, el.as_bytes()).await?; } sync(&mut alice, &mut bob).await?; @@ -1866,8 +1893,9 @@ mod tests { let mut alice = alice_store.new_replica(myspace.clone())?; let mut bob = bob_store.new_replica(myspace.clone())?; - alice.delete_prefix("foo", &author)?; - bob.hash_and_insert("fooz", &author, "fooz".as_bytes())?; + alice.delete_prefix("foo", &author).await?; + bob.hash_and_insert("fooz", &author, "fooz".as_bytes()) + .await?; sync(&mut alice, &mut bob).await?; check_entries(&mut alice_store, &myspace.id(), &author, &["fog", "fooz"])?; check_entries(&mut bob_store, &myspace.id(), &author, &["fog", "fooz"])?; @@ -1876,27 +1904,28 @@ mod tests { Ok(()) } - #[test] - fn test_replica_remove_memory() -> Result<()> { + #[tokio::test] + async fn test_replica_remove_memory() -> Result<()> { let alice_store = store::Store::memory(); - test_replica_remove(alice_store) + test_replica_remove(alice_store).await } - #[test] - fn test_replica_remove_fs() -> Result<()> { + #[tokio::test] + #[cfg(feature = "fs-store")] + async fn test_replica_remove_fs() -> Result<()> { let alice_dbfile = tempfile::NamedTempFile::new()?; let alice_store = store::fs::Store::persistent(alice_dbfile.path())?; - test_replica_remove(alice_store) + test_replica_remove(alice_store).await } - fn test_replica_remove(mut store: Store) -> Result<()> { + async fn test_replica_remove(mut store: Store) -> Result<()> { let mut rng = rand::rng(); let namespace = NamespaceSecret::new(&mut rng); let author = Author::new(&mut rng); let mut replica = store.new_replica(namespace.clone())?; // insert entry - let hash = replica.hash_and_insert(b"foo", &author, b"bar")?; + let hash = replica.hash_and_insert(b"foo", &author, b"bar").await?; let res = store .get_many(namespace.id(), Query::all())? .collect::>(); @@ -1919,7 +1948,7 @@ mod tests { // may recreate replica let mut replica = store.new_replica(namespace.clone())?; - replica.insert(b"foo", &author, hash, 3)?; + replica.insert(b"foo", &author, hash, 3).await?; let res = store .get_many(namespace.id(), Query::all())? .collect::>(); @@ -1928,20 +1957,21 @@ mod tests { Ok(()) } - #[test] - fn test_replica_delete_edge_cases_memory() -> Result<()> { + #[tokio::test] + async fn test_replica_delete_edge_cases_memory() -> Result<()> { let store = store::Store::memory(); - test_replica_delete_edge_cases(store) + test_replica_delete_edge_cases(store).await } - #[test] - fn test_replica_delete_edge_cases_fs() -> Result<()> { + #[tokio::test] + #[cfg(feature = "fs-store")] + async fn test_replica_delete_edge_cases_fs() -> Result<()> { let dbfile = tempfile::NamedTempFile::new()?; let store = store::fs::Store::persistent(dbfile.path())?; - test_replica_delete_edge_cases(store) + test_replica_delete_edge_cases(store).await } - fn test_replica_delete_edge_cases(mut store: Store) -> Result<()> { + async fn test_replica_delete_edge_cases(mut store: Store) -> Result<()> { let mut rng = rand::rng(); let author = Author::new(&mut rng); let namespace = NamespaceSecret::new(&mut rng); @@ -1956,23 +1986,23 @@ mod tests { for suffix in edgecases { let key = [prefix, suffix].to_vec(); expected.push(key.clone()); - replica.insert(&key, &author, hash, len)?; + replica.insert(&key, &author, hash, len).await?; } assert_keys(&mut store, namespace.id(), expected); let mut replica = store.new_replica(namespace.clone())?; - replica.delete_prefix([prefix], &author)?; + replica.delete_prefix([prefix], &author).await?; assert_keys(&mut store, namespace.id(), vec![]); } let mut replica = store.new_replica(namespace.clone())?; let key = vec![1u8, 0u8]; - replica.insert(key, &author, hash, len)?; + replica.insert(key, &author, hash, len).await?; let key = vec![1u8, 1u8]; - replica.insert(key, &author, hash, len)?; + replica.insert(key, &author, hash, len).await?; let key = vec![1u8, 2u8]; - replica.insert(key, &author, hash, len)?; + replica.insert(key, &author, hash, len).await?; let prefix = vec![1u8, 1u8]; - replica.delete_prefix(prefix, &author)?; + replica.delete_prefix(prefix, &author).await?; assert_keys( &mut store, namespace.id(), @@ -1981,11 +2011,11 @@ mod tests { let mut replica = store.new_replica(namespace.clone())?; let key = vec![0u8, 255u8]; - replica.insert(key, &author, hash, len)?; + replica.insert(key, &author, hash, len).await?; let key = vec![0u8, 0u8]; - replica.insert(key, &author, hash, len)?; + replica.insert(key, &author, hash, len).await?; let prefix = vec![0u8]; - replica.delete_prefix(prefix, &author)?; + replica.delete_prefix(prefix, &author).await?; assert_keys( &mut store, namespace.id(), @@ -1995,27 +2025,28 @@ mod tests { Ok(()) } - #[test] - fn test_latest_iter_memory() -> Result<()> { + #[tokio::test] + async fn test_latest_iter_memory() -> Result<()> { let store = store::Store::memory(); - test_latest_iter(store) + test_latest_iter(store).await } - #[test] - fn test_latest_iter_fs() -> Result<()> { + #[tokio::test] + #[cfg(feature = "fs-store")] + async fn test_latest_iter_fs() -> Result<()> { let dbfile = tempfile::NamedTempFile::new()?; let store = store::fs::Store::persistent(dbfile.path())?; - test_latest_iter(store) + test_latest_iter(store).await } - fn test_latest_iter(mut store: Store) -> Result<()> { + async fn test_latest_iter(mut store: Store) -> Result<()> { let mut rng = rand::rng(); let author0 = Author::new(&mut rng); let author1 = Author::new(&mut rng); let namespace = NamespaceSecret::new(&mut rng); let mut replica = store.new_replica(namespace.clone())?; - replica.hash_and_insert(b"a0.1", &author0, b"hi")?; + replica.hash_and_insert(b"a0.1", &author0, b"hi").await?; let latest = store .get_latest_for_each_author(namespace.id())? .collect::>>()?; @@ -2023,8 +2054,8 @@ mod tests { assert_eq!(latest[0].2, b"a0.1".to_vec()); let mut replica = store.new_replica(namespace.clone())?; - replica.hash_and_insert(b"a1.1", &author1, b"hi")?; - replica.hash_and_insert(b"a0.2", &author0, b"hi")?; + replica.hash_and_insert(b"a1.1", &author1, b"hi").await?; + replica.hash_and_insert(b"a0.2", &author0, b"hi").await?; let latest = store .get_latest_for_each_author(namespace.id())? .collect::>>()?; @@ -2035,24 +2066,25 @@ mod tests { Ok(()) } - #[test] - fn test_replica_byte_keys_memory() -> Result<()> { + #[tokio::test] + async fn test_replica_byte_keys_memory() -> Result<()> { let store = store::Store::memory(); - test_replica_byte_keys(store)?; + test_replica_byte_keys(store).await?; Ok(()) } - #[test] - fn test_replica_byte_keys_fs() -> Result<()> { + #[tokio::test] + #[cfg(feature = "fs-store")] + async fn test_replica_byte_keys_fs() -> Result<()> { let dbfile = tempfile::NamedTempFile::new()?; let store = store::fs::Store::persistent(dbfile.path())?; - test_replica_byte_keys(store)?; + test_replica_byte_keys(store).await?; Ok(()) } - fn test_replica_byte_keys(mut store: Store) -> Result<()> { + async fn test_replica_byte_keys(mut store: Store) -> Result<()> { let mut rng = rand::rng(); let author = Author::new(&mut rng); let namespace = NamespaceSecret::new(&mut rng); @@ -2062,11 +2094,11 @@ mod tests { let key = vec![1u8, 0u8]; let mut replica = store.new_replica(namespace.clone())?; - replica.insert(key, &author, hash, len)?; + replica.insert(key, &author, hash, len).await?; assert_keys(&mut store, namespace.id(), vec![vec![1u8, 0u8]]); let key = vec![1u8, 2u8]; let mut replica = store.new_replica(namespace.clone())?; - replica.insert(key, &author, hash, len)?; + replica.insert(key, &author, hash, len).await?; assert_keys( &mut store, namespace.id(), @@ -2075,7 +2107,7 @@ mod tests { let key = vec![0u8, 255u8]; let mut replica = store.new_replica(namespace.clone())?; - replica.insert(key, &author, hash, len)?; + replica.insert(key, &author, hash, len).await?; assert_keys( &mut store, namespace.id(), @@ -2085,21 +2117,22 @@ mod tests { Ok(()) } - #[test] - fn test_replica_capability_memory() -> Result<()> { + #[tokio::test] + async fn test_replica_capability_memory() -> Result<()> { let store = store::Store::memory(); - test_replica_capability(store) + test_replica_capability(store).await } - #[test] - fn test_replica_capability_fs() -> Result<()> { + #[tokio::test] + #[cfg(feature = "fs-store")] + async fn test_replica_capability_fs() -> Result<()> { let dbfile = tempfile::NamedTempFile::new()?; let store = store::fs::Store::persistent(dbfile.path())?; - test_replica_capability(store) + test_replica_capability(store).await } #[allow(clippy::redundant_pattern_matching)] - fn test_replica_capability(mut store: Store) -> Result<()> { + async fn test_replica_capability(mut store: Store) -> Result<()> { let mut rng = rand_chacha::ChaCha12Rng::seed_from_u64(1); let author = store.new_author(&mut rng)?; let namespace = NamespaceSecret::new(&mut rng); @@ -2108,18 +2141,18 @@ mod tests { let capability = Capability::Read(namespace.id()); store.import_namespace(capability)?; let mut replica = store.open_replica(&namespace.id())?; - let res = replica.hash_and_insert(b"foo", &author, b"bar"); + let res = replica.hash_and_insert(b"foo", &author, b"bar").await; assert!(matches!(res, Err(InsertError::ReadOnly))); // import write capability - insert must succeed let capability = Capability::Write(namespace.clone()); store.import_namespace(capability)?; let mut replica = store.open_replica(&namespace.id())?; - let res = replica.hash_and_insert(b"foo", &author, b"bar"); + let res = replica.hash_and_insert(b"foo", &author, b"bar").await; assert!(matches!(res, Ok(_))); store.close_replica(namespace.id()); let mut replica = store.open_replica(&namespace.id())?; - let res = replica.hash_and_insert(b"foo", &author, b"bar"); + let res = replica.hash_and_insert(b"foo", &author, b"bar").await; assert!(res.is_ok()); // import read capability again - insert must still succeed @@ -2127,7 +2160,7 @@ mod tests { store.import_namespace(capability)?; store.close_replica(namespace.id()); let mut replica = store.open_replica(&namespace.id())?; - let res = replica.hash_and_insert(b"foo", &author, b"bar"); + let res = replica.hash_and_insert(b"foo", &author, b"bar").await; assert!(res.is_ok()); store.flush()?; Ok(()) @@ -2140,6 +2173,7 @@ mod tests { } #[tokio::test] + #[cfg(feature = "fs-store")] async fn test_actor_capability_fs() -> Result<()> { let dbfile = tempfile::NamedTempFile::new()?; let store = store::fs::Store::persistent(dbfile.path())?; @@ -2217,7 +2251,7 @@ mod tests { replica1.info.subscribe(events1_sender); replica2.info.subscribe(events2_sender); - replica1.hash_and_insert(b"foo", &author, b"init")?; + replica1.hash_and_insert(b"foo", &author, b"init").await?; let from1 = replica1.sync_initial_message()?; let from2 = replica2 @@ -2233,7 +2267,7 @@ mod tests { // now we will receive the entry from rpelica1. we will insert a newer entry now, while the // sync is already running. this means the entry from replica1 will be rejected. we make // sure that no InsertRemote event is emitted for this entry. - replica2.hash_and_insert(b"foo", &author, b"update")?; + replica2.hash_and_insert(b"foo", &author, b"update").await?; let from2 = replica2 .sync_process_message(from1, peer1, &mut state2) .await @@ -2254,24 +2288,25 @@ mod tests { Ok(()) } - #[test] - fn test_replica_queries_mem() -> Result<()> { + #[tokio::test] + async fn test_replica_queries_mem() -> Result<()> { let store = store::Store::memory(); - test_replica_queries(store)?; + test_replica_queries(store).await?; Ok(()) } - #[test] - fn test_replica_queries_fs() -> Result<()> { + #[tokio::test] + #[cfg(feature = "fs-store")] + async fn test_replica_queries_fs() -> Result<()> { let dbfile = tempfile::NamedTempFile::new()?; let store = store::fs::Store::persistent(dbfile.path())?; - test_replica_queries(store)?; + test_replica_queries(store).await?; Ok(()) } - fn test_replica_queries(mut store: Store) -> Result<()> { + async fn test_replica_queries(mut store: Store) -> Result<()> { let mut rng = rand_chacha::ChaCha12Rng::seed_from_u64(1); let namespace = NamespaceSecret::new(&mut rng); let namespace_id = namespace.id(); @@ -2287,10 +2322,10 @@ mod tests { ); let mut replica = store.new_replica(namespace.clone())?; - replica.hash_and_insert("hi/world", &a2, "a2")?; - replica.hash_and_insert("hi/world", &a1, "a1")?; - replica.hash_and_insert("hi/moon", &a2, "a1")?; - replica.hash_and_insert("hi", &a3, "a3")?; + replica.hash_and_insert("hi/world", &a2, "a2").await?; + replica.hash_and_insert("hi/world", &a1, "a1").await?; + replica.hash_and_insert("hi/moon", &a2, "a1").await?; + replica.hash_and_insert("hi", &a3, "a3").await?; struct QueryTester<'a> { store: &'a mut Store, @@ -2420,7 +2455,7 @@ mod tests { ); let mut replica = store.new_replica(namespace)?; - replica.delete_prefix("hi/world", &a2)?; + replica.delete_prefix("hi/world", &a2).await?; let mut qt = QueryTester { store: &mut store, namespace: namespace_id, @@ -2451,6 +2486,7 @@ mod tests { } #[test] + #[cfg(feature = "fs-store")] fn test_dl_policies_fs() -> Result<()> { let dbfile = tempfile::NamedTempFile::new()?; let mut store = store::fs::Store::persistent(dbfile.path())?; diff --git a/tests/client.rs b/tests/client.rs index ed93a949..6ba45c6b 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -31,7 +31,7 @@ async fn test_doc_close() -> Result<()> { // dropping doc1 will close the doc if not already closed // wait a bit because the close-on-drop spawns a task for which we cannot track completion. drop(doc1); - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; + n0_future::time::sleep(n0_future::time::Duration::from_millis(100)).await; // operations on doc2 still succeed doc2.set_bytes(author, "foo", "bar").await?; @@ -167,6 +167,7 @@ async fn test_default_author_memory() -> Result<()> { #[tokio::test] #[traced_test] +#[cfg(feature = "fs-store")] async fn test_default_author_persist() -> TestResult<()> { let iroh_root_dir = tempfile::TempDir::new()?; let iroh_root = iroh_root_dir.path(); @@ -216,7 +217,7 @@ async fn test_default_author_persist() -> TestResult<()> { // somehow the blob store is not shutdown correctly (yet?) on macos. // so we give it some time until we find a proper fix. #[cfg(target_os = "macos")] - tokio::time::sleep(std::time::Duration::from_secs(1)).await; + n0_future::time::sleep(std::time::Duration::from_secs(1)).await; tokio::fs::remove_file(iroh_root.join("default-author")).await?; drop(iroh); diff --git a/tests/gc.rs b/tests/gc.rs index df726a75..0f28e251 100644 --- a/tests/gc.rs +++ b/tests/gc.rs @@ -1,9 +1,12 @@ -use std::{path::PathBuf, time::Duration}; +#![allow(unused)] + +use std::path::PathBuf; use anyhow::Result; use bytes::Bytes; use futures_lite::StreamExt; use iroh_blobs::api::blobs::ImportMode; +use n0_future::time::Duration; use rand::RngCore; use testdir::testdir; use util::Node; @@ -18,6 +21,7 @@ pub fn create_test_data(size: usize) -> Bytes { } /// Wrap a bao store in a node that has gc enabled. +#[cfg(feature = "fs-store")] async fn persistent_node( path: PathBuf, gc_period: Duration, @@ -35,6 +39,7 @@ async fn persistent_node( } #[tokio::test] +#[cfg(feature = "fs-store")] async fn redb_doc_import_stress() -> Result<()> { let _ = tracing_subscriber::fmt::try_init(); let dir = testdir!(); diff --git a/tests/sync.rs b/tests/sync.rs index 70b4a799..3037ec6d 100644 --- a/tests/sync.rs +++ b/tests/sync.rs @@ -1,9 +1,4 @@ -use std::{ - collections::HashMap, - future::Future, - sync::Arc, - time::{Duration, Instant}, -}; +use std::{collections::HashMap, future::Future, sync::Arc}; use anyhow::{anyhow, bail, Context, Result}; use bytes::Bytes; @@ -20,7 +15,9 @@ use iroh_docs::{ store::{DownloadPolicy, FilterKind, Query}, AuthorId, ContentStatus, Entry, }; +use n0_future::time::{Duration, Instant}; use rand::{CryptoRng, Rng, SeedableRng}; +#[cfg(feature = "fs-store")] use tempfile::tempdir; use tracing::{debug, error_span, info, Instrument}; use tracing_test::traced_test; @@ -113,12 +110,13 @@ async fn sync_simple() -> Result<()> { assert_latest(blobs1, &doc1, b"k1", b"v1").await; info!("node0: assert 2 events"); - assert_next( + assert_next_unordered( &mut events0, TIMEOUT, vec![ Box::new(move |e| matches!(e, LiveEvent::NeighborUp(peer) if *peer == peer1)), Box::new(move |e| match_sync_finished(e, peer1)), + match_event!(LiveEvent::PendingContentReady), ], ) .await; @@ -140,7 +138,7 @@ async fn sync_subscribe_no_sync() -> Result<()> { let mut sub = doc.subscribe().await?; let author = client.docs().author_create().await?; doc.set_bytes(author, b"k".to_vec(), b"v".to_vec()).await?; - let event = tokio::time::timeout(Duration::from_millis(100), sub.next()).await?; + let event = n0_future::time::timeout(Duration::from_millis(100), sub.next()).await?; assert!( matches!(event, Some(Ok(LiveEvent::InsertLocal { .. }))), "expected InsertLocal but got {event:?}" @@ -587,6 +585,7 @@ async fn test_sync_via_relay() -> Result<()> { #[tokio::test] #[traced_test] #[ignore = "flaky"] +#[cfg(feature = "fs-store")] async fn sync_restart_node() -> Result<()> { let mut rng = test_rng(b"sync_restart_node"); let (relay_map, _relay_url, _guard) = iroh::test_utils::run_relay_server().await?; @@ -656,7 +655,7 @@ async fn sync_restart_node() -> Result<()> { info!(me = %id1.fmt_short(), "node1 down"); info!(me = %id1.fmt_short(), "sleep 1s"); - tokio::time::sleep(Duration::from_secs(1)).await; + n0_future::time::sleep(Duration::from_secs(1)).await; info!(me = %id2.fmt_short(), "node2 set b"); let hash_b = doc2.set_bytes(author2, "n2/b", "b").await?; @@ -842,7 +841,7 @@ async fn test_download_policies() -> Result<()> { (downloaded_a, downloaded_b) }; - let (downloaded_a, mut downloaded_b) = tokio::time::timeout(TIMEOUT, fut) + let (downloaded_a, mut downloaded_b) = n0_future::time::timeout(TIMEOUT, fut) .await .context("timeout elapsed")?; @@ -873,7 +872,7 @@ async fn sync_big() -> Result<()> { tokio::task::spawn(async move { for i in 0.. { - tokio::time::sleep(Duration::from_secs(1)).await; + n0_future::time::sleep(Duration::from_secs(1)).await; info!("tick {i}"); } }); @@ -1011,7 +1010,7 @@ async fn test_list_docs_stream() -> testresult::TestResult<()> { } }; - tokio::time::timeout(Duration::from_secs(2), fut) + n0_future::time::timeout(Duration::from_secs(2), fut) .await .expect("not to timeout"); @@ -1080,7 +1079,7 @@ async fn wait_for_events( matcher: impl Fn(&LiveEvent) -> bool, ) -> anyhow::Result> { let mut res = Vec::with_capacity(count); - let sleep = tokio::time::sleep(timeout); + let sleep = n0_future::time::sleep(timeout); tokio::pin!(sleep); while res.len() < count { tokio::select! { @@ -1159,6 +1158,7 @@ impl PartialEq for (Entry, Bytes) { #[tokio::test] #[traced_test] +#[cfg(feature = "fs-store")] async fn doc_delete() -> Result<()> { let tempdir = tempdir()?; // TODO(Frando): iroh-blobs has gc only for fs store atm, change test to test both @@ -1183,7 +1183,7 @@ async fn doc_delete() -> Result<()> { // wait for gc // TODO: allow to manually trigger gc - tokio::time::sleep(Duration::from_secs(2)).await; + n0_future::time::sleep(Duration::from_secs(2)).await; let bytes = client.blobs().get_bytes(hash).await; assert!(bytes.is_err()); node.shutdown().await?; @@ -1288,7 +1288,7 @@ async fn assert_next( } items }; - let res = tokio::time::timeout(timeout, fut).await; + let res = n0_future::time::timeout(timeout, fut).await; res.expect("timeout reached") } @@ -1356,7 +1356,7 @@ async fn assert_next_unordered_with_optionals( Ok(()) }; tokio::pin!(fut); - let res = tokio::time::timeout(timeout, fut) + let res = n0_future::time::timeout(timeout, fut) .await .map_err(|_| anyhow!("Timeout reached ({timeout:?})")) .and_then(|res| res); diff --git a/tests/util.rs b/tests/util.rs index b81de0d5..a048701f 100644 --- a/tests/util.rs +++ b/tests/util.rs @@ -1,4 +1,5 @@ -#![allow(dead_code)] +#![allow(unused)] + use std::{ net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6}, ops::Deref, @@ -6,7 +7,7 @@ use std::{ }; use iroh::{address_lookup::IntoAddressLookup, dns::DnsResolver, EndpointId, RelayMode, SecretKey}; -use iroh_blobs::store::{fs::options::Options, GcConfig}; +use iroh_blobs::store::GcConfig; use iroh_docs::{engine::ProtectCallbackHandler, protocol::Docs}; use iroh_gossip::net::Gossip; @@ -64,9 +65,8 @@ impl Client { pub struct Builder { endpoint: iroh::endpoint::Builder, use_n0_address_lookup: bool, - path: Option, - // node_address_lookup: Option>, - gc_interval: Option, + storage: Storage, + gc_interval: Option, #[debug(skip)] register_gc_done_cb: Option>, bind_random_port: bool, @@ -97,9 +97,10 @@ impl Builder { let endpoint = builder.bind().await?; let mut router = iroh::protocol::Router::builder(endpoint.clone()); let gossip = Gossip::builder().spawn(endpoint.clone()); - let mut docs_builder = match self.path { - Some(ref path) => Docs::persistent(path.to_path_buf()), - None => Docs::memory(), + let mut docs_builder = match self.storage { + Storage::Memory => Docs::memory(), + #[cfg(feature = "fs-store")] + Storage::Persistent(ref path) => Docs::persistent(path.to_path_buf()), }; if let Some(protect_cb) = protect_cb { docs_builder = docs_builder.protect_handler(protect_cb); @@ -163,7 +164,7 @@ impl Builder { self } - pub fn gc_interval(mut self, value: Option) -> Self { + pub fn gc_interval(mut self, value: Option) -> Self { self.gc_interval = value; self } @@ -183,11 +184,11 @@ impl Builder { self } - fn new(path: Option) -> Self { + fn new(storage: Storage) -> Self { Self { endpoint: iroh::Endpoint::empty_builder(RelayMode::Disabled), use_n0_address_lookup: true, - path, + storage, gc_interval: None, bind_random_port: true, // node_address_lookup: None, @@ -197,30 +198,38 @@ impl Builder { } } +#[derive(Debug)] +enum Storage { + Memory, + #[cfg(feature = "fs-store")] + Persistent(PathBuf), +} + impl Node { /// Creates a new node with memory storage pub fn memory() -> Builder { - Builder::new(None) + Builder::new(Storage::Memory) } /// Creates a new node with persistent storage + #[cfg(feature = "fs-store")] pub fn persistent(path: impl AsRef) -> Builder { - let path = Some(path.as_ref().to_owned()); - Builder::new(path) + Builder::new(Storage::Persistent(path.as_ref().to_owned())) } } impl Builder { /// Spawns the node pub async fn spawn(self) -> anyhow::Result { - let (store, protect_handler) = match self.path { - None => { + let (store, protect_handler) = match self.storage { + Storage::Memory => { let store = iroh_blobs::store::mem::MemStore::new(); ((*store).clone(), None) } - Some(ref path) => { + #[cfg(feature = "fs-store")] + Storage::Persistent(ref path) => { let db_path = path.join("blobs.db"); - let mut opts = Options::new(path); + let mut opts = iroh_blobs::store::fs::options::Options::new(path); let protect_handler = if let Some(interval) = self.gc_interval { let (handler, cb) = ProtectCallbackHandler::new(); opts.gc = Some(GcConfig {