diff --git a/.editorconfig b/.editorconfig index 32a1e27..1a5baf5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,9 +1,9 @@ root = true -[*] +[*.rs] charset = utf-8 end_of_line = lf indent_size = 4 indent_style = space insert_final_newline = true -trim_trailing_whitespace = true \ No newline at end of file +trim_trailing_whitespace = true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4140289..5f650f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,118 +8,166 @@ jobs: fail-fast: false matrix: toolchain: - - rust: stable + - rust: stable #- rust: nightly platform: - - target: x86_64-unknown-linux-gnu - host: ubuntu-latest - cross: false - - - target: x86_64-apple-darwin - host: macos-latest - cross: false - - - target: x86_64-pc-windows-msvc - host: windows-latest - cross: false - - - target: armv7-linux-androideabi - host: ubuntu-latest - cross: true - - target: aarch64-linux-android - host: ubuntu-latest - cross: true - - - target: aarch64-apple-ios - host: macos-latest - cross: true - - # - target: wasm32-unknown-unknown - # host: ubuntu-latest - # cross: true + - target: x86_64-unknown-linux-gnu + host: ubuntu-latest + cross: false + + - target: x86_64-apple-darwin + host: macos-latest + cross: false + + - target: x86_64-pc-windows-msvc + host: windows-latest + cross: false + + - target: armv7-linux-androideabi + host: ubuntu-latest + cross: true + - target: aarch64-linux-android + host: ubuntu-latest + cross: true + + - target: aarch64-apple-ios + host: macos-latest + cross: true + + # - target: wasm32-unknown-unknown + # host: ubuntu-latest + # cross: true env: RUST_BACKTRACE: 1 + RUST_LOG: netsim_embed_machine=debug,info CARGO_INCREMENTAL: 0 LLVM_CONFIG_PATH: /usr/local/opt/llvm/bin/llvm-config NDK_HOME: /usr/local/lib/android/sdk/ndk-bundle runs-on: ${{ matrix.platform.host }} steps: - - name: Checkout sources - uses: actions/checkout@v2 - - - name: Cache cargo folder - uses: actions/cache@v1 - with: - path: ~/.cargo - key: ${{ matrix.platform.target }}-cargo-${{ matrix.toolchain.rust }} - - - name: Install dependencies ubuntu - if: matrix.platform.host == 'ubuntu-latest' - run: sudo apt-get install llvm-dev - - - name: Install dependencies macos - if: matrix.platform.host == 'macos-latest' - run: brew install llvm - - - name: Install dependencies windows - if: matrix.platform.host == 'windows-latest' - run: choco install llvm - - - name: Install rust toolchain - uses: hecrj/setup-rust-action@v1 - with: - rust-version: ${{ matrix.toolchain.rust }} - targets: ${{ matrix.platform.target }} - - - name: Install cargo-apk - if: contains(matrix.platform.target, 'android') - run: cargo install cargo-apk - - - name: Build - if: contains(matrix.platform.target, 'android') == false - run: cargo build --all-features --target ${{ matrix.platform.target }} - - - name: Build android - if: contains(matrix.platform.target, 'android') - run: cargo apk check --all-features --target ${{ matrix.platform.target }} - - - name: Rust tests - if: matrix.platform.cross == false - run: cargo test --all-features - - - name: Build netsim integration tests - if: contains(matrix.platform.target, 'linux-gnu') - run: cargo build -p ipfs-embed-cli -p ipfs-embed-harness --release - - - name: Run bitswap integration test - if: contains(matrix.platform.target, 'linux-gnu') - run: ./target/release/bitswap - - - name: Run sim-open integration test - if: contains(matrix.platform.target, 'linux-gnu') - run: ./target/release/sim_open + - name: Checkout sources + uses: actions/checkout@v3 + + - name: Cache cargo folder + uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ matrix.platform.target }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Install Protoc + uses: arduino/setup-protoc@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Install dependencies ubuntu + if: matrix.platform.host == 'ubuntu-latest' + run: sudo apt-get install llvm-dev + + - name: Install dependencies macos + if: matrix.platform.host == 'macos-latest' + run: brew install llvm + + - name: Install dependencies windows + if: matrix.platform.host == 'windows-latest' + run: choco install llvm + + - name: Install rust toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.toolchain.rust }} + target: ${{ matrix.platform.target }} + + - name: Install cargo-apk + if: contains(matrix.platform.target, 'android') + uses: baptiste0928/cargo-install@bf6758885262d0e6f61089a9d8c8790d3ac3368f # v1.3.0 + with: + crate: cargo-apk + + - name: Build + if: contains(matrix.platform.target, 'android') == false + uses: actions-rs/cargo@v1 + with: + command: build + args: --target ${{ matrix.platform.target }} + + - name: Build Tokio + if: contains(matrix.platform.target, 'android') == false + uses: actions-rs/cargo@v1 + with: + command: build + args: --target ${{ matrix.platform.target }} --no-default-features --features tokio + + - name: Build android + if: contains(matrix.platform.target, 'android') + uses: actions-rs/cargo@v1 + with: + command: apk + args: -- build --target ${{ matrix.platform.target }} -p ipfs-embed + + - name: Rust tests + if: matrix.platform.cross == false + uses: actions-rs/cargo@v1 + with: + command: test + + - name: Build netsim integration tests + if: contains(matrix.platform.target, 'linux-gnu') + uses: actions-rs/cargo@v1 + with: + command: build + args: -p ipfs-embed-cli -p harness --release + + - name: Run netsim integration tests + if: contains(matrix.platform.target, 'linux-gnu') + uses: actions-rs/cargo@v1 + with: + command: test + args: --release -p harness lint-rust: runs-on: ubuntu-latest steps: - - name: Checkout sources - uses: actions/checkout@v2 - - - name: Cache cargo folder - uses: actions/cache@v1 - with: - path: ~/.cargo - key: lint-cargo - - - name: Install rust toolchain - uses: hecrj/setup-rust-action@v1 - with: - rust-version: stable - components: clippy, rustfmt - - - name: cargo fmt - run: cargo fmt --all -- --check - - - name: cargo clippy - run: cargo clippy --workspace --examples --tests --all-features -- -D warnings + - name: Checkout sources + uses: actions/checkout@v3 + + - name: Cache cargo folder + uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Install rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + components: clippy, rustfmt + + - name: Install Protoc + uses: arduino/setup-protoc@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: cargo fmt + uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check + + - name: cargo clippy + uses: actions-rs/cargo@v1 + with: + command: clippy + args: --workspace --examples --tests -- -D warnings diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..0a0dacc --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,6 @@ +edition = "2018" +error_on_line_overflow = true +error_on_unformatted = true +unstable_features = true +wrap_comments = true +imports_granularity = "Crate" diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..35578aa --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,38 @@ +# Changelog + +This changelog was begun after 0.22. + +## Release 0.26 + +- update to libp2p 0.50 +- mitigate issue with sim-open redials happening too soon after the error, leading to `EADDRNOTAVAIL` +- various CI-related fixes to get github actions green again + +## Release 0.25.1 + +- use patch libp2p-yamux 0.41.1 +- fix display of error messages + +## Release 0.25 + +- offer `keep_alive` config option to keep all explicitly dialled or incoming connections open indefinitely + +## Release 0.24 + +- update to libp2p 0.49 +- restructure network behaviour to hold all state in the polling task +- send all external commands to that task, i.e. everything is async now + +## Release 0.23 + +- update to libp2p 0.43 +- make PortReuse configurable (and recommend to turn it off) +- update to ipfs-sqlite-block-store 0.10 and thereby rusqlite 0.26 +- rewrite address book logic to validate addresses and retain only confirmed ones: + - every successful outgoing connection counts as confirmation + - every outgoing dial failure removes unconfirmed addresses (or confirmed, if PeerId changed) + - all discovered addresses from MDNS and Kademlia are validated by dialling + - all incoming connections’ remote addresses are translated to likely listen addresses using IdentifyInfo +- offer detailed peer information and error history in PeerInfo +- forward all connection-related swarm events +- allow DNS fallback configuration in case system config parsing fails diff --git a/Cargo.lock b/Cargo.lock index 1d4997a..87baebd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aead" version = "0.3.2" @@ -18,6 +33,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" dependencies = [ "generic-array", + "rand_core 0.6.4", ] [[package]] @@ -93,31 +109,31 @@ dependencies = [ [[package]] name = "ahash" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.3", + "getrandom 0.2.7", "once_cell", "version_check", ] [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" dependencies = [ "memchr", ] [[package]] -name = "ansi_term" -version = "0.11.0" +name = "android_system_properties" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ - "winapi", + "libc", ] [[package]] @@ -131,9 +147,18 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.44" +version = "1.0.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" +dependencies = [ + "backtrace", +] + +[[package]] +name = "arc-swap" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1" +checksum = "983cd8b9d4b02a6dc6ffa557262eb5858a27a0038ffffe21a0f133eaa819a164" [[package]] name = "arrayref" @@ -149,15 +174,96 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "arrayvec" -version = "0.7.1" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "asn1-rs" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ff05a702273012438132f449575dbc804e27b2f3cbe3069aa237d26c98fa33" +dependencies = [ + "asn1-rs-derive 0.1.0", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time 0.3.17", +] + +[[package]] +name = "asn1-rs" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf6690c370453db30743b373a60ba498fc0d6d83b11f4abfd87a84a075db5dd4" +dependencies = [ + "asn1-rs-derive 0.4.0", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time 0.3.17", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8b7511298d5b7784b40b092d9e9dcd3a627a5707e4b5e507931ab0d44eeebf" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4dc07131ffa69b8072d35f5007352af944213cde02545e2103680baed38fcd" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "asn1_der" -version = "0.7.4" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22d1f4b888c298a027c99dc9048015fac177587de20fc30232a057dfbe24a21" + +[[package]] +name = "assert_cmd" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6e24d2cce90c53b948c46271bfb053e4bdc2db9b5d3f65e20f8cf28a1b7fc3" +checksum = "93ae1ddd39efd67689deb1979d80bad3bf7f2b09c6e6117c8d1f2443b5e2f83e" +dependencies = [ + "bstr", + "doc-comment", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] [[package]] name = "async-attributes" @@ -171,9 +277,9 @@ dependencies = [ [[package]] name = "async-channel" -version = "1.6.1" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" +checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" dependencies = [ "concurrent-queue", "event-listener", @@ -204,46 +310,56 @@ dependencies = [ "slab", ] +[[package]] +name = "async-fs" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" +dependencies = [ + "async-lock", + "autocfg", + "blocking", + "futures-lite", +] + [[package]] name = "async-global-executor" -version = "2.0.2" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9586ec52317f36de58453159d48351bc244bc24ced3effc1fce22f3d48664af6" +checksum = "0da5b41ee986eed3f524c380e6d64965aea573882a8907682ad100f7859305ca" dependencies = [ "async-channel", "async-executor", "async-io", - "async-mutex", + "async-lock", "blocking", "futures-lite", - "num_cpus", "once_cell", ] [[package]] name = "async-h1" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5142de15b549749cce62923a50714b0d7b77f5090ced141599e78899865451" +checksum = "8101020758a4fc3a7c326cb42aa99e9fa77cbfb76987c128ad956406fe1f70a7" dependencies = [ "async-channel", "async-dup", "async-std", - "byte-pool", "futures-core", "http-types", "httparse", - "lazy_static", "log", - "pin-project 1.0.8", + "pin-project", ] [[package]] name = "async-io" -version = "1.6.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b" +checksum = "83e21f3a490c72b3b0cf44962180e60045de2925d8dff97918f7ee43c8f637c7" dependencies = [ + "autocfg", "concurrent-queue", "futures-lite", "libc", @@ -252,36 +368,40 @@ dependencies = [ "parking", "polling", "slab", - "socket2 0.4.1", + "socket2", "waker-fn", "winapi", ] [[package]] name = "async-lock" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6a8ea61bf9947a1007c5cada31e647dbc77b103c679858150003ba697ea798b" +checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6" dependencies = [ "event-listener", ] [[package]] -name = "async-mutex" -version = "1.4.0" +name = "async-net" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" +checksum = "4051e67316bc7eff608fe723df5d32ed639946adcd69e07df41fd42a7b411f1f" dependencies = [ - "event-listener", + "async-io", + "autocfg", + "blocking", + "futures-lite", ] [[package]] name = "async-process" -version = "1.2.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b21b63ab5a0db0369deb913540af2892750e42d949faacc7a61495ac418a1692" +checksum = "02111fd8655a613c25069ea89fc8d9bb89331fa77486eb3bc059ee757cfa481c" dependencies = [ "async-io", + "autocfg", "blocking", "cfg-if 1.0.0", "event-listener", @@ -303,14 +423,14 @@ dependencies = [ "async-trait", "base64 0.12.3", "bincode", - "blake3", + "blake3 0.3.8", "chrono", "hmac 0.8.1", "kv-log-macro", "rand 0.7.3", "serde", "serde_json", - "sha2", + "sha2 0.9.9", ] [[package]] @@ -329,9 +449,9 @@ dependencies = [ [[package]] name = "async-std" -version = "1.10.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8056f1455169ab86dd47b47391e4ab0cbd25410a70e9fe675544f49bafaf952" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" dependencies = [ "async-attributes", "async-channel", @@ -348,9 +468,8 @@ dependencies = [ "kv-log-macro", "log", "memchr", - "num_cpus", "once_cell", - "pin-project-lite 0.2.7", + "pin-project-lite 0.2.9", "pin-utils", "slab", "wasm-bindgen-futures", @@ -358,29 +477,30 @@ dependencies = [ [[package]] name = "async-std-resolver" -version = "0.20.3" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed4e2c3da14d8ad45acb1e3191db7a918e9505b6f155b218e70a7c9a1a48c638" +checksum = "6ba50e24d9ee0a8950d3d03fc6d0dd10aa14b5de3b101949b4e160f7fee7c723" dependencies = [ "async-std", "async-trait", "futures-io", "futures-util", "pin-utils", + "socket2", "trust-dns-resolver", ] [[package]] name = "async-task" -version = "4.0.3" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" +checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" [[package]] name = "async-trait" -version = "0.1.51" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44318e776df68115a881de9a8fd1b9e53368d7a4a5ce4cc48517da3393233a5e" +checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" dependencies = [ "proc-macro2", "quote", @@ -389,24 +509,15 @@ dependencies = [ [[package]] name = "asynchronous-codec" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0de5164e5edbf51c45fb8c2d9664ae1c095cce1b265ecf7569093c0d66ef690" +checksum = "06a0daa378f5fd10634e44b0a29b2a87b890657658e072a30d6f26e57ddee182" dependencies = [ "bytes", "futures-sink", "futures-util", "memchr", - "pin-project-lite 0.2.7", -] - -[[package]] -name = "atomic" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3410529e8288c463bedb5930f82833bc0c90e5d2fe639a56582a4d09220b281" -dependencies = [ - "autocfg", + "pin-project-lite 0.2.9", ] [[package]] @@ -428,26 +539,36 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] -name = "bao" -version = "0.11.0" +name = "backtrace" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0459c2bb916c8dd306cdb368bd09ff245763b9d6be6ccc7e2b8a3975eda3c9c" +checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" dependencies = [ - "arrayref", - "arrayvec 0.7.1", - "blake3", + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", ] [[package]] name = "base-x" -version = "0.2.8" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + +[[package]] +name = "base16ct" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" [[package]] name = "base64" @@ -461,6 +582,12 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "base64ct" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" + [[package]] name = "bincode" version = "1.3.3" @@ -476,33 +603,13 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "blake-streams-core" -version = "0.1.1" -source = "git+https://github.com/Actyx/blake-streams?branch=sim-open#038f0c8e6d438b812e775cab6669dec8538c7916" -dependencies = [ - "anyhow", - "bao", - "base64 0.13.0", - "ed25519-dalek", - "fnv", - "getrandom 0.2.3", - "parking_lot", - "rkyv", - "sled", - "tracing", - "zerocopy", -] - [[package]] name = "blake2" -version = "0.9.2" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4e37d16930f5459780f5621038b6382b9bb37c19016f39fb6b5808d831f174" +checksum = "b9cf849ee05b2ee5fba5e36f97ff8ec2533916700fc0758d40d92136a42f3388" dependencies = [ - "crypto-mac 0.8.0", - "digest", - "opaque-debug", + "digest 0.10.5", ] [[package]] @@ -517,7 +624,20 @@ dependencies = [ "cfg-if 0.1.10", "constant_time_eq", "crypto-mac 0.8.0", - "digest", + "digest 0.9.0", +] + +[[package]] +name = "blake3" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08e53fc5a564bb15bfe6fae56bd71522205f1f91893f9c0116edad6496c183f" +dependencies = [ + "arrayref", + "arrayvec 0.7.2", + "cc", + "cfg-if 1.0.0", + "constant_time_eq", ] [[package]] @@ -526,10 +646,28 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "block-padding", "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-modes" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a0e8073e8baa88212fb5823574c02ebccb395136ba9a164ab89379ec6072f0" +dependencies = [ + "block-padding", + "cipher 0.2.5", +] + [[package]] name = "block-padding" version = "0.2.1" @@ -538,9 +676,9 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" [[package]] name = "blocking" -version = "1.0.2" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e170dbede1f740736619b776d7251cb1b9095c435c34d8ca9f57fcd2f335e9" +checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" dependencies = [ "async-channel", "async-task", @@ -557,41 +695,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] -name = "bumpalo" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" - -[[package]] -name = "byte-pool" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c7230ddbb427b1094d477d821a99f3f54d36333178eeb806e279bcdcecf0ca" -dependencies = [ - "crossbeam-queue", - "stable_deref_trait", -] - -[[package]] -name = "bytecheck" -version = "0.6.5" +name = "bstr" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb738a1e65989ecdcd5bba16079641bd7209688fa546e1064832fd6e012fd32a" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" dependencies = [ - "bytecheck_derive", - "ptr_meta", + "lazy_static", + "memchr", + "regex-automata", ] [[package]] -name = "bytecheck_derive" -version = "0.6.5" +name = "bumpalo" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3b4dff26fdc9f847dab475c9fec16f2cba82d5aa1f09981b87c44520721e10a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" [[package]] name = "byteorder" @@ -601,31 +719,42 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" [[package]] name = "cache-padded" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" +checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" [[package]] name = "cached" -version = "0.23.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e2afe73808fbaac302e39c9754bfc3c4b4d0f99c9c240b9f4e4efc841ad1b74" +checksum = "af4dfac631a8e77b2f327f7852bb6172771f5279c4512efe79fad6067b37be3d" dependencies = [ - "hashbrown 0.9.1", + "hashbrown 0.11.2", "once_cell", ] [[package]] name = "cc" -version = "1.0.70" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "ccm" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0" +checksum = "5aca1a8fbc20b50ac9673ff014abfb2b5f4085ee1a850d408f14a159c5853ac7" +dependencies = [ + "aead 0.3.2", + "cipher 0.2.5", + "subtle", +] [[package]] name = "cfg-if" @@ -641,9 +770,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chacha20" -version = "0.7.3" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f08493fa7707effc63254c66c6ea908675912493cd67952eda23c09fae2610b1" +checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6" dependencies = [ "cfg-if 1.0.0", "cipher 0.3.0", @@ -653,9 +782,9 @@ dependencies = [ [[package]] name = "chacha20poly1305" -version = "0.8.2" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6547abe025f4027edacd9edaa357aded014eecec42a5070d9b885c3c334aba2" +checksum = "a18446b09be63d457bbec447509e85f662f32952b035ce892290396bc0b0cff5" dependencies = [ "aead 0.4.3", "chacha20", @@ -666,26 +795,30 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.19" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" dependencies = [ - "libc", + "iana-time-zone", + "js-sys", "num-integer", "num-traits", "serde", - "time 0.1.43", + "time 0.1.44", + "wasm-bindgen", "winapi", ] [[package]] name = "cid" -version = "0.7.0" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6b8976b33648136e969aafa6eb33d58ff0d301fa0b4e8d513db58fd32cd81aa" +checksum = "f6ed9c8b2d17acb8110c46f1da5bf4a696d745e1474a16db0cd2b49cd0249bf2" dependencies = [ + "core2", "multibase", "multihash", + "serde", "unsigned-varint", ] @@ -707,120 +840,201 @@ dependencies = [ "generic-array", ] +[[package]] +name = "cipher" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1873270f8f7942c191139cb8a40fd228da6c3fd2fc376d7e92d47aa14aeb59e" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "clap" -version = "2.33.3" +version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ - "ansi_term 0.11.0", + "ansi_term", "atty", "bitflags", - "strsim", - "textwrap", + "strsim 0.8.0", + "textwrap 0.11.0", "unicode-width", "vec_map", ] [[package]] -name = "concurrent-queue" -version = "1.2.2" +name = "clap" +version = "3.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" +checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" dependencies = [ - "cache-padded", + "atty", + "bitflags", + "clap_derive", + "clap_lex", + "indexmap", + "once_cell", + "strsim 0.10.0", + "termcolor", + "textwrap 0.15.1", ] [[package]] -name = "const_fn" -version = "0.4.8" +name = "clap_derive" +version = "3.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92cfa0fd5690b3cf8c1ef2cabbd9b7ef22fa53cf5e1f92b05103f6d5d1cf6e7" +checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +dependencies = [ + "heck 0.4.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] [[package]] -name = "constant_time_eq" -version = "0.1.5" +name = "clap_lex" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] [[package]] -name = "convert_case" -version = "0.4.0" +name = "cmake" +version = "0.1.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +checksum = "e8ad8cef104ac57b68b89df3208164d228503abbdce70f6880ffa3d970e7443a" +dependencies = [ + "cc", +] [[package]] -name = "cookie" -version = "0.14.4" +name = "codespan-reporting" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a5d7b21829bc7b4bf4754a978a241ae54ea55a40f92bb20216e54096f4b951" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" dependencies = [ - "aes-gcm 0.8.0", - "base64 0.13.0", - "hkdf", - "hmac 0.10.1", + "termcolor", + "unicode-width", +] + +[[package]] +name = "concurrent-queue" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" +dependencies = [ + "cache-padded", +] + +[[package]] +name = "const-oid" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "722e23542a15cea1f65d4a1419c4cfd7a26706c70871a13a04238ca3f40f1661" + +[[package]] +name = "const_fn" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a5d7b21829bc7b4bf4754a978a241ae54ea55a40f92bb20216e54096f4b951" +dependencies = [ + "aes-gcm 0.8.0", + "base64 0.13.0", + "hkdf 0.10.0", + "hmac 0.10.1", "percent-encoding", - "rand 0.8.4", - "sha2", + "rand 0.8.5", + "sha2 0.9.9", "time 0.2.27", "version_check", ] [[package]] -name = "cpufeatures" -version = "0.2.1" +name = "core-foundation" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" dependencies = [ + "core-foundation-sys", "libc", ] [[package]] -name = "cpuid-bool" -version = "0.2.0" +name = "core-foundation-sys" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] -name = "crc32fast" -version = "1.2.1" +name = "core2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" dependencies = [ - "cfg-if 1.0.0", + "memchr", ] [[package]] -name = "crossbeam-epoch" -version = "0.9.5" +name = "cpufeatures" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils", - "lazy_static", - "memoffset", - "scopeguard", + "libc", ] [[package]] -name = "crossbeam-queue" -version = "0.3.2" +name = "cpuid-bool" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" + +[[package]] +name = "crc" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9" +checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3" dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils", + "crc-catalog", ] +[[package]] +name = "crc-catalog" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d0165d2900ae6778e36e80bbc4da3b5eefccee9ba939761f9c2882a5d9af3ff" + [[package]] name = "crossbeam-utils" -version = "0.8.5" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" +checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" dependencies = [ "cfg-if 1.0.0", - "lazy_static", ] [[package]] @@ -829,6 +1043,28 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "crypto-mac" version = "0.8.0" @@ -849,11 +1085,21 @@ dependencies = [ "subtle", ] +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array", + "subtle", +] + [[package]] name = "ctor" -version = "0.1.21" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa" +checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb" dependencies = [ "quote", "syn", @@ -884,20 +1130,102 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" dependencies = [ "byteorder", - "digest", + "digest 0.9.0", "rand_core 0.5.1", "subtle", "zeroize", ] [[package]] -name = "dashmap" -version = "4.0.2" +name = "curve25519-dalek" +version = "4.0.0-pre.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" +checksum = "4033478fbf70d6acf2655ac70da91ee65852d69daf7a67bf7a2f518fb47aafcf" dependencies = [ - "cfg-if 1.0.0", - "num_cpus", + "byteorder", + "digest 0.9.0", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "cxx" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f83d0ebf42c6eafb8d7c52f7e5f2d3003b89c7aa4fd2b79229209459a849af8" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07d050484b55975889284352b0ffc2ecbda25c0c55978017c132b29ba0818a86" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d2199b00553eda8012dfec8d3b1c75fce747cf27c169a270b3b99e3448ab78" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb67a6de1f602736dd7eaead0080cf3435df806c61b24b13328db128c58868f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "darling" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e" +dependencies = [ + "darling_core", + "quote", + "syn", ] [[package]] @@ -926,19 +1254,95 @@ dependencies = [ "syn", ] +[[package]] +name = "der" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dd2ae565c0a381dde7fade45fce95984c568bdcb4700a4fdbe3175e0380b2f" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der-parser" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe398ac75057914d7d07307bf67dc7f3f574a26783b4fc7805a20ffa9f506e82" +dependencies = [ + "asn1-rs 0.3.1", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "der-parser" +version = "8.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d4bc9b0db0a0df9ae64634ac5bdefb7afcb534e182275ca0beadbe486701c1" +dependencies = [ + "asn1-rs 0.5.1", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "derive_builder" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07adf7be193b71cc36b193d0f5fe60b918a3a9db4dad0449f57bcfd519704a3" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_builder_macro" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68" +dependencies = [ + "derive_builder_core", + "syn", +] + [[package]] name = "derive_more" -version = "0.99.16" +version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40eebddd2156ce1bb37b20bbe5151340a31828b1f2d22ba4141f3531710e38df" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", "proc-macro2", "quote", - "rustc_version 0.3.3", + "rustc_version 0.4.0", "syn", ] +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + [[package]] name = "digest" version = "0.9.0" @@ -948,6 +1352,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "digest" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" +dependencies = [ + "block-buffer 0.10.3", + "crypto-common", + "subtle", +] + [[package]] name = "discard" version = "1.0.4" @@ -955,26 +1370,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" [[package]] -name = "dns-parser" -version = "0.8.0" +name = "displaydoc" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4d33be9473d06f75f58220f71f7a9317aca647dc061dbd3c361b0bef505fbea" +checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" dependencies = [ - "byteorder", - "quick-error", + "proc-macro2", + "quote", + "syn", ] +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "dtoa" -version = "0.4.8" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" +checksum = "f8a6eee2d5d0d113f015688310da018bd1d864d86bd567c8fca9c266889e1bfa" + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", +] [[package]] name = "ed25519" -version = "1.2.0" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4620d40f6d2601794401d6dd95a5cf69b6c157852539470eeda433a99b3c0efc" +checksum = "1e9c280362032ea4203659fc489832d0204ef09f247a0506f170dafcac08c369" dependencies = [ "signature", ] @@ -985,37 +1419,80 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ - "curve25519-dalek", + "curve25519-dalek 3.2.0", "ed25519", "rand 0.7.3", "serde", - "sha2", + "sha2 0.9.9", "zeroize", ] [[package]] name = "either" -version = "1.6.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint", + "der", + "digest 0.10.5", + "ff", + "generic-array", + "group", + "hkdf 0.12.3", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] [[package]] name = "enum-as-inner" -version = "0.3.3" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c5f0096a91d210159eceb2ff5e1c4da18388a170e1e3ce948aac9c8fdbbf595" +checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" dependencies = [ - "heck", + "heck 0.4.0", "proc-macro2", "quote", "syn", ] +[[package]] +name = "erased-serde" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54558e0ba96fbe24280072642eceb9d7d442e32c7ec0ea9e7ecd7b4ea2cf4e11" +dependencies = [ + "serde", +] + +[[package]] +name = "escargot" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5584ba17d7ab26a8a7284f13e5bd196294dd2f2d79773cff29b9e9edef601a6" +dependencies = [ + "log", + "once_cell", + "serde", + "serde_json", +] + [[package]] name = "event-listener" -version = "2.5.1" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "fallible-iterator" @@ -1031,20 +1508,20 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "fastrand" -version = "1.5.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b394ed3d285a429378d3b384b9eb1285267e7df4b166df24b7a6939a04dc392e" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" dependencies = [ "instant", ] [[package]] name = "femme" -version = "2.1.1" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af1a24f391a5a94d756db5092c6576aad494b88a71a5a36b20c67b63e0df034" +checksum = "cc04871e5ae3aa2952d552dae6b291b3099723bf779a8054281c1366a54613ef" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "js-sys", "log", "serde", @@ -1054,11 +1531,30 @@ dependencies = [ "web-sys", ] +[[package]] +name = "ff" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df689201f395c6b90dfe87127685f8dbfc083a5e779e613575d8bd7314300c3e" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "fixedbitset" -version = "0.2.0" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "float-cmp" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] [[package]] name = "fnv" @@ -1068,24 +1564,13 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" dependencies = [ - "matches", "percent-encoding", ] -[[package]] -name = "fs2" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -1094,9 +1579,9 @@ checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "futures" -version = "0.3.17" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12aa0eb539080d55c3f2d45a67c3b58b6b0773c1a3ca2dfec66d58c97fd66ca" +checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" dependencies = [ "futures-channel", "futures-core", @@ -1109,9 +1594,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.17" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888" +checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" dependencies = [ "futures-core", "futures-sink", @@ -1119,15 +1604,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.17" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" +checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" [[package]] name = "futures-executor" -version = "0.3.17" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45025be030969d763025784f7f355043dc6bc74093e4ecc5000ca4dc50d8745c" +checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" dependencies = [ "futures-core", "futures-task", @@ -1137,9 +1622,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.17" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377" +checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" [[package]] name = "futures-lite" @@ -1152,34 +1637,43 @@ dependencies = [ "futures-io", "memchr", "parking", - "pin-project-lite 0.2.7", + "pin-project-lite 0.2.9", "waker-fn", ] [[package]] name = "futures-macro" -version = "0.3.17" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb" +checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" dependencies = [ - "autocfg", - "proc-macro-hack", "proc-macro2", "quote", "syn", ] +[[package]] +name = "futures-rustls" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2411eed028cdf8c8034eaf21f9915f956b6c3abec4d4c7949ee67f0721127bd" +dependencies = [ + "futures-io", + "rustls 0.20.7", + "webpki 0.22.0", +] + [[package]] name = "futures-sink" -version = "0.3.17" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11" +checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" [[package]] name = "futures-task" -version = "0.3.17" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99" +checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" [[package]] name = "futures-timer" @@ -1189,11 +1683,10 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" -version = "0.3.17" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" +checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" dependencies = [ - "autocfg", "futures-channel", "futures-core", "futures-io", @@ -1201,27 +1694,16 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.7", + "pin-project-lite 0.2.9", "pin-utils", - "proc-macro-hack", - "proc-macro-nested", "slab", ] -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - [[package]] name = "generic-array" -version = "0.14.4" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ "typenum", "version_check", @@ -1240,13 +1722,13 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.3" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi 0.10.2+wasi-snapshot-preview1", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] @@ -1269,24 +1751,59 @@ dependencies = [ "polyval 0.5.3", ] +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" + [[package]] name = "gloo-timers" -version = "0.2.1" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f" +checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9" dependencies = [ "futures-channel", "futures-core", "js-sys", "wasm-bindgen", - "web-sys", ] [[package]] -name = "hashbrown" -version = "0.9.1" +name = "group" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "harness" +version = "0.1.0" +dependencies = [ + "anyhow", + "assert_cmd", + "async-global-executor", + "async-process", + "async-std", + "escargot", + "futures", + "ipfs-embed-cli", + "libipld", + "libp2p", + "maplit", + "multihash", + "netsim-embed", + "predicates", + "rand 0.8.5", + "structopt", + "tempdir", + "tracing", + "tracing-subscriber", +] [[package]] name = "hashbrown" @@ -1297,6 +1814,15 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + [[package]] name = "hashlink" version = "0.7.0" @@ -1315,6 +1841,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -1342,10 +1874,19 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f" dependencies = [ - "digest", + "digest 0.9.0", "hmac 0.10.1", ] +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac 0.12.1", +] + [[package]] name = "hmac" version = "0.8.1" @@ -1353,7 +1894,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" dependencies = [ "crypto-mac 0.8.0", - "digest", + "digest 0.9.0", ] [[package]] @@ -1363,7 +1904,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" dependencies = [ "crypto-mac 0.10.1", - "digest", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac 0.11.1", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.5", ] [[package]] @@ -1372,7 +1932,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" dependencies = [ - "digest", + "digest 0.9.0", "generic-array", "hmac 0.8.1", ] @@ -1390,13 +1950,12 @@ dependencies = [ [[package]] name = "http-client" -version = "6.5.1" +version = "6.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea880b03c18a7e981d7fb3608b8904a98425d53c440758fcebf7d934aa56547c" +checksum = "1947510dc91e2bf586ea5ffb412caad7673264e14bb39fb9078da114a94ce1a5" dependencies = [ "async-trait", "cfg-if 1.0.0", - "dashmap", "http-types", "log", ] @@ -1414,7 +1973,7 @@ dependencies = [ "cookie", "futures-lite", "infer", - "pin-project-lite 0.2.7", + "pin-project-lite 0.2.9", "rand 0.7.3", "serde", "serde_json", @@ -1425,9 +1984,39 @@ dependencies = [ [[package]] name = "httparse" -version = "1.5.1" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "iana-time-zone" +version = "0.1.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5a6ef98976b22b3b7f2f3a806f858cb862044cfa66805aa3ad84cb3d3b785ed" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" @@ -1441,50 +2030,53 @@ dependencies = [ ] [[package]] -name = "if-addrs" -version = "0.6.6" +name = "idna" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9a83ec4af652890ac713ffd8dc859e650420a5ef47f7b9be29b6664ab50fbc8" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" dependencies = [ - "if-addrs-sys", - "libc", - "winapi", + "unicode-bidi", + "unicode-normalization", ] [[package]] -name = "if-addrs-sys" -version = "0.3.2" +name = "if-addrs" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de74b9dd780476e837e5eb5ab7c88b49ed304126e412030a0adba99c8efe79ea" +checksum = "cbc0fa01ffc752e9dbc72818cdb072cd028b86be5e09dd04c5a643704fe101a9" dependencies = [ - "cc", "libc", + "winapi", ] [[package]] name = "if-watch" -version = "0.2.2" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8ab7f67bad3240049cb24fb9cb0b4c2c6af4c245840917fbbdededeee91179" +checksum = "ba7abdbb86e485125dad06c2691e1e393bf3b08c7b743b43aa162a00fd39062e" dependencies = [ "async-io", + "core-foundation", + "fnv", "futures", - "futures-lite", "if-addrs", "ipnet", - "libc", "log", - "winapi", + "rtnetlink", + "smol", + "system-configuration", + "tokio", + "windows", ] [[package]] name = "indexmap" -version = "1.7.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ "autocfg", - "hashbrown 0.11.2", + "hashbrown 0.12.3", ] [[package]] @@ -1493,15 +2085,43 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64e9829a50b42bb782c1df523f78d332fe371b10c661e78b7a3c34b0198e9fac" +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + [[package]] name = "instant" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "interceptor" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ffaa4d24f546a18eaeee91f7b2c52e080e20e285e43cd7c27a527b4712cfdad" +dependencies = [ + "async-trait", + "bytes", + "log", + "rand 0.8.5", + "rtcp", + "rtp", + "thiserror", + "tokio", + "waitgroup", + "webrtc-srtp", + "webrtc-util", +] + [[package]] name = "ioctl-sys" version = "0.7.1" @@ -1510,11 +2130,11 @@ checksum = "7f9d0b6b23885487578d10590edc36fd95426257c7017973b20633e34df23b08" [[package]] name = "ipconfig" -version = "0.2.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7" +checksum = "723519edce41262b05d4143ceb95050e4c614f483e78e9fd9e39a8275a84ad98" dependencies = [ - "socket2 0.3.19", + "socket2", "widestring", "winapi", "winreg", @@ -1522,13 +2142,14 @@ dependencies = [ [[package]] name = "ipfs-embed" -version = "0.22.4" +version = "0.26.1" dependencies = [ "anyhow", + "async-executor", "async-global-executor", - "async-io", "async-std", "async-trait", + "chrono", "fnv", "futures", "futures-timer", @@ -1537,21 +2158,21 @@ dependencies = [ "libipld", "libp2p", "libp2p-bitswap", - "libp2p-blake-streams", "libp2p-broadcast", - "libp2p-quic", "multihash", "names", - "parking_lot", - "pin-project 1.0.8", + "parking_lot 0.11.2", + "pin-project", "prometheus", - "rand 0.8.4", + "rand 0.8.5", + "regex", "tempdir", "thiserror", "tide", "tokio", "tracing", "tracing-subscriber", + "trust-dns-resolver", "void", ] @@ -1562,95 +2183,71 @@ dependencies = [ "anyhow", "async-process", "async-std", - "ed25519-dalek", - "ipfs-embed", - "libipld", - "multihash", - "structopt", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "ipfs-embed-harness" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-global-executor", - "async-process", + "chrono", "futures", - "ipfs-embed-cli", + "ipfs-embed", "libipld", - "libp2p", "multihash", - "netsim-embed", - "rand 0.8.4", + "parking_lot 0.11.2", + "serde", + "serde_json", "structopt", - "tempdir", "tracing", "tracing-subscriber", ] [[package]] name = "ipfs-sqlite-block-store" -version = "0.7.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fd994229c56ee401f1ae971e93e1d4873cb089df79d4ba78fe2c71bf460d9ed" +checksum = "9cb925e7e17691fdb4cc5179c010b6a12e78a9588d257f16cbcb8f8503778f97" dependencies = [ "anyhow", "derive_more", "fnv", "futures", + "itertools", "libipld", - "parking_lot", + "parking_lot 0.11.2", "rusqlite", "tracing", ] [[package]] name = "ipnet" -version = "2.3.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" - -[[package]] -name = "itertools" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" -dependencies = [ - "either", -] +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" [[package]] name = "itertools" -version = "0.10.1" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" -version = "0.4.8" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" [[package]] name = "js-sys" -version = "0.3.54" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1866b355d9c878e5e607473cbe3f63282c0b7aad2db1dbebf55076c686918254" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" dependencies = [ "wasm-bindgen", ] [[package]] name = "keccak" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" +checksum = "f9b7d56ba4a8344d6be9729995e6b06f928af29998cdf79fe390cbf6b1fee838" [[package]] name = "kv-log-macro" @@ -1669,15 +2266,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.101" +version = "0.2.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" +checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" [[package]] name = "libipld" -version = "0.12.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373a32b8d77bf13d6d5552b068e55991cc26f6f43edae25409fec5f555494438" +checksum = "ac9c3aa309c260aa2f174bac968901eddc546e9d85950c28eae6a7bec402f926" dependencies = [ "async-trait", "cached", @@ -1689,15 +2286,15 @@ dependencies = [ "libipld-pb", "log", "multihash", - "parking_lot", + "parking_lot 0.12.1", "thiserror", ] [[package]] name = "libipld-cbor" -version = "0.12.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51882ee3a6ebc8b770b507558b8bc993659d7e96e160fb796bc1fc7c290d74c4" +checksum = "8dd1ab68c9d26f20c7d0dfea6eecbae8c00359875210001b33ca27d4a02f3d09" dependencies = [ "byteorder", "libipld-core", @@ -1706,10 +2303,11 @@ dependencies = [ [[package]] name = "libipld-cbor-derive" -version = "0.12.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80cf5eeddac31280a924ac77ad90df98887ccc8c351c324b11d15b620f8cf74f" +checksum = "69ec2f49393a1347a2d95ebcb248ff75d0d47235919b678036c010a8cd927375" dependencies = [ + "proc-macro-crate", "proc-macro2", "quote", "syn", @@ -1718,12 +2316,13 @@ dependencies = [ [[package]] name = "libipld-core" -version = "0.12.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29a8a4ac024d51f15cc7b71d888b51bf3ab5d26a502e3f48fc9df33d7fd02ac" +checksum = "d44790246ec6b7314cba745992c23d479d018073e66d49ae40ae1b64e5dd8eb5" dependencies = [ "anyhow", "cid", + "core2", "multibase", "multihash", "thiserror", @@ -1731,34 +2330,36 @@ dependencies = [ [[package]] name = "libipld-macro" -version = "0.12.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d553f07747f7cb5e62d15de5fb416bf0e22968b2ee685226e91faaffd43a464" +checksum = "852c011562ae5059b67c3a917f9f5945af5a68df8e39ede4444fff33274d25e2" dependencies = [ "libipld-core", ] [[package]] name = "libipld-pb" -version = "0.12.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ddbe8356b2bb59c6875d329b3f28cb323d3c0cd07a2b9804a7008e1856e4727" +checksum = "c003be513496578115256a1b4ac7b80d4ece2462c9869dfb736fd30d8bb1d1c0" dependencies = [ "libipld-core", - "prost 0.7.0", - "prost-build 0.7.0", + "prost 0.10.4", + "prost-build 0.10.4", "thiserror", ] [[package]] name = "libp2p" -version = "0.40.0" -source = "git+https://github.com/Actyx/rust-libp2p?branch=sim-open#9eecb895de76b5d32ddab62945d13efc5d4c6722" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e0a0d2f693675f49ded13c5d510c48b78069e23cbd9108d7ccd59f6dc568819" dependencies = [ - "atomic", "bytes", "futures", - "lazy_static", + "futures-timer", + "getrandom 0.2.7", + "instant", "libp2p-core", "libp2p-dns", "libp2p-gossipsub", @@ -1770,22 +2371,23 @@ dependencies = [ "libp2p-noise", "libp2p-ping", "libp2p-pnet", + "libp2p-quic", "libp2p-request-response", "libp2p-swarm", - "libp2p-swarm-derive", "libp2p-tcp", + "libp2p-webrtc", "libp2p-yamux", "multiaddr", - "parking_lot", - "pin-project 1.0.8", + "parking_lot 0.12.1", + "pin-project", "smallvec", - "wasm-timer", ] [[package]] name = "libp2p-bitswap" -version = "0.19.0" -source = "git+https://github.com/Actyx/libp2p-bitswap?branch=sim-open#a439f1b88eedb57577999911727d1efd97421191" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d1cf3106f9aaed6a0098351e8dd98314ccc276cb847a82b032247f3e328346" dependencies = [ "async-trait", "fnv", @@ -1794,32 +2396,18 @@ dependencies = [ "libipld", "libp2p", "prometheus", - "prost 0.7.0", - "prost-build 0.7.0", + "prost 0.9.0", + "prost-build 0.9.0", "thiserror", "tracing", "unsigned-varint", ] [[package]] -name = "libp2p-blake-streams" -version = "0.1.1" -source = "git+https://github.com/Actyx/blake-streams?branch=sim-open#038f0c8e6d438b812e775cab6669dec8538c7916" -dependencies = [ - "anyhow", - "async-trait", - "blake-streams-core", - "fnv", - "futures", - "libp2p", - "tracing", - "zerocopy", -] - -[[package]] -name = "libp2p-broadcast" -version = "0.5.0" -source = "git+https://github.com/Actyx/libp2p-broadcast?branch=sim-open-2#315c46e6e7a7da2d81958b9e328a982995b43f92" +name = "libp2p-broadcast" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca7e36e129939fff26aecf77c6fd00d61b98df67dc064a4c52b9bbc9775c1e8" dependencies = [ "fnv", "futures", @@ -1828,8 +2416,9 @@ dependencies = [ [[package]] name = "libp2p-core" -version = "0.30.0" -source = "git+https://github.com/Actyx/rust-libp2p?branch=sim-open#9eecb895de76b5d32ddab62945d13efc5d4c6722" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6a8fcd392ff67af6cc3f03b1426c41f7f26b6b9aff2dc632c1c56dd649e571f" dependencies = [ "asn1_der", "bs58", @@ -1838,20 +2427,23 @@ dependencies = [ "fnv", "futures", "futures-timer", - "lazy_static", + "instant", "libsecp256k1", "log", "multiaddr", "multihash", "multistream-select", - "parking_lot", - "pin-project 1.0.8", - "prost 0.8.0", - "prost-build 0.8.0", - "rand 0.7.3", + "once_cell", + "p256", + "parking_lot 0.12.1", + "pin-project", + "prost 0.11.0", + "prost-build 0.11.1", + "rand 0.8.5", "ring", "rw-stream-sink", - "sha2", + "sec1", + "sha2 0.10.6", "smallvec", "thiserror", "unsigned-varint", @@ -1861,21 +2453,24 @@ dependencies = [ [[package]] name = "libp2p-dns" -version = "0.30.0" -source = "git+https://github.com/Actyx/rust-libp2p?branch=sim-open#9eecb895de76b5d32ddab62945d13efc5d4c6722" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e42a271c1b49f789b92f7fc87749fa79ce5c7bdc88cbdfacb818a4bca47fec5" dependencies = [ "async-std-resolver", "futures", "libp2p-core", "log", + "parking_lot 0.12.1", "smallvec", "trust-dns-resolver", ] [[package]] name = "libp2p-gossipsub" -version = "0.33.0" -source = "git+https://github.com/Actyx/rust-libp2p?branch=sim-open#9eecb895de76b5d32ddab62945d13efc5d4c6722" +version = "0.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a173171c71c29bb156f98886c7c4824596de3903dadf01e2e79d2ccdcf38cd9f" dependencies = [ "asynchronous-codec", "base64 0.13.0", @@ -1884,96 +2479,113 @@ dependencies = [ "fnv", "futures", "hex_fmt", + "instant", "libp2p-core", "libp2p-swarm", "log", - "prost 0.8.0", - "prost-build 0.8.0", - "rand 0.7.3", + "prometheus-client", + "prost 0.11.0", + "prost-build 0.11.1", + "prost-codec", + "rand 0.8.5", "regex", - "sha2", + "sha2 0.10.6", "smallvec", + "thiserror", "unsigned-varint", "wasm-timer", ] [[package]] name = "libp2p-identify" -version = "0.31.0" -source = "git+https://github.com/Actyx/rust-libp2p?branch=sim-open#9eecb895de76b5d32ddab62945d13efc5d4c6722" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "647d6a99f8d5b7366ee6bcc608ec186e2fb58b497cf914c8409b803bd0f594a2" dependencies = [ + "asynchronous-codec", "futures", + "futures-timer", "libp2p-core", "libp2p-swarm", "log", - "prost 0.8.0", - "prost-build 0.8.0", + "lru", + "prost 0.11.0", + "prost-build 0.11.1", + "prost-codec", "smallvec", - "wasm-timer", + "thiserror", + "void", ] [[package]] name = "libp2p-kad" -version = "0.32.0" -source = "git+https://github.com/Actyx/rust-libp2p?branch=sim-open#9eecb895de76b5d32ddab62945d13efc5d4c6722" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ee545eedf4f88502b2a4a2323405c3225d212d643212b0615856ca227fb9c3" dependencies = [ - "arrayvec 0.5.2", + "arrayvec 0.7.2", "asynchronous-codec", "bytes", "either", "fnv", "futures", + "futures-timer", + "instant", "libp2p-core", "libp2p-swarm", "log", - "prost 0.8.0", - "prost-build 0.8.0", - "rand 0.7.3", - "sha2", + "prost 0.11.0", + "prost-build 0.11.1", + "rand 0.8.5", + "sha2 0.10.6", "smallvec", + "thiserror", "uint", "unsigned-varint", "void", - "wasm-timer", ] [[package]] name = "libp2p-mdns" -version = "0.32.0" -source = "git+https://github.com/Actyx/rust-libp2p?branch=sim-open#9eecb895de76b5d32ddab62945d13efc5d4c6722" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04f378264aade9872d6ccd315c0accc18be3a35d15fc1b9c36e5b6f983b62b5b" dependencies = [ "async-io", "data-encoding", - "dns-parser", "futures", "if-watch", - "lazy_static", "libp2p-core", "libp2p-swarm", "log", - "rand 0.8.4", + "rand 0.8.5", "smallvec", - "socket2 0.4.1", + "socket2", + "tokio", + "trust-dns-proto", "void", ] [[package]] name = "libp2p-metrics" -version = "0.1.0" -source = "git+https://github.com/Actyx/rust-libp2p?branch=sim-open#9eecb895de76b5d32ddab62945d13efc5d4c6722" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ad8a64f29da86005c86a4d2728b8a0719e9b192f4092b609fd8790acb9dec55" dependencies = [ "libp2p-core", + "libp2p-gossipsub", "libp2p-identify", "libp2p-kad", "libp2p-ping", "libp2p-swarm", - "open-metrics-client", + "prometheus-client", ] [[package]] name = "libp2p-mplex" -version = "0.30.0" -source = "git+https://github.com/Actyx/rust-libp2p?branch=sim-open#9eecb895de76b5d32ddab62945d13efc5d4c6722" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03805b44107aa013e7cbbfa5627b31c36cbedfdfb00603c0311998882bc4bace" dependencies = [ "asynchronous-codec", "bytes", @@ -1981,153 +2593,215 @@ dependencies = [ "libp2p-core", "log", "nohash-hasher", - "parking_lot", - "rand 0.7.3", + "parking_lot 0.12.1", + "rand 0.8.5", "smallvec", "unsigned-varint", ] [[package]] name = "libp2p-noise" -version = "0.33.0" -source = "git+https://github.com/Actyx/rust-libp2p?branch=sim-open#9eecb895de76b5d32ddab62945d13efc5d4c6722" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a978cb57efe82e892ec6f348a536bfbd9fee677adbe5689d7a93ad3a9bffbf2e" dependencies = [ "bytes", - "curve25519-dalek", + "curve25519-dalek 3.2.0", "futures", - "lazy_static", "libp2p-core", "log", - "prost 0.8.0", - "prost-build 0.8.0", - "rand 0.8.4", - "sha2", + "once_cell", + "prost 0.11.0", + "prost-build 0.11.1", + "rand 0.8.5", + "sha2 0.10.6", "snow", "static_assertions", - "x25519-dalek", + "thiserror", + "x25519-dalek 1.1.1", "zeroize", ] [[package]] name = "libp2p-ping" -version = "0.31.0" -source = "git+https://github.com/Actyx/rust-libp2p?branch=sim-open#9eecb895de76b5d32ddab62945d13efc5d4c6722" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "929fcace45a112536e22b3dcfd4db538723ef9c3cb79f672b98be2cc8e25f37f" dependencies = [ "futures", + "futures-timer", + "instant", "libp2p-core", "libp2p-swarm", "log", - "rand 0.7.3", + "rand 0.8.5", "void", - "wasm-timer", ] [[package]] name = "libp2p-pnet" -version = "0.21.0" -source = "git+https://github.com/Actyx/rust-libp2p?branch=sim-open#9eecb895de76b5d32ddab62945d13efc5d4c6722" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de160c5631696cea22be326c19bd9d306e254c4964945263aea10f25f6e0864e" dependencies = [ "futures", "log", - "pin-project 1.0.8", - "rand 0.7.3", + "pin-project", + "rand 0.8.5", "salsa20", "sha3", ] [[package]] name = "libp2p-quic" -version = "0.6.1" -source = "git+https://github.com/Actyx/libp2p-quic?branch=sim-open-2#80bf75972c30c9748108ba3fcec98757ccde99e0" +version = "0.7.0-alpha" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01e7c867e95c8130667b24409d236d37598270e6da69b3baf54213ba31ffca59" dependencies = [ - "anyhow", - "async-global-executor", - "async-io", + "async-std", "bytes", - "ed25519-dalek", - "fnv", "futures", + "futures-timer", "if-watch", - "libp2p", - "multihash", - "parking_lot", - "quinn-noise", + "libp2p-core", + "libp2p-tls", + "log", + "parking_lot 0.12.1", "quinn-proto", - "rand_core 0.5.1", + "rand 0.8.5", + "rustls 0.20.7", "thiserror", - "tracing", - "udp-socket", + "tokio", ] [[package]] name = "libp2p-request-response" -version = "0.13.0" -source = "git+https://github.com/Actyx/rust-libp2p?branch=sim-open#9eecb895de76b5d32ddab62945d13efc5d4c6722" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3236168796727bfcf4927f766393415361e2c644b08bedb6a6b13d957c9a4884" dependencies = [ "async-trait", "bytes", "futures", + "instant", "libp2p-core", "libp2p-swarm", "log", - "lru", - "minicbor", - "rand 0.7.3", + "rand 0.8.5", "smallvec", "unsigned-varint", - "wasm-timer", ] [[package]] name = "libp2p-swarm" -version = "0.31.0" -source = "git+https://github.com/Actyx/rust-libp2p?branch=sim-open#9eecb895de76b5d32ddab62945d13efc5d4c6722" +version = "0.41.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a35472fe3276b3855c00f1c032ea8413615e030256429ad5349cdf67c6e1a0" dependencies = [ + "async-std", "either", + "fnv", "futures", + "futures-timer", + "instant", "libp2p-core", + "libp2p-swarm-derive", "log", - "rand 0.7.3", + "pin-project", + "rand 0.8.5", "smallvec", + "thiserror", + "tokio", "void", - "wasm-timer", ] [[package]] name = "libp2p-swarm-derive" -version = "0.24.0" -source = "git+https://github.com/Actyx/rust-libp2p?branch=sim-open#9eecb895de76b5d32ddab62945d13efc5d4c6722" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d527d5827582abd44a6d80c07ff8b50b4ee238a8979e05998474179e79dc400" dependencies = [ + "heck 0.4.0", "quote", "syn", ] [[package]] name = "libp2p-tcp" -version = "0.30.0" -source = "git+https://github.com/Actyx/rust-libp2p?branch=sim-open#9eecb895de76b5d32ddab62945d13efc5d4c6722" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4b257baf6df8f2df39678b86c578961d48cc8b68642a12f0f763f56c8e5858d" dependencies = [ "async-io", "futures", "futures-timer", - "if-addrs", "if-watch", - "ipnet", "libc", "libp2p-core", "log", - "socket2 0.4.1", + "socket2", + "tokio", +] + +[[package]] +name = "libp2p-tls" +version = "0.1.0-alpha" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7905ce0d040576634e8a3229a7587cc8beab83f79db6023800f1792895defa8" +dependencies = [ + "futures", + "futures-rustls", + "libp2p-core", + "rcgen 0.10.0", + "ring", + "rustls 0.20.7", + "thiserror", + "webpki 0.22.0", + "x509-parser 0.14.0", + "yasna", +] + +[[package]] +name = "libp2p-webrtc" +version = "0.4.0-alpha" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb6cd86dd68cba72308ea05de1cebf3ba0ae6e187c40548167955d4e3970f6a" +dependencies = [ + "async-trait", + "asynchronous-codec", + "bytes", + "futures", + "futures-timer", + "hex", + "if-watch", + "libp2p-core", + "libp2p-noise", + "log", + "multihash", + "prost 0.11.0", + "prost-build 0.11.1", + "prost-codec", + "rand 0.8.5", + "rcgen 0.9.3", + "serde", + "stun", + "thiserror", + "tinytemplate", "tokio", + "tokio-util", + "webrtc", ] [[package]] name = "libp2p-yamux" -version = "0.34.0" -source = "git+https://github.com/Actyx/rust-libp2p?branch=sim-open#9eecb895de76b5d32ddab62945d13efc5d4c6722" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f63594a0aa818642d9d4915c791945053877253f08a3626f13416b5cd928a29" dependencies = [ "futures", "libp2p-core", - "parking_lot", + "log", + "parking_lot 0.12.1", "thiserror", "yamux", ] @@ -2162,95 +2836,106 @@ dependencies = [ [[package]] name = "libsecp256k1" -version = "0.6.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9d220bc1feda2ac231cb78c3d26f27676b8cf82c96971f7aeef3d0cf2797c73" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" dependencies = [ "arrayref", - "base64 0.12.3", - "digest", + "base64 0.13.0", + "digest 0.9.0", "hmac-drbg", "libsecp256k1-core", "libsecp256k1-gen-ecmult", "libsecp256k1-gen-genmult", - "rand 0.7.3", + "rand 0.8.5", "serde", - "sha2", + "sha2 0.9.9", "typenum", ] [[package]] name = "libsecp256k1-core" -version = "0.2.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f6ab710cec28cef759c5f18671a27dae2a5f952cdaaee1d8e2908cb2478a80" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" dependencies = [ "crunchy", - "digest", + "digest 0.9.0", "subtle", ] [[package]] name = "libsecp256k1-gen-ecmult" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccab96b584d38fac86a83f07e659f0deafd0253dc096dab5a36d53efe653c5c3" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" dependencies = [ "libsecp256k1-core", ] [[package]] name = "libsecp256k1-gen-genmult" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67abfe149395e3aa1c48a2beb32b068e2334402df8181f818d3aee2b304c4f5d" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" dependencies = [ "libsecp256k1-core", ] [[package]] name = "libsqlite3-sys" -version = "0.22.2" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290b64917f8b0cb885d9de0f9959fe1f775d7fa12f1da2db9001c1c8ab60f89d" +checksum = "d2cafc7c74096c336d9d27145f7ebd4f4b6f95ba16aa5a282387267e6925cb58" dependencies = [ "cc", "pkg-config", "vcpkg", ] +[[package]] +name = "link-cplusplus" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +dependencies = [ + "cc", +] + [[package]] name = "linked-hash-map" -version = "0.5.4" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "lock_api" -version = "0.4.5" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ + "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.14" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if 1.0.0", + "serde", "value-bag", ] [[package]] name = "lru" -version = "0.6.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ea2d928b485416e8908cff2d97d621db22b27f7b3b6729e438bcf42c671ba91" +checksum = "b6e8aaa3f231bb4bd57b84b2d5dc3ae7f350265df8aa96492e0bc394a1571909" dependencies = [ - "hashbrown 0.11.2", + "hashbrown 0.12.3", ] [[package]] @@ -2262,6 +2947,12 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + [[package]] name = "match_cfg" version = "0.1.0" @@ -2270,9 +2961,9 @@ checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" [[package]] name = "matchers" -version = "0.0.1" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ "regex-automata", ] @@ -2283,73 +2974,67 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +[[package]] +name = "md-5" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +dependencies = [ + "digest 0.10.5", +] + [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ "autocfg", ] [[package]] -name = "minicbor" -version = "0.11.1" +name = "minimal-lexical" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6dced2db4cf71eff54673f123588a8f7ec25339aab89dc6b5d01e66a2225f34" -dependencies = [ - "minicbor-derive", -] +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] -name = "minicbor-derive" -version = "0.7.1" +name = "miniz_oxide" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355264ec26c23aae1d8d4599356cbd7e84134182b8feb9573acbd6d68c09c73e" +checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" dependencies = [ - "proc-macro2", - "quote", - "syn", + "adler", ] [[package]] name = "mio" -version = "0.7.13" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" dependencies = [ "libc", "log", - "miow", - "ntapi", - "winapi", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", ] [[package]] name = "multiaddr" -version = "0.13.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48ee4ea82141951ac6379f964f71b20876d43712bea8faf6dd1a375e08a46499" +checksum = "a4aebdb21e90f81d13ed01dc84123320838e53963c2ca94b60b305d3fa64f31e" dependencies = [ "arrayref", - "bs58", "byteorder", "data-encoding", + "multibase", "multihash", "percent-encoding", "serde", @@ -2371,23 +3056,23 @@ dependencies = [ [[package]] name = "multihash" -version = "0.14.0" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "752a61cd890ff691b4411423d23816d5866dd5621e4d1c5687a53b94b5a979d8" +checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" dependencies = [ - "blake3", - "digest", - "generic-array", + "blake3 1.3.1", + "core2", + "digest 0.10.5", "multihash-derive", - "sha2", + "sha2 0.10.6", "unsigned-varint", ] [[package]] name = "multihash-derive" -version = "0.7.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "424f6e86263cd5294cbd7f1e95746b95aca0e0d66bff31e5a40d6baa87b4aa99" +checksum = "fc076939022111618a5026d3be019fd8b366e76314538ff9a1b59ffbcbf98bcd" dependencies = [ "proc-macro-crate", "proc-macro-error", @@ -2405,32 +3090,100 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "multistream-select" -version = "0.11.0" -source = "git+https://github.com/Actyx/rust-libp2p?branch=sim-open#9eecb895de76b5d32ddab62945d13efc5d4c6722" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8552ab875c1313b97b8d20cb857b9fd63e2d1d6a0a1b53ce9821e575405f27a" dependencies = [ "bytes", "futures", "log", - "pin-project 1.0.8", - "rand 0.7.3", + "pin-project", "smallvec", "unsigned-varint", ] [[package]] name = "names" -version = "0.11.0" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7d66043b25d4a6cccb23619d10c19c25304b355a7dccd4a8e11423dd2382146" +dependencies = [ + "clap 3.2.22", + "rand 0.8.5", +] + +[[package]] +name = "netlink-packet-core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345b8ab5bd4e71a2986663e88c56856699d060e78e152e6e9d7966fcd5491297" +dependencies = [ + "anyhow", + "byteorder", + "libc", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-route" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9ea4302b9759a7a88242299225ea3688e63c85ea136371bb6cf94fd674efaab" +dependencies = [ + "anyhow", + "bitflags", + "byteorder", + "libc", + "netlink-packet-core", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-utils" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25af9cf0dc55498b7bd94a1508af7a78706aa0ab715a73c5169273e03c84845e" +dependencies = [ + "anyhow", + "byteorder", + "paste", + "thiserror", +] + +[[package]] +name = "netlink-proto" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65b4b14489ab424703c092062176d52ba55485a89c076b4f9db05092b7223aa6" +dependencies = [ + "bytes", + "futures", + "log", + "netlink-packet-core", + "netlink-sys", + "thiserror", + "tokio", +] + +[[package]] +name = "netlink-sys" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef320dab323286b50fb5cdda23f61c796a72a89998ab565ca32525c5c556f2da" +checksum = "92b654097027250401127914afb37cb1f311df6610a9891ff07a757e94199027" dependencies = [ - "rand 0.3.23", + "async-io", + "bytes", + "futures", + "libc", + "log", + "tokio", ] [[package]] name = "netsim-embed" -version = "0.5.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258b4524327a04ce1bb6a0667b7b93c7a5ed8b42415ea77255b1bb05c8a8965c" +checksum = "9e561fd26ad86f3ba2804dc4eeb05e3e67ec2feca705c61f6d145b3915e79e56" dependencies = [ "async-global-executor", "async-process", @@ -2445,23 +3198,23 @@ dependencies = [ [[package]] name = "netsim-embed-core" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b92241116c80c5aa3905e98f2e3932b5e66be85b8290dbe4b209aac82b527d" +checksum = "1c0e99608e5e6c3631a0d6e86b08506ff9c058ae4eb7dee5f13207dd4c116c16" dependencies = [ "async-global-executor", "async-io", "futures", "libpacket", - "rand 0.8.4", + "rand 0.8.5", "thiserror", ] [[package]] name = "netsim-embed-machine" -version = "0.4.3" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8b12cb54c976fe69a4209735c65d70553cad88d60feed99d14c0fc362fe8e3c" +checksum = "b0f1112c44fffc1f82dfc64b790f3fd0eca33094d8aec86de7fea872a441f39b" dependencies = [ "async-global-executor", "async-io", @@ -2482,14 +3235,14 @@ dependencies = [ "futures", "log", "netsim-embed-core", - "rand 0.8.4", + "rand 0.8.5", ] [[package]] name = "netsim-embed-router" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b108c88d477023d72a217ee21b6fbedac622d4a84499f3408fa9e8691fef0b" +checksum = "b7981b4047459f5fce6c1e922ea4cf2d7d4a8fc6774f9dae07e1ac975dfc3ebf" dependencies = [ "async-global-executor", "futures", @@ -2498,6 +3251,18 @@ dependencies = [ "netsim-embed-core", ] +[[package]] +name = "nix" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" +dependencies = [ + "bitflags", + "cfg-if 1.0.0", + "libc", + "memoffset", +] + [[package]] name = "nohash-hasher" version = "0.2.0" @@ -2505,19 +3270,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" [[package]] -name = "ntapi" -version = "0.3.6" +name = "nom" +version = "7.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" dependencies = [ + "overload", "winapi", ] +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-integer" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg", "num-traits", @@ -2525,28 +3318,55 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" dependencies = [ "hermit-abi", "libc", ] +[[package]] +name = "object" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "memchr", +] + +[[package]] +name = "oid-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e20717fa0541f39bd146692035c37bedfa532b3e5071b35761082407546b2a" +dependencies = [ + "asn1-rs 0.3.1", +] + +[[package]] +name = "oid-registry" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d4bda43fd1b844cbc6e6e54b5444e2b1bc7838bce59ad205902cccbb26d6761" +dependencies = [ + "asn1-rs 0.5.1", +] + [[package]] name = "once_cell" -version = "1.8.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" [[package]] name = "opaque-debug" @@ -2555,35 +3375,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] -name = "open-metrics-client" -version = "0.12.0" +name = "os_str_bytes" +version = "6.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7337d80c23c2d8b1349563981bc4fb531220733743ba8115454a67b181173f0d" -dependencies = [ - "dtoa", - "itoa", - "open-metrics-client-derive-text-encode", - "owning_ref", -] +checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" [[package]] -name = "open-metrics-client-derive-text-encode" +name = "overload" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c83b586f00268c619c1cb3340ec1a6f59dd9ba1d9833a273a68e6d5cd8ffc" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "p256" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" dependencies = [ - "proc-macro2", - "quote", - "syn", + "ecdsa", + "elliptic-curve", + "sha2 0.10.6", ] [[package]] -name = "owning_ref" -version = "0.4.1" +name = "p384" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce" +checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" dependencies = [ - "stable_deref_trait", + "ecdsa", + "elliptic-curve", + "sha2 0.10.6", ] [[package]] @@ -2600,7 +3422,17 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", - "parking_lot_core", + "parking_lot_core 0.8.5", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.3", ] [[package]] @@ -2618,64 +3450,72 @@ dependencies = [ ] [[package]] -name = "percent-encoding" -version = "2.1.0" +name = "parking_lot_core" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] [[package]] -name = "pest" -version = "2.1.3" +name = "paste" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" -dependencies = [ - "ucd-trie", -] +checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" [[package]] -name = "petgraph" -version = "0.5.1" +name = "pem" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" +checksum = "03c64931a1a212348ec4f3b4362585eca7159d0d09cbdf4a7f74f02173596fd4" dependencies = [ - "fixedbitset", - "indexmap", + "base64 0.13.0", ] [[package]] -name = "pin-project" -version = "0.4.28" +name = "pem-rfc7468" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "918192b5c59119d51e0cd221f4d49dde9112824ba717369e903c97d076083d0f" +checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac" dependencies = [ - "pin-project-internal 0.4.28", + "base64ct", ] [[package]] -name = "pin-project" -version = "1.0.8" +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "petgraph" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "576bc800220cc65dac09e99e97b08b358cfab6e17078de8dc5fee223bd2d0c08" +checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" dependencies = [ - "pin-project-internal 1.0.8", + "fixedbitset", + "indexmap", ] [[package]] -name = "pin-project-internal" -version = "0.4.28" +name = "pin-project" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be26700300be6d9d23264c73211d8190e755b6b5ca7a1b28230025511b52a5e" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" dependencies = [ - "proc-macro2", - "quote", - "syn", + "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.8" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ "proc-macro2", "quote", @@ -2690,9 +3530,9 @@ checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" [[package]] name = "pin-project-lite" -version = "0.2.7" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] name = "pin-utils" @@ -2700,18 +3540,29 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" -version = "0.3.19" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" [[package]] name = "polling" -version = "2.1.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92341d779fa34ea8437ef4d82d440d5e1ce3f3ff7f824aa64424cd481f9a1f25" +checksum = "899b00b9c8ab553c743b3e11e87c5c7d423b2a2de229ba95b24a756344748011" dependencies = [ + "autocfg", "cfg-if 1.0.0", "libc", "log", @@ -2755,16 +3606,47 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.10" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "predicates" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5aab5be6e4732b473071984b3164dbbfb7a3674d30ea5ff44410b6bcd960c3c" +dependencies = [ + "difflib", + "float-cmp", + "itertools", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da1c2388b1513e1b605fcec39a95e0a9e8ef088f71443ef37099fa9ae6673fcb" + +[[package]] +name = "predicates-tree" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +checksum = "4d86de6de25020a36c6d3643a86d9a6a9f552107c0559c60ea03551b5e16c032" +dependencies = [ + "predicates-core", + "termtree", +] [[package]] name = "proc-macro-crate" -version = "1.0.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fdbd1df62156fbc5945f4762632564d7d038153091c3fcf1067f6aef7cff92" +checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" dependencies = [ + "once_cell", "thiserror", "toml", ] @@ -2799,100 +3681,166 @@ version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" -[[package]] -name = "proc-macro-nested" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" - [[package]] name = "proc-macro2" -version = "1.0.29" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "prometheus" -version = "0.12.0" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5986aa8d62380092d2f50f8b1cdba9cb9b6731ffd4b25b51fd126b6c3e05b99c" +checksum = "45c8babc29389186697fe5a2a4859d697825496b83db5d0b65271cdc0488e88c" dependencies = [ "cfg-if 1.0.0", "fnv", "lazy_static", "memchr", - "parking_lot", + "parking_lot 0.12.1", "protobuf", "thiserror", ] +[[package]] +name = "prometheus-client" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83cd1b99916654a69008fd66b4f9397fbe08e6e51dfe23d4417acf5d3b8cb87c" +dependencies = [ + "dtoa", + "itoa", + "parking_lot 0.12.1", + "prometheus-client-derive-text-encode", +] + +[[package]] +name = "prometheus-client-derive-text-encode" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66a455fbcb954c1a7decf3c586e860fd7889cddf4b8e164be736dbac95a953cd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "prost" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e6984d2f1a23009bd270b8bb56d0926810a3d483f59c987d77969e9d8e840b2" +checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" dependencies = [ "bytes", - "prost-derive 0.7.0", + "prost-derive 0.9.0", ] [[package]] name = "prost" -version = "0.8.0" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71adf41db68aa0daaefc69bb30bcd68ded9b9abaad5d1fbb6304c4fb390e083e" +dependencies = [ + "bytes", + "prost-derive 0.10.1", +] + +[[package]] +name = "prost" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de5e2533f59d08fcf364fd374ebda0692a70bd6d7e66ef97f306f45c6c5d8020" +checksum = "399c3c31cdec40583bb68f0b18403400d01ec4289c383aa047560439952c4dd7" dependencies = [ "bytes", - "prost-derive 0.8.0", + "prost-derive 0.11.0", ] [[package]] name = "prost-build" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d3ebd75ac2679c2af3a92246639f9fcc8a442ee420719cc4fe195b98dd5fa3" +checksum = "62941722fb675d463659e49c4f3fe1fe792ff24fe5bbaa9c08cd3b98a1c354f5" dependencies = [ "bytes", - "heck", - "itertools 0.9.0", + "heck 0.3.3", + "itertools", + "lazy_static", "log", "multimap", "petgraph", - "prost 0.7.0", - "prost-types 0.7.0", + "prost 0.9.0", + "prost-types 0.9.0", + "regex", "tempfile", "which", ] [[package]] name = "prost-build" -version = "0.8.0" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae5a4388762d5815a9fc0dea33c56b021cdc8dde0c55e0c9ca57197254b0cab" +dependencies = [ + "bytes", + "cfg-if 1.0.0", + "cmake", + "heck 0.4.0", + "itertools", + "lazy_static", + "log", + "multimap", + "petgraph", + "prost 0.10.4", + "prost-types 0.10.1", + "regex", + "tempfile", + "which", +] + +[[package]] +name = "prost-build" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355f634b43cdd80724ee7848f95770e7e70eefa6dcf14fea676216573b8fd603" +checksum = "7f835c582e6bd972ba8347313300219fed5bfa52caf175298d860b61ff6069bb" dependencies = [ "bytes", - "heck", - "itertools 0.10.1", + "heck 0.4.0", + "itertools", + "lazy_static", "log", "multimap", "petgraph", - "prost 0.8.0", - "prost-types 0.8.0", + "prost 0.11.0", + "prost-types 0.11.1", + "regex", "tempfile", "which", ] +[[package]] +name = "prost-codec" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc34979ff898b6e141106178981ce2596c387ea6e62533facfc61a37fc879c0" +dependencies = [ + "asynchronous-codec", + "bytes", + "prost 0.11.0", + "thiserror", + "unsigned-varint", +] + [[package]] name = "prost-derive" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "169a15f3008ecb5160cba7d37bcd690a7601b6d30cfb87a117d45e59d52af5d4" +checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" dependencies = [ "anyhow", - "itertools 0.9.0", + "itertools", "proc-macro2", "quote", "syn", @@ -2900,62 +3848,65 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.8.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "600d2f334aa05acb02a755e217ef1ab6dea4d51b58b7846588b747edec04efba" +checksum = "7b670f45da57fb8542ebdbb6105a925fe571b67f9e7ed9f47a06a84e72b4e7cc" dependencies = [ "anyhow", - "itertools 0.10.1", + "itertools", "proc-macro2", "quote", "syn", ] [[package]] -name = "prost-types" -version = "0.7.0" +name = "prost-derive" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b518d7cdd93dab1d1122cf07fa9a60771836c668dde9d9e2a139f957f0d9f1bb" +checksum = "7345d5f0e08c0536d7ac7229952590239e77abf0a0100a1b1d890add6ea96364" dependencies = [ - "bytes", - "prost 0.7.0", + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "prost-types" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "603bbd6394701d13f3f25aada59c7de9d35a6a5887cfc156181234a44002771b" +checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a" dependencies = [ "bytes", - "prost 0.8.0", + "prost 0.9.0", ] [[package]] -name = "protobuf" -version = "2.25.1" +name = "prost-types" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23129d50f2c9355ced935fce8a08bd706ee2e7ce2b3b33bf61dace0e379ac63a" +checksum = "2d0a014229361011dc8e69c8a1ec6c2e8d0f2af7c91e3ea3f5b2170298461e68" +dependencies = [ + "bytes", + "prost 0.10.4", +] [[package]] -name = "ptr_meta" -version = "0.1.4" +name = "prost-types" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +checksum = "4dfaa718ad76a44b3415e6c4d53b17c8f99160dcb3a99b10470fce8ad43f6e3e" dependencies = [ - "ptr_meta_derive", + "bytes", + "prost 0.11.0", ] [[package]] -name = "ptr_meta_derive" -version = "0.1.4" +name = "protobuf" +version = "2.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" [[package]] name = "quick-error" @@ -2963,61 +3914,33 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" -[[package]] -name = "quinn-noise" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1c50d22f398d489a79753d17bab5a6f9fb48e5cc537ce50b0bb9312ae07ea9" -dependencies = [ - "bytes", - "chacha20poly1305", - "curve25519-dalek", - "ed25519-dalek", - "quinn-proto", - "rand_core 0.5.1", - "ring", - "sha2", - "subtle", - "tracing", - "x25519-dalek", - "xoodoo", - "zeroize", -] - [[package]] name = "quinn-proto" -version = "0.7.3" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "047aa96ec7ee6acabad7a1318dff72e9aff8994316bf2166c9b94cbec78ca54c" +checksum = "57098b1a3d2159d13dc3a98c0e3a5f8ab91ac3dd2471e52b1d712ea0c1085555" dependencies = [ "bytes", - "rand 0.8.4", + "rand 0.8.5", "ring", + "rustc-hash", + "rustls 0.20.7", "slab", "thiserror", "tinyvec", "tracing", + "webpki 0.22.0", ] [[package]] name = "quote" -version = "1.0.9" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.3.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" -dependencies = [ - "libc", - "rand 0.4.6", -] - [[package]] name = "rand" version = "0.4.6" @@ -3041,19 +3964,18 @@ dependencies = [ "libc", "rand_chacha 0.2.2", "rand_core 0.5.1", - "rand_hc 0.2.0", + "rand_hc", ] [[package]] name = "rand" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha 0.3.1", - "rand_core 0.6.3", - "rand_hc 0.3.1", + "rand_core 0.6.4", ] [[package]] @@ -3073,7 +3995,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -3102,11 +4024,11 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.3", + "getrandom 0.2.7", ] [[package]] @@ -3119,19 +4041,29 @@ dependencies = [ ] [[package]] -name = "rand_hc" -version = "0.3.1" +name = "rcgen" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" dependencies = [ - "rand_core 0.6.3", + "pem", + "ring", + "time 0.3.17", + "x509-parser 0.13.2", + "yasna", ] [[package]] -name = "rawbytes" -version = "0.1.2" +name = "rcgen" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04d0088f16afb86d12c7f239d8de4637fa68ecc99a3db227e1ab58a294713e60" +checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" +dependencies = [ + "pem", + "ring", + "time 0.3.17", + "yasna", +] [[package]] name = "rdrand" @@ -3144,18 +4076,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.10" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.5.4" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" dependencies = [ "aho-corasick", "memchr", @@ -3173,9 +4105,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "remove_dir_all" @@ -3186,15 +4118,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "rend" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d0351a2e529ee30d571ef31faa5a4e0b9addaad087697b77efb20d2809e41c7" -dependencies = [ - "bytecheck", -] - [[package]] name = "resolv-conf" version = "0.7.0" @@ -3205,6 +4128,17 @@ dependencies = [ "quick-error", ] +[[package]] +name = "rfc6979" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88c86280f057430a52f4861551b092a01b419b8eacefc7c995eacb9dc132fe32" +dependencies = [ + "crypto-bigint", + "hmac 0.12.1", + "zeroize", +] + [[package]] name = "ring" version = "0.16.20" @@ -3221,41 +4155,57 @@ dependencies = [ ] [[package]] -name = "rkyv" -version = "0.7.17" +name = "route-recognizer" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56770675ebc04927ded3e60633437841581c285dc6236109ea25fbf3beb7b59e" + +[[package]] +name = "rtcp" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1204395d00a3821c44ee6adb1fdd58f353ed16ee944bbc2f2068a4b54311e9a" +checksum = "c11171e7e37998dcf54a9e9d4a6e2e1932c994424c7d39bc6349fed1424c45c3" dependencies = [ - "bytecheck", - "hashbrown 0.11.2", - "ptr_meta", - "rend", - "rkyv_derive", - "seahash", + "bytes", + "thiserror", + "webrtc-util", ] [[package]] -name = "rkyv_derive" -version = "0.7.17" +name = "rtnetlink" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a2fec830152e70e4e6509d4ea223abc664a5e16bfa18647ebf01c4c8936912" +checksum = "322c53fd76a18698f1c27381d58091de3a043d356aa5bd0d510608b565f469a0" dependencies = [ - "proc-macro2", - "quote", - "syn", + "async-global-executor", + "futures", + "log", + "netlink-packet-route", + "netlink-proto", + "nix", + "thiserror", + "tokio", ] [[package]] -name = "route-recognizer" -version = "0.2.0" +name = "rtp" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56770675ebc04927ded3e60633437841581c285dc6236109ea25fbf3beb7b59e" +checksum = "a2a095411ff00eed7b12e4c6a118ba984d113e1079582570d56a5ee723f11f80" +dependencies = [ + "async-trait", + "bytes", + "rand 0.8.5", + "serde", + "thiserror", + "webrtc-util", +] [[package]] name = "rusqlite" -version = "0.25.3" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57adcf67c8faaf96f3248c2a7b419a0dbc52ebe36ba83dd57fe83827c1ea4eb3" +checksum = "4ba4d3462c8b2e4d7f4fcfcf2b296dc6b65404fbbc7b63daa37fd485c149daf7" dependencies = [ "bitflags", "fallible-iterator", @@ -3266,6 +4216,18 @@ dependencies = [ "smallvec", ] +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc_version" version = "0.2.3" @@ -3277,37 +4239,71 @@ dependencies = [ [[package]] name = "rustc_version" -version = "0.3.3" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.14", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "rustls" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +dependencies = [ + "base64 0.13.0", + "log", + "ring", + "sct 0.6.1", + "webpki 0.21.4", +] + +[[package]] +name = "rustls" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" dependencies = [ - "semver 0.11.0", + "log", + "ring", + "sct 0.7.0", + "webpki 0.22.0", ] [[package]] name = "rw-stream-sink" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4da5fcb054c46f5a5dff833b129285a93d3f0179531735e6c866e8cc307d2020" +checksum = "26338f5e09bb721b85b135ea05af7767c90b52f6de4f087d4f4a3a9d64e7dc04" dependencies = [ "futures", - "pin-project 0.4.28", + "pin-project", "static_assertions", ] [[package]] name = "ryu" -version = "1.0.5" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" [[package]] name = "salsa20" -version = "0.8.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecbd2eb639fd7cab5804a0837fe373cc2172d15437e804c054a9fb885cb923b0" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" dependencies = [ - "cipher 0.3.0", + "cipher 0.4.3", ] [[package]] @@ -3317,69 +4313,112 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] -name = "seahash" -version = "4.1.0" +name = "scratch" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" +checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" [[package]] -name = "semver" -version = "0.9.0" +name = "sct" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" dependencies = [ - "semver-parser 0.7.0", + "ring", + "untrusted", ] [[package]] -name = "semver" -version = "0.11.0" +name = "sct" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ - "semver-parser 0.10.2", + "ring", + "untrusted", ] [[package]] -name = "semver-parser" -version = "0.7.0" +name = "sdp" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +checksum = "4d22a5ef407871893fd72b4562ee15e4742269b173959db4b8df6f538c414e13" +dependencies = [ + "rand 0.8.5", + "substring", + "thiserror", + "url", +] [[package]] -name = "semver-parser" -version = "0.10.2" +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "pest", + "semver-parser", ] +[[package]] +name = "semver" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "serde" -version = "1.0.130" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" +checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.130" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" +checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "serde_fmt" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2963a69a2b3918c1dc75a45a18bd3fcd1120e31d3f59deb1b2f9b5d5ffb8baa4" +dependencies = [ + "serde", +] + [[package]] name = "serde_json" -version = "1.0.67" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7f9e390c27c3c0ce8bc5d725f6e4d30a29d26659494aa4b17535f7522c5c950" +checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074" dependencies = [ "itoa", "ryu", @@ -3388,9 +4427,9 @@ dependencies = [ [[package]] name = "serde_qs" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a72808528a89fa9eca23bbb6a1eb92cb639b881357269b6510f11e50c0f8a9" +checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6" dependencies = [ "percent-encoding", "serde", @@ -3399,9 +4438,9 @@ dependencies = [ [[package]] name = "serde_urlencoded" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", "itoa", @@ -3409,51 +4448,82 @@ dependencies = [ "serde", ] +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + [[package]] name = "sha1" -version = "0.6.0" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +dependencies = [ + "sha1_smol", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" [[package]] name = "sha2" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ - "block-buffer", + "block-buffer 0.9.0", "cfg-if 1.0.0", "cpufeatures", - "digest", + "digest 0.9.0", "opaque-debug", ] +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.5", +] + [[package]] name = "sha3" -version = "0.9.1" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +checksum = "e2904bea16a1ae962b483322a1c7b81d976029203aea1f461e51cd7705db7ba9" dependencies = [ - "block-buffer", - "digest", + "digest 0.10.5", "keccak", - "opaque-debug", ] [[package]] name = "sharded-slab" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740223c51853f3145fe7c90360d2d4232f2b62e3449489c207eccde818979982" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" dependencies = [ "lazy_static", ] [[package]] name = "signal-hook" -version = "0.3.10" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c98891d737e271a2954825ef19e46bd16bdb98e2746f2eec4f7a4ef7946efd1" +checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" dependencies = [ "libc", "signal-hook-registry", @@ -3470,9 +4540,13 @@ dependencies = [ [[package]] name = "signature" -version = "1.3.1" +version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19772be3c4dd2ceaacf03cb41d5885f2a02c4d8804884918e3a258480803335" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.5", + "rand_core 0.6.4", +] [[package]] name = "simple-mutex" @@ -3485,66 +4559,59 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590" - -[[package]] -name = "sled" -version = "0.34.7" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" dependencies = [ - "crc32fast", - "crossbeam-epoch", - "crossbeam-utils", - "fs2", - "fxhash", - "libc", - "log", - "parking_lot", + "autocfg", ] [[package]] name = "smallvec" -version = "1.6.1" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "smol" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" +checksum = "85cf3b5351f3e783c1d79ab5fc604eeed8b8ae9abd36b166e8b87a089efd85e4" +dependencies = [ + "async-channel", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-net", + "async-process", + "blocking", + "futures-lite", + "once_cell", +] [[package]] name = "snow" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6142f7c25e94f6fd25a32c3348ec230df9109b463f59c8c7acc4bd34936babb7" +checksum = "774d05a3edae07ce6d68ea6984f3c05e9bba8927e3dd591e3b479e5b03213d0d" dependencies = [ "aes-gcm 0.9.4", "blake2", "chacha20poly1305", - "rand 0.8.4", - "rand_core 0.6.3", + "curve25519-dalek 4.0.0-pre.1", + "rand_core 0.6.4", "ring", - "rustc_version 0.3.3", - "sha2", + "rustc_version 0.4.0", + "sha2 0.10.6", "subtle", - "x25519-dalek", -] - -[[package]] -name = "socket2" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "winapi", ] [[package]] name = "socket2" -version = "0.4.1" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "765f090f0e423d2b55843402a07915add955e7d60657db13707a159727326cad" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" dependencies = [ "libc", "winapi", @@ -3557,10 +4624,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] -name = "stable_deref_trait" -version = "1.2.0" +name = "spki" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der", +] [[package]] name = "standback" @@ -3632,30 +4703,64 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "structopt" -version = "0.3.23" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf9d950ef167e25e0bdb073cf1d68e9ad2795ac826f2f3f59647817cf23c0bfa" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" dependencies = [ - "clap", + "clap 2.34.0", "lazy_static", "structopt-derive", ] [[package]] name = "structopt-derive" -version = "0.4.16" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134d838a2c9943ac3125cf6df165eda53493451b719f3255b2a26b85f772d0ba" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ - "heck", + "heck 0.3.3", "proc-macro-error", "proc-macro2", "quote", "syn", ] +[[package]] +name = "stun" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7e94b1ec00bad60e6410e058b52f1c66de3dc5fe4d62d09b3e52bb7d3b73e25" +dependencies = [ + "base64 0.13.0", + "crc", + "lazy_static", + "md-5", + "rand 0.8.5", + "ring", + "subtle", + "thiserror", + "tokio", + "url", + "webrtc-util", +] + +[[package]] +name = "substring" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ee6433ecef213b2e72f587ef64a2f5943e7cd16fbd82dbe8bc07486c534c86" +dependencies = [ + "autocfg", +] + [[package]] name = "subtle" version = "2.4.1" @@ -3667,23 +4772,26 @@ name = "sval" version = "1.0.0-alpha.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45f6ee7c7b87caf59549e9fe45d6a69c75c8019e79e212a835c5da0e92f0ba08" +dependencies = [ + "serde", +] [[package]] name = "syn" -version = "1.0.76" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84" +checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] name = "synstructure" -version = "0.12.5" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "474aaa926faa1603c40b7885a9eaea29b444d1cb2850cb7c0e37bb1a4182f4fa" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2", "quote", @@ -3691,6 +4799,27 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "system-configuration" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75182f12f490e953596550b65ee31bda7c8e043d9386174b353bda50838c3fd" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tempdir" version = "0.3.7" @@ -3703,18 +4832,33 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" dependencies = [ "cfg-if 1.0.0", + "fastrand", "libc", - "rand 0.8.4", "redox_syscall", "remove_dir_all", "winapi", ] +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "termtree" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" + [[package]] name = "textwrap" version = "0.11.0" @@ -3724,20 +4868,26 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "textwrap" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" + [[package]] name = "thiserror" -version = "1.0.29" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.29" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" dependencies = [ "proc-macro2", "quote", @@ -3746,9 +4896,9 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" dependencies = [ "once_cell", ] @@ -3770,7 +4920,7 @@ dependencies = [ "http-types", "kv-log-macro", "log", - "pin-project-lite 0.2.7", + "pin-project-lite 0.2.9", "route-recognizer", "serde", "serde_json", @@ -3778,11 +4928,12 @@ dependencies = [ [[package]] name = "time" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" dependencies = [ "libc", + "wasi 0.10.0+wasi-snapshot-preview1", "winapi", ] @@ -3796,11 +4947,29 @@ dependencies = [ "libc", "standback", "stdweb", - "time-macros", + "time-macros 0.1.1", "version_check", "winapi", ] +[[package]] +name = "time" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" +dependencies = [ + "itoa", + "serde", + "time-core", + "time-macros 0.2.6", +] + +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + [[package]] name = "time-macros" version = "0.1.1" @@ -3811,6 +4980,15 @@ dependencies = [ "time-macros-impl", ] +[[package]] +name = "time-macros" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +dependencies = [ + "time-core", +] + [[package]] name = "time-macros-impl" version = "0.1.2" @@ -3824,11 +5002,21 @@ dependencies = [ "syn", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "tinyvec" -version = "1.4.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5241dd6f21443a3606b432718b166d3cedc962fd4b8bea54a8bc7f514ebda986" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] @@ -3841,44 +5029,75 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.11.0" +version = "1.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4efe6fc2395938c8155973d7be49fe8d03a843726e285e100a8a383cc0154ce" +checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" dependencies = [ "autocfg", + "bytes", "libc", + "memchr", "mio", "num_cpus", - "pin-project-lite 0.2.7", + "parking_lot 0.12.1", + "pin-project-lite 0.2.9", + "signal-hook-registry", + "socket2", + "tokio-macros", "winapi", ] +[[package]] +name = "tokio-macros" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-util" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +dependencies = [ + "bytes", + "futures-core", + "futures-io", + "futures-sink", + "pin-project-lite 0.2.9", + "tokio", +] + [[package]] name = "toml" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" dependencies = [ "serde", ] [[package]] name = "tracing" -version = "0.1.27" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ba9ab62b7d6497a8638dfda5e5c4fb3b2d5a7fca4118f2b96151c8ef1a437e" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if 1.0.0", - "pin-project-lite 0.2.7", + "pin-project-lite 0.2.9", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" -version = "0.1.16" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98863d0dd09fa59a1b79c6750ad80dbda6b75f4e71c437a6a1a8cb91a8bcbd77" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", @@ -3887,61 +5106,48 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.20" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46125608c26121c81b0c6d693eab5a420e416da7e43c426d2e8f7df8da8a3acf" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ - "lazy_static", + "once_cell", + "valuable", ] [[package]] name = "tracing-log" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" dependencies = [ "lazy_static", "log", "tracing-core", ] -[[package]] -name = "tracing-serde" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b" -dependencies = [ - "serde", - "tracing-core", -] - [[package]] name = "tracing-subscriber" -version = "0.2.22" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62af966210b88ad5776ee3ba12d5f35b8d6a2b2a12168f3080cf02b814d7376b" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" dependencies = [ - "ansi_term 0.12.1", - "chrono", - "lazy_static", "matchers", + "nu-ansi-term", + "once_cell", "regex", - "serde", - "serde_json", "sharded-slab", "smallvec", "thread_local", "tracing", "tracing-core", "tracing-log", - "tracing-serde", ] [[package]] name = "trust-dns-proto" -version = "0.20.3" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0d7f5db438199a6e2609debe3f69f808d074e0a2888ee0bccb45fe234d03f4" +checksum = "4f7f83d1e4a0e4358ac54c5c3681e5d7da5efc5a7a632c90bb6d6669ddd9bc26" dependencies = [ "async-trait", "cfg-if 1.0.0", @@ -3950,67 +5156,69 @@ dependencies = [ "futures-channel", "futures-io", "futures-util", - "idna", + "idna 0.2.3", "ipnet", "lazy_static", - "log", - "rand 0.8.4", + "rand 0.8.5", "smallvec", + "socket2", "thiserror", "tinyvec", "tokio", + "tracing", "url", ] [[package]] name = "trust-dns-resolver" -version = "0.20.3" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ad17b608a64bd0735e67bde16b0636f8aa8591f831a25d18443ed00a699770" +checksum = "aff21aa4dcefb0a1afbfac26deb0adc93888c7d295fb63ab273ef276ba2b7cfe" dependencies = [ "cfg-if 1.0.0", "futures-util", "ipconfig", "lazy_static", - "log", "lru-cache", - "parking_lot", + "parking_lot 0.12.1", "resolv-conf", "smallvec", "thiserror", "tokio", + "tracing", "trust-dns-proto", ] [[package]] -name = "typenum" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" - -[[package]] -name = "ucd-trie" -version = "0.1.3" +name = "turn" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" +checksum = "4712ee30d123ec7ae26d1e1b218395a16c87cdbaf4b3925d170d684af62ea5e8" +dependencies = [ + "async-trait", + "base64 0.13.0", + "futures", + "log", + "md-5", + "rand 0.8.5", + "ring", + "stun", + "thiserror", + "tokio", + "webrtc-util", +] [[package]] -name = "udp-socket" -version = "0.1.5" +name = "typenum" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab965d55b986832d7aefec8778caf524fa14ab0f7fe79588ebc85da4a83f0572" -dependencies = [ - "async-io", - "futures-lite", - "libc", - "socket2 0.4.1", -] +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "uint" -version = "0.9.1" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6470ab50f482bde894a037a57064480a246dbfdd5960bd65a44824693f08da5f" +checksum = "a45526d29728d135c2900b0d30573fe3ee79fceb12ef534c7bb30e810a91b601" dependencies = [ "byteorder", "crunchy", @@ -4020,36 +5228,42 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "246f4c42e67e7a4e3c6106ff716a5d067d4132a642840b242e357e468a2a0085" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" [[package]] name = "unicode-normalization" -version = "0.1.19" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" +checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" [[package]] name = "unicode-width" -version = "0.1.8" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "unicode-xid" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "universal-hash" @@ -4063,9 +5277,9 @@ dependencies = [ [[package]] name = "unsigned-varint" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f8d425fafb8cd76bc3f22aace4af471d3156301d7508f2107e98fbeae10bc7f" +checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" dependencies = [ "asynchronous-codec", "bytes", @@ -4081,24 +5295,41 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.2.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" dependencies = [ "form_urlencoded", - "idna", - "matches", + "idna 0.3.0", "percent-encoding", "serde", ] +[[package]] +name = "uuid" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c" +dependencies = [ + "getrandom 0.2.7", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "value-bag" -version = "1.0.0-alpha.7" +version = "1.0.0-alpha.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd320e1520f94261153e96f7534476ad869c14022aee1e59af7c778075d840ae" +checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55" dependencies = [ "ctor", + "erased-serde", + "serde", + "serde_fmt", "sval", "version_check", ] @@ -4117,9 +5348,9 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "void" @@ -4127,6 +5358,24 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "waitgroup" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1f50000a783467e6c0200f9d10642f4bc424e39efc1b770203e88b488f79292" +dependencies = [ + "atomic-waker", +] + [[package]] name = "waker-fn" version = "1.1.0" @@ -4141,15 +5390,21 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" +version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.77" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e68338db6becec24d3c7977b5bf8a48be992c934b5d07177e3931f5dc9b076c" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" dependencies = [ "cfg-if 1.0.0", "serde", @@ -4159,13 +5414,13 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.77" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f34c405b4f0658583dba0c1c7c9b694f3cac32655db463b56c254a1c75269523" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", "syn", @@ -4174,9 +5429,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.27" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a87d738d4abc4cf22f6eb142f5b9a81301331ee3c767f2fef2fda4e325492060" +checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -4186,9 +5441,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.77" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d5a6580be83b19dc570a8f9c324251687ab2184e57086f71625feb57ec77c8" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4196,9 +5451,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.77" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3775a030dc6f5a0afd8a84981a21cc92a781eb429acef9ecce476d0c9113e92" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", @@ -4209,9 +5464,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.77" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c279e376c7a8e8752a8f1eaa35b7b0bee6bb9fb0cdacfa97cc3f1f289c87e2b4" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "wasm-timer" @@ -4221,7 +5476,7 @@ checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" dependencies = [ "futures", "js-sys", - "parking_lot", + "parking_lot 0.11.2", "pin-utils", "wasm-bindgen", "wasm-bindgen-futures", @@ -4230,14 +5485,246 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.54" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a84d70d1ec7d2da2d26a5bd78f4bca1b8c3254805363ce743b7a05bc30d195a" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" dependencies = [ "js-sys", "wasm-bindgen", ] +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webrtc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3bc9049bdb2cea52f5fd4f6f728184225bdb867ed0dc2410eab6df5bdd67bb" +dependencies = [ + "arc-swap", + "async-trait", + "bytes", + "hex", + "interceptor", + "lazy_static", + "log", + "rand 0.8.5", + "rcgen 0.9.3", + "regex", + "ring", + "rtcp", + "rtp", + "rustls 0.19.1", + "sdp", + "serde", + "serde_json", + "sha2 0.10.6", + "stun", + "thiserror", + "time 0.3.17", + "tokio", + "turn", + "url", + "waitgroup", + "webrtc-data", + "webrtc-dtls", + "webrtc-ice", + "webrtc-mdns", + "webrtc-media", + "webrtc-sctp", + "webrtc-srtp", + "webrtc-util", +] + +[[package]] +name = "webrtc-data" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef36a4d12baa6e842582fe9ec16a57184ba35e1a09308307b67d43ec8883100" +dependencies = [ + "bytes", + "derive_builder", + "log", + "thiserror", + "tokio", + "webrtc-sctp", + "webrtc-util", +] + +[[package]] +name = "webrtc-dtls" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7021987ae0a2ed6c8cd33f68e98e49bb6e74ffe9543310267b48a1bbe3900e5f" +dependencies = [ + "aes 0.6.0", + "aes-gcm 0.8.0", + "async-trait", + "bincode", + "block-modes", + "byteorder", + "ccm", + "curve25519-dalek 3.2.0", + "der-parser 8.1.0", + "elliptic-curve", + "hkdf 0.12.3", + "hmac 0.10.1", + "log", + "oid-registry 0.6.0", + "p256", + "p384", + "rand 0.8.5", + "rand_core 0.6.4", + "rcgen 0.9.3", + "ring", + "rustls 0.19.1", + "sec1", + "serde", + "sha-1", + "sha2 0.9.9", + "signature", + "subtle", + "thiserror", + "tokio", + "webpki 0.21.4", + "webrtc-util", + "x25519-dalek 2.0.0-pre.1", + "x509-parser 0.13.2", +] + +[[package]] +name = "webrtc-ice" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494483fbb2f5492620871fdc78b084aed8807377f6e3fe88b2e49f0a9c9c41d7" +dependencies = [ + "arc-swap", + "async-trait", + "crc", + "log", + "rand 0.8.5", + "serde", + "serde_json", + "stun", + "thiserror", + "tokio", + "turn", + "url", + "uuid", + "waitgroup", + "webrtc-mdns", + "webrtc-util", +] + +[[package]] +name = "webrtc-mdns" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2548ae970afc2ae8e0b0dbfdacd9602d426d4f0ff6cda4602a45c0fd7ceaa82a" +dependencies = [ + "log", + "socket2", + "thiserror", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-media" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2a3c157a040324e5049bcbd644ffc9079e6738fa2cfab2bcff64e5cc4c00d7" +dependencies = [ + "byteorder", + "bytes", + "derive_builder", + "displaydoc", + "rand 0.8.5", + "rtp", + "thiserror", + "webrtc-util", +] + +[[package]] +name = "webrtc-sctp" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d47adcd9427eb3ede33d5a7f3424038f63c965491beafcc20bc650a2f6679c0" +dependencies = [ + "arc-swap", + "async-trait", + "bytes", + "crc", + "log", + "rand 0.8.5", + "thiserror", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-srtp" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6183edc4c1c6c0175f8812eefdce84dfa0aea9c3ece71c2bf6ddd3c964de3da5" +dependencies = [ + "aead 0.4.3", + "aes 0.7.5", + "aes-gcm 0.9.4", + "async-trait", + "byteorder", + "bytes", + "ctr 0.8.0", + "hmac 0.11.0", + "log", + "rtcp", + "rtp", + "sha-1", + "subtle", + "thiserror", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-util" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f1db1727772c05cf7a2cfece52c3aca8045ca1e176cd517d323489aa3c6d87" +dependencies = [ + "async-trait", + "bitflags", + "bytes", + "cc", + "ipnet", + "lazy_static", + "libc", + "log", + "nix", + "rand 0.8.5", + "thiserror", + "tokio", + "winapi", +] + [[package]] name = "wepoll-ffi" version = "0.1.2" @@ -4249,20 +5736,20 @@ dependencies = [ [[package]] name = "which" -version = "4.2.2" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea187a8ef279bc014ec368c27a920da2024d2a711109bfbe3440585d5cf27ad9" +checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" dependencies = [ "either", - "lazy_static", "libc", + "once_cell", ] [[package]] name = "widestring" -version = "0.4.3" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c" +checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" [[package]] name = "winapi" @@ -4280,17 +5767,112 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45296b64204227616fdbf2614cefa4c236b98ee64dfaaaa435207ed99fe7829f" +dependencies = [ + "windows_aarch64_msvc 0.34.0", + "windows_i686_gnu 0.34.0", + "windows_i686_msvc 0.34.0", + "windows_x86_64_gnu 0.34.0", + "windows_x86_64_msvc 0.34.0", +] + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + [[package]] name = "winreg" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" dependencies = [ "winapi", ] @@ -4301,70 +5883,96 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" dependencies = [ - "curve25519-dalek", + "curve25519-dalek 3.2.0", "rand_core 0.5.1", "zeroize", ] [[package]] -name = "xoodoo" -version = "0.1.0" +name = "x25519-dalek" +version = "2.0.0-pre.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1d3c62cbaa9cfdecac0b6aaac5cef3fae1de43c858ac902d6f78b6ee0926107" +checksum = "e5da623d8af10a62342bcbbb230e33e58a63255a58012f8653c578e54bab48df" dependencies = [ - "rawbytes", + "curve25519-dalek 3.2.0", + "rand_core 0.6.4", "zeroize", ] +[[package]] +name = "x509-parser" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9bace5b5589ffead1afb76e43e34cff39cd0f3ce7e170ae0c29e53b88eb1c" +dependencies = [ + "asn1-rs 0.3.1", + "base64 0.13.0", + "data-encoding", + "der-parser 7.0.0", + "lazy_static", + "nom", + "oid-registry 0.4.0", + "ring", + "rusticata-macros", + "thiserror", + "time 0.3.17", +] + +[[package]] +name = "x509-parser" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" +dependencies = [ + "asn1-rs 0.5.1", + "base64 0.13.0", + "data-encoding", + "der-parser 8.1.0", + "lazy_static", + "nom", + "oid-registry 0.6.0", + "rusticata-macros", + "thiserror", + "time 0.3.17", +] + [[package]] name = "yamux" -version = "0.9.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7d9028f208dd5e63c614be69f115c1b53cacc1111437d4c765185856666c107" +checksum = "e5d9ba232399af1783a58d8eb26f6b5006fbefe2dc9ef36bd283324792d03ea5" dependencies = [ "futures", "log", "nohash-hasher", - "parking_lot", - "rand 0.8.4", + "parking_lot 0.12.1", + "rand 0.8.5", "static_assertions", ] [[package]] -name = "zerocopy" +name = "yasna" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e59ec1d2457bd6c0dd89b50e7d9d6b0b647809bf3f0a59ac85557046950b7b2" +checksum = "346d34a236c9d3e5f3b9b74563f238f955bbd05fa0b8b4efa53c130c43982f4c" dependencies = [ - "byteorder", - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0af017aca1fa6181f5dd7a802456fe6f7666ecdcc18d0910431f0fc89d474e51" -dependencies = [ - "proc-macro2", - "syn", - "synstructure", + "time 0.3.17", ] [[package]] name = "zeroize" -version = "1.3.0" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.1.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2c1e130bebaeab2f23886bf9acbaca14b092408c452543c857f66399cd6dab1" +checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 67be0c0..69da705 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,72 +3,77 @@ members = [".", "cli", "harness"] [package] name = "ipfs-embed" -version = "0.22.4" -authors = ["David Craven "] +version = "0.26.1" +authors = ["David Craven ", "Roland Kuhn "] edition = "2018" license = "MIT OR Apache-2.0" description = "small embeddable ipfs implementation" repository = "https://github.com/ipfs-rust/ipfs-embed" [features] -default = ["async_global"] -async_global = ["async-global-executor", "libp2p/tcp-async-io", "libp2p/dns-async-std"] -tokio = ["tokio-crate", "libp2p/tcp-tokio", "libp2p/dns-tokio"] +default = ["async_global", "rsa", "ecdsa", "secp256k1"] +rsa = ["libp2p/rsa"] +ecdsa = ["libp2p/ecdsa"] +secp256k1 = ["libp2p/secp256k1"] +async_global = ["async-global-executor", "libp2p/async-std"] +tokio = ["tokio-crate", "libp2p/tokio"] telemetry = ["tide", "async_global"] +# Makes it possible to exchange data via Bitswap with a go-ipfs node +compat = ["libp2p-bitswap/compat"] [dependencies] -anyhow = "1.0.41" -async-global-executor = { version = "2.0.2", optional = true } -async-io = "1.6.0" -async-trait = "0.1.50" +anyhow = "1.0.56" +async-global-executor = { version = "2.0.3", optional = true } +async-trait = "0.1.52" +chrono = "0.4.19" fnv = "1.0.7" -futures = "0.3.15" +futures = "0.3.21" futures-timer = "3.0.2" -ipfs-sqlite-block-store = "0.7.0" +ipfs-sqlite-block-store = "0.13.0" lazy_static = "1.4.0" -libipld = { version = "0.12.0", default-features = false } -libp2p-bitswap = "0.19.0" -libp2p-blake-streams = "0.1.0" -libp2p-broadcast = "0.5.0" -libp2p-quic = { version = "0.6.1", features = ["noise"] } -names = "0.11.0" -parking_lot = "0.11.1" -pin-project = "1.0.7" -prometheus = "0.12.0" -thiserror = "1.0.26" +libipld = { version = "0.14.0", default-features = false } +libp2p-bitswap = "0.25.0" +libp2p-broadcast = "0.12.0" +names = "0.13.0" +parking_lot = "0.11.2" +pin-project = "1.0.10" +prometheus = "0.13.0" +rand = "0.8.5" +thiserror = "1.0.30" tide = { version = "0.16.0", optional = true } -tokio-crate = { package = "tokio", version = "1.8.0", features = ["rt"], optional = true } -tracing = "0.1.26" +tokio-crate = { package = "tokio", version = "1.17.0", features = ["rt"], optional = true } +tracing = "0.1.32" +trust-dns-resolver = "0.22.0" void = "1.0.2" [dependencies.libp2p] -version = "0.40.0" -default-features = false +version = "0.50.0" features = [ + "dns", "gossipsub", "identify", "kad", + "macros", "mdns", + "mplex", + "noise", "ping", - #"relay", - "mplex", "noise", "pnet", "yamux", + "pnet", + "tcp", + "yamux", ] [dev-dependencies] -async-std = { version = "1.9.0", features = ["attributes"] } -libipld = { version = "0.12.0", default-features = false, features = ["dag-cbor", "dag-pb", "derive"] } -libp2p-bitswap = { version = "0.19.0", default-features = false, features = ["compat"] } -multihash = { version = "0.14.0", default-features = false, features = ["blake3"] } -rand = "0.8.4" +anyhow = { version = "1", features = ["backtrace"] } +async-executor = "1.4.1" +async-std = { version = "1.11.0", features = ["attributes"] } +libipld = { version = "0.14.0", default-features = false, features = ["dag-cbor", "dag-pb", "derive"] } +libp2p-bitswap = { version = "0.25.0", default-features = false, features = ["compat"] } +multihash = { version = "0.16.1", default-features = false, features = ["blake3"] } +rand = "0.8.5" +regex = "1.5.5" tempdir = "0.3.7" -tracing-subscriber = "0.2.19" +tracing-subscriber = { version = "0.3.9", features = ["env-filter"] } [profile.release] debug = true - -[patch.crates-io] -libp2p = { git = "https://github.com/Actyx/rust-libp2p", branch = "sim-open" } -libp2p-bitswap = { git = "https://github.com/Actyx/libp2p-bitswap", branch = "sim-open" } -libp2p-broadcast = { git = "https://github.com/Actyx/libp2p-broadcast", branch = "sim-open-2" } -libp2p-quic = { git = "https://github.com/Actyx/libp2p-quic", branch = "sim-open-2" } -libp2p-blake-streams = { git = "https://github.com/Actyx/blake-streams", branch = "sim-open" } diff --git a/README.md b/README.md index a6b2035..bb33c4c 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ applications. * temporary recursive pins for building dags, preventing races with the garbage collector * efficiently syncing large dags of blocks -It does *not* aim at being compatible in any way with `go-ipfs`. +Some compatibility with go-ipfs can be enabled with the `compat` feature flag. ## Getting started ```rust @@ -263,9 +263,7 @@ where ## Efficient block storage implementation - ipfs-embed internals -Ipfs embed uses sled to implement the block store. Sled is a rust embedded key value store, -exposing an api that implements persistent lock free `BTreeMap` with support for transactions -involving multiple trees. +Ipfs embed uses SQLite to implement the block store, which is a performant embeddable SQL persistence layer / database. ```rust type Id = u64; diff --git a/cli/Cargo.toml b/cli/Cargo.toml index cdee5bd..66a4fa1 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -6,13 +6,17 @@ edition = "2018" publish = false [dependencies] -anyhow = "1.0.41" -async-process = "1.1.0" -async-std = { version = "1.9.0", features = ["attributes"] } -ed25519-dalek = "1.0.1" +anyhow = "1.0.56" +async-process = "1.3.0" +async-std = { version = "1.11.0", features = ["attributes"] } +chrono = "0.4.19" +futures = "0.3.24" ipfs-embed = { path = ".." } -libipld = { version = "0.12.0", default-features = false, features = ["dag-cbor"] } -multihash = { version = "0.14.0", default-features = false, features = ["blake3"] } -structopt = "0.3.22" -tracing = "0.1.26" -tracing-subscriber = "0.2.19" +libipld = { version = "0.14.0", default-features = false, features = ["dag-cbor"] } +multihash = { version = "0.16.1", default-features = false, features = ["blake3"] } +parking_lot = "0.11.2" +serde = { version = "1.0.136", features = ["derive"] } +serde_json = "1.0.79" +structopt = "0.3.26" +tracing = "0.1.32" +tracing-subscriber = { version = "0.3.9", features = ["env-filter"] } diff --git a/cli/src/lib.rs b/cli/src/lib.rs index 1158fc1..aab4804 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -1,7 +1,10 @@ use anyhow::Result; -use ed25519_dalek::{PublicKey, SecretKey}; -use ipfs_embed::{Block, Cid, DefaultParams, Keypair, Multiaddr, PeerId, StreamId, ToLibp2p}; -use std::path::PathBuf; +use ipfs_embed::{ + identity::ed25519::{Keypair, SecretKey}, + Block, Cid, DefaultParams, Multiaddr, PeerId, PeerInfo, +}; +use serde::{Deserialize, Serialize}; +use std::{collections::HashMap, path::PathBuf}; use structopt::StructOpt; #[derive(Debug, StructOpt)] @@ -21,6 +24,8 @@ pub struct Config { pub bootstrap: Vec, #[structopt(long)] pub external: Vec, + #[structopt(long)] + pub disable_port_reuse: bool, } impl Config { @@ -33,6 +38,7 @@ impl Config { bootstrap: vec![], external: vec![], enable_mdns: false, + disable_port_reuse: false, } } } @@ -70,26 +76,30 @@ impl From for async_process::Command { if config.enable_mdns { cmd.arg("--enable-mdns"); } + if config.disable_port_reuse { + cmd.arg("--disable-port-reuse"); + } cmd } } pub fn keypair(i: u64) -> Keypair { - let mut keypair = [0; 32]; - keypair[..8].copy_from_slice(&i.to_be_bytes()); - let secret = SecretKey::from_bytes(&keypair).unwrap(); - let public = PublicKey::from(&secret); - Keypair { secret, public } + let mut secret = [0; 32]; + secret[..8].copy_from_slice(&i.to_be_bytes()); + Keypair::from(SecretKey::from_bytes(secret).unwrap()) } pub fn peer_id(i: u64) -> PeerId { - keypair(i).to_peer_id() + ipfs_embed::identity::Keypair::Ed25519(keypair(i)) + .public() + .into() } #[derive(Debug, Eq, PartialEq)] pub enum Command { AddAddress(PeerId, Multiaddr), Dial(PeerId), + PrunePeers, Get(Cid), Insert(Block), Alias(String, Option), @@ -102,6 +112,7 @@ impl std::fmt::Display for Command { match self { Self::AddAddress(peer, addr) => write!(f, ">add-address {} {}", peer, addr)?, Self::Dial(peer) => write!(f, ">dial {}", peer)?, + Self::PrunePeers => write!(f, ">prune-peers")?, Self::Get(cid) => write!(f, ">get {}", cid)?, Self::Insert(block) => { write!(f, ">insert {} ", block.cid())?; @@ -137,6 +148,7 @@ impl std::str::FromStr for Command { let peer = parts.next().unwrap().parse()?; Self::Dial(peer) } + Some(">prune-peers") => Self::PrunePeers, Some(">get") => { let cid = parts.next().unwrap().parse()?; Self::Get(cid) @@ -185,7 +197,39 @@ pub enum Event { Flushed, Synced, Bootstrapped, - NewHead(StreamId, u64), + PeerInfo(PeerId, PeerInfoIo), + PeerRemoved(PeerId), + DialFailure(PeerId, Multiaddr, String), + ConnectionEstablished(PeerId, Multiaddr), + ConnectionClosed(PeerId, Multiaddr), +} + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PeerInfoIo { + pub protocol_version: Option, + pub agent_version: Option, + pub protocols: Vec, + pub listeners: Vec, + pub addresses: HashMap, + pub connections: Vec, + pub failures: Vec, +} + +impl From for PeerInfoIo { + fn from(info: PeerInfo) -> Self { + Self { + protocol_version: info.protocol_version().map(ToOwned::to_owned), + agent_version: info.agent_version().map(ToOwned::to_owned), + protocols: info.protocols().map(ToOwned::to_owned).collect(), + listeners: info.listen_addresses().cloned().collect(), + addresses: info + .addresses() + .map(|(a, s, _dt)| (a.clone(), format!("{:?}", s))) + .collect(), + connections: info.connections().map(|(a, ..)| a.clone()).collect(), + failures: info.recent_failures().map(|f| format!("{:?}", f)).collect(), + } + } } impl std::fmt::Display for Event { @@ -212,7 +256,13 @@ impl std::fmt::Display for Event { Self::Flushed => write!(f, " write!(f, " write!(f, " write!(f, " { + write!(f, " write!(f, " write!(f, " write!(f, " write!(f, " Self::Flushed, Some(" Self::Synced, Some(" Self::Bootstrapped, - Some(" { + Some(" { let id = parts.next().unwrap().parse()?; - let offset = parts.next().unwrap().parse()?; - Self::NewHead(id, offset) + let s = parts.collect::>().join(" "); + let info = serde_json::from_str(s.as_str())?; + Self::PeerInfo(id, info) + } + Some(" { + let id = parts.next().unwrap().parse()?; + Self::PeerRemoved(id) + } + Some(" { + let id = parts.next().unwrap().parse()?; + let addr = parts.next().unwrap().parse()?; + let error = parts.collect::>().join(" "); + Self::DialFailure(id, addr, error) + } + Some(" { + let id = parts.next().unwrap().parse()?; + let addr = parts.next().unwrap().parse()?; + Self::ConnectionEstablished(id, addr) + } + Some(" { + let id = parts.next().unwrap().parse()?; + let addr = parts.next().unwrap().parse()?; + Self::ConnectionClosed(id, addr) } - _ => return Err(anyhow::anyhow!("invalid event `{}`", s)), + _ => anyhow::bail!("invalid event `{}`", s), }) } } diff --git a/cli/src/main.rs b/cli/src/main.rs index 22d832f..6ac7d6e 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,15 +1,18 @@ use anyhow::Result; use async_std::stream::StreamExt; +use futures::TryFutureExt; use ipfs_embed::{DefaultParams, Ipfs, NetworkConfig, StorageConfig}; use ipfs_embed_cli::{keypair, Command, Config, Event}; -use std::io::Write; -use std::time::Duration; +use parking_lot::Mutex; +use std::{io::Write, sync::Arc, time::Duration}; use structopt::StructOpt; +use tracing_subscriber::fmt::format::FmtSpan; #[async_std::main] async fn main() { tracing_subscriber::fmt() .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .with_span_events(FmtSpan::ACTIVE | FmtSpan::CLOSE) .init(); if let Err(err) = run().await { tracing::error!("{}", err); @@ -33,6 +36,7 @@ async fn run() -> Result<()> { None }, kad: None, + port_reuse: !config.disable_port_reuse, ..Default::default() }; let node_name = if let Some(node_name) = config.node_name { @@ -42,11 +46,11 @@ async fn run() -> Result<()> { }; network.identify.as_mut().unwrap().agent_version = node_name; - let ipfs = Ipfs::::new(ipfs_embed::Config { storage, network }).await?; - let mut events = ipfs.swarm_events(); + let mut ipfs = Ipfs::::new(ipfs_embed::Config { storage, network }).await?; + let mut events = ipfs.swarm_events().await?; for addr in config.listen_on { - let _ = ipfs.listen_on(addr)?; + let _ = ipfs.listen_on(addr); } for addr in config.external { @@ -58,6 +62,9 @@ async fn run() -> Result<()> { unimplemented!() } + let ipfs = Arc::new(Mutex::new(ipfs)); + let ipfs2 = ipfs.clone(); + async_std::task::spawn(async move { while let Some(event) = events.next().await { let event = match event { @@ -82,7 +89,19 @@ async fn run() -> Result<()> { Some(Event::Unsubscribed(peer_id, topic)) } ipfs_embed::Event::Bootstrapped => Some(Event::Bootstrapped), - ipfs_embed::Event::NewHead(head) => Some(Event::NewHead(*head.id(), head.len())), + ipfs_embed::Event::NewInfo(peer) => match ipfs2.lock().peer_info(&peer) { + Some(info) => Some(Event::PeerInfo(peer, info.into())), + None => Some(Event::PeerRemoved(peer)), + }, + ipfs_embed::Event::ListenerError(_, _) => None, + ipfs_embed::Event::DialFailure(p, a, e) => Some(Event::DialFailure(p, a, e)), + ipfs_embed::Event::ConnectionEstablished(p, a) => Some( + Event::ConnectionEstablished(p, a.get_remote_address().clone()), + ), + ipfs_embed::Event::ConnectionClosed(p, a) => { + Some(Event::ConnectionClosed(p, a.get_remote_address().clone())) + } + ipfs_embed::Event::AddressChanged(_, _, _) => None, }; if let Some(event) = event { println!("{}", event); @@ -93,31 +112,38 @@ async fn run() -> Result<()> { loop { line.clear(); stdin.read_line(&mut line)?; - match line.parse()? { - Command::AddAddress(peer, addr) => { - ipfs.add_address(&peer, addr); - } - Command::Dial(peer) => { - ipfs.dial(&peer); - } - Command::Get(cid) => { - let block = ipfs.get(&cid)?; - writeln!(stdout, "{}", Event::Block(block))?; - } - Command::Insert(block) => { - ipfs.insert(&block)?; - } - Command::Alias(alias, cid) => { - ipfs.alias(&alias, cid.as_ref())?; - } - Command::Flush => { - ipfs.flush().await?; - writeln!(stdout, "{}", Event::Flushed)?; + #[allow(clippy::unit_arg)] + let result = match line.parse() { + Ok(Command::AddAddress(peer, addr)) => Ok(ipfs.lock().add_address(peer, addr)), + Ok(Command::Dial(peer)) => Ok(ipfs.lock().dial(peer)), + Ok(Command::PrunePeers) => Ok(ipfs.lock().prune_peers(Duration::ZERO)), + Ok(Command::Get(cid)) => ipfs + .lock() + .get(&cid) + .map(|block| writeln!(stdout, "{}", Event::Block(block)).expect("print")), + Ok(Command::Insert(block)) => ipfs.lock().insert(block), + Ok(Command::Alias(alias, cid)) => ipfs.lock().alias(&alias, cid.as_ref()), + Ok(Command::Flush) => { + let f = ipfs + .lock() + .flush() + .inspect_ok(|_| writeln!(stdout, "{}", Event::Flushed).expect("print")); + f.await } - Command::Sync(cid) => { - ipfs.sync(&cid, ipfs.peers()).await?; - writeln!(stdout, "{}", Event::Synced)?; + Ok(Command::Sync(cid)) => { + let providers = ipfs.lock().peers(); + tracing::debug!("sync {} from {:?}", cid, providers); + let f = ipfs + .lock() + .sync(&cid, providers) + .and_then(|f| f) + .inspect_ok(|_| writeln!(stdout, "{}", Event::Synced).expect("print")); + f.await } + Err(err) => Err(err), + }; + if let Err(err) = result { + eprintln!("main loop error (line = {}): {}", line, err); } } } diff --git a/examples/compat.rs b/examples/compat.rs index 855aa88..0091a6b 100644 --- a/examples/compat.rs +++ b/examples/compat.rs @@ -21,10 +21,10 @@ fn tracing_try_init() { async fn main() -> anyhow::Result<()> { tracing_try_init(); let config = Config::default(); - let ipfs = Ipfs::::new(config).await?; + let mut ipfs = Ipfs::::new(config).await?; let peer: PeerId = "QmRSGx67Kq8w7xSBDia7hQfbfuvauMQGgxcwSWw976x4BS".parse()?; let addr: Multiaddr = "/ip4/54.173.33.96/tcp/4001".parse()?; - ipfs.dial_address(&peer, addr); + ipfs.dial_address(peer, addr); // 10 random bytes let _cid_rand10: Cid = "QmXQsqVRpp2W7fbYZHi4aB2Xkqfd3DpwWskZoLVEYigMKC".parse()?; @@ -42,7 +42,7 @@ async fn main() -> anyhow::Result<()> { let block = ipfs.fetch(&cid_simple_dag, vec![peer]).await?; println!("got single block. len = {}", block.data().len()); - let mut updates = ipfs.sync(&cid_simple_dag, vec![peer]); + let mut updates = ipfs.sync(&cid_simple_dag, vec![peer]).await?; println!("starting sync of large file"); while let Some(update) = updates.next().await { println!("{:?}", update); diff --git a/examples/restart.rs b/examples/restart.rs index b69d02e..cac12a4 100644 --- a/examples/restart.rs +++ b/examples/restart.rs @@ -1,8 +1,10 @@ use anyhow::Result; use ipfs_embed::{Config, DefaultParams, Ipfs}; -use std::fs::File; -use std::io::{BufRead, BufReader}; -use std::time::Duration; +use std::{ + fs::File, + io::{BufRead, BufReader}, + time::Duration, +}; #[async_std::main] async fn main() -> anyhow::Result<()> { diff --git a/examples/sync.rs b/examples/sync.rs index 0b51b2a..cb93fce 100644 --- a/examples/sync.rs +++ b/examples/sync.rs @@ -1,10 +1,9 @@ use anyhow::Result; use futures::stream::StreamExt; -use ipfs_embed::{generate_keypair, Config, Ipfs}; -use libipld::cbor::DagCborCodec; -use libipld::multihash::Code; -use libipld::store::DefaultParams; -use libipld::{alias, Block, Cid, DagCbor}; +use ipfs_embed::{identity::ed25519::Keypair, Config, Ipfs}; +use libipld::{ + alias, cbor::DagCborCodec, multihash::Code, store::DefaultParams, Block, Cid, DagCbor, +}; use rand::RngCore; const ROOT: &str = alias!(root); @@ -44,27 +43,27 @@ async fn main() -> Result<()> { tracing_subscriber::fmt() .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) .init(); - let mut config = Config::new("/tmp/local1".as_ref(), generate_keypair()); + let mut config = Config::new("/tmp/local1".as_ref(), Keypair::generate()); config.network.kad = None; - let a = Ipfs::::new(config).await?; - a.listen_on("/ip4/127.0.0.1/tcp/0".parse()?)? + let mut a = Ipfs::::new(config).await?; + a.listen_on("/ip4/127.0.0.1/tcp/0".parse()?) .next() .await .unwrap(); - let mut config = Config::new("/tmp/local2".as_ref(), generate_keypair()); + let mut config = Config::new("/tmp/local2".as_ref(), Keypair::generate()); config.network.kad = None; let b = Ipfs::::new(config).await?; println!("starting import"); let start = std::time::Instant::now(); - let tmp = a.create_temp_pin()?; + let mut tmp = a.create_temp_pin()?; let mut builder = NodeBuilder::default(); for _ in 0..1000 { let block = builder.create()?; - a.temp_pin(&tmp, block.cid())?; - let _ = a.insert(&block)?; + a.temp_pin(&mut tmp, block.cid())?; + a.insert(block)?; } a.alias(ROOT, builder.prev.as_ref())?; a.flush().await?; @@ -77,6 +76,7 @@ async fn main() -> Result<()> { b.alias(ROOT, builder.prev.as_ref())?; b.sync(builder.prev.as_ref().unwrap(), vec![a.local_peer_id()]) + .await? .await?; b.flush().await?; diff --git a/harness/Cargo.toml b/harness/Cargo.toml index d18bc91..a2977de 100644 --- a/harness/Cargo.toml +++ b/harness/Cargo.toml @@ -1,22 +1,29 @@ [package] -name = "ipfs-embed-harness" +name = "harness" version = "0.1.0" authors = ["David Craven "] edition = "2018" publish = false [dependencies] -anyhow = "1.0.41" -async-global-executor = "2.0.2" -async-process = "1.1.0" -futures = "0.3.15" +anyhow = "1.0.56" +async-global-executor = "2.0.3" +async-process = "1.3.0" +async-std = "1.11.0" +escargot = "0.5.7" +futures = "0.3.21" ipfs-embed-cli = { path = "../cli" } -libipld = { version = "0.12.0", default-features = false, features = ["dag-cbor", "dag-pb", "derive"] } -libp2p = { version = "0.40.0", default-features = false } -multihash = { version = "0.14.0", default-features = false, features = ["blake3"] } -netsim-embed = "0.5.0" -rand = "0.8.4" -structopt = "0.3.22" +libipld = { version = "0.14.0", default-features = false, features = ["dag-cbor", "dag-pb", "derive"] } +libp2p = { version = "0.50.0", default-features = false } +maplit = "1.0.2" +multihash = { version = "0.16.1", default-features = false, features = ["blake3"] } +netsim-embed = "0.7.1" +rand = "0.8.5" +structopt = "0.3.26" tempdir = "0.3.7" -tracing = "0.1.26" -tracing-subscriber = "0.2.19" +tracing = "0.1.32" +tracing-subscriber = "0.3.9" + +[dev-dependencies] +assert_cmd = "2.0.4" +predicates = "2.1.1" diff --git a/harness/src/bin/bitswap.rs b/harness/src/bin/bitswap.rs index 7b1dc55..45d3643 100644 --- a/harness/src/bin/bitswap.rs +++ b/harness/src/bin/bitswap.rs @@ -1,121 +1,134 @@ #[cfg(target_os = "linux")] fn main() -> anyhow::Result<()> { + use anyhow::Context; + use futures::FutureExt; + use harness::{MachineExt, MultiaddrExt, MyFutureExt}; use ipfs_embed_cli::{Command, Event}; - use ipfs_embed_harness::{MachineExt, MultiaddrExt}; use libipld::alias; use std::time::Instant; - ipfs_embed_harness::run_netsim(|mut network, opts| async move { - let providers = 0..opts.n_providers; - let consumers = opts.n_providers..(opts.n_providers + opts.n_consumers); + harness::build_bin()?; - let mut peers = Vec::with_capacity(network.machines().len()); - for machine in network.machines_mut() { - let peer = machine.peer_id(); - loop { - if let Some(Event::NewListenAddr(addr)) = machine.recv().await { - if !addr.is_loopback() { - peers.push((peer, addr)); - break; + harness::run_netsim(|mut network, opts, _net, _tmp| { + async move { + let providers = 0..opts.n_providers; + let consumers = opts.n_providers..(opts.n_providers + opts.n_consumers); + + let mut peers = Vec::with_capacity(network.machines().len()); + for machine in network.machines_mut() { + let peer = machine.peer_id(); + loop { + if let Some(Event::NewListenAddr(addr)) = + machine.recv().timeout(3).await.unwrap() + { + if !addr.is_loopback() { + peers.push((peer, addr)); + break; + } } } } - } - for machine in network.machines_mut() { - for (peer, addr) in &peers { - machine.send(Command::AddAddress(*peer, addr.clone())); + // create some blocks in each node that will not participate in the sync + if opts.n_spam > 0 { + println!("creating spam data"); + } + for i in 0..opts.n_spam { + let alias = format!("passive-{}", i); + let (cid, blocks) = harness::build_tree(opts.tree_width, opts.tree_depth)?; + for machine in network.machines_mut() { + machine.send(Command::Alias(alias.clone(), Some(cid))); + for block in blocks.iter().rev() { + machine.send(Command::Insert(block.clone())); + } + } } - } - // create some blocks in each node that will not participate in the sync - if opts.n_spam > 0 { - println!("creating spam data"); - } - for i in 0..opts.n_spam { - let alias = format!("passive-{}", i); - let (cid, blocks) = - ipfs_embed_harness::build_tree(opts.tree_width, opts.tree_depth).unwrap(); - for machine in network.machines_mut() { - machine.send(Command::Alias(alias.clone(), Some(cid))); + // create the blocks to be synced in n_providers nodes + println!("creating test data"); + let root = alias!(root); + let (cid, blocks) = harness::build_tree(opts.tree_width, opts.tree_depth)?; + for machine in &mut network.machines_mut()[providers] { + machine.send(Command::Alias(root.to_string(), Some(cid))); for block in blocks.iter().rev() { machine.send(Command::Insert(block.clone())); } } - } - // create the blocks to be synced in n_providers nodes - println!("creating test data"); - let root = alias!(root); - let (cid, blocks) = - ipfs_embed_harness::build_tree(opts.tree_width, opts.tree_depth).unwrap(); - for machine in &mut network.machines_mut()[providers] { - machine.send(Command::Alias(root.to_string(), Some(cid))); - for block in blocks.iter().rev() { - machine.send(Command::Insert(block.clone())); + // flush test and spam data + for machine in network.machines_mut() { + machine.send(Command::Flush); } - } - - // flush test and spam data - for machine in network.machines_mut() { - machine.send(Command::Flush); - } - for machine in network.machines_mut() { - loop { - if let Some(Event::Flushed) = machine.recv().await { - break; - } + let started = Instant::now(); + for machine in network.machines_mut() { + machine + .select_draining(|e| matches!(e, Event::Flushed).then(|| ())) + .deadline(started, 10) + .await + .unwrap() + .unwrap(); } - } - - // compute total size of data to be synced - let size: usize = blocks.iter().map(|block| block.data().len()).sum(); - println!("test data built {} blocks, {} bytes", blocks.len(), size); - let t0 = Instant::now(); + // compute total size of data to be synced + let size: usize = blocks.iter().map(|block| block.data().len()).sum(); + println!("test data built {} blocks, {} bytes", blocks.len(), size); - for machine in &mut network.machines_mut()[consumers.clone()] { - machine.send(Command::Alias(root.to_string(), Some(cid))); - machine.send(Command::Sync(cid)); - } + let t0 = Instant::now(); - for machine in &mut network.machines_mut()[consumers.clone()] { - loop { - if let Some(Event::Synced) = machine.recv().await { - break; - } + for machine in &mut network.machines_mut()[consumers.clone()] { + machine.send(Command::Alias(root.to_string(), Some(cid))); + machine.send(Command::Sync(cid)); } - machine.send(Command::Flush); - loop { - if let Some(Event::Flushed) = machine.recv().await { - break; - } + + let started = Instant::now(); + for machine in &mut network.machines_mut()[consumers.clone()] { + machine + .select_draining(|e| matches!(e, Event::Synced).then(|| ())) + .deadline(started, 20) + .await + .unwrap() + .unwrap(); + machine.send(Command::Flush); + machine + .select_draining(|e| matches!(e, Event::Flushed).then(|| ())) + .timeout(1) + .await + .unwrap() + .unwrap(); } - } - println!( - "tree sync complete in {} ms {} blocks {} bytes {} providers {} consumers", - t0.elapsed().as_millis(), - blocks.len(), - size, - opts.n_providers, - opts.n_consumers, - ); + println!( + "tree sync complete in {} ms {} blocks {} bytes {} providers {} consumers", + t0.elapsed().as_millis(), + blocks.len(), + size, + opts.n_providers, + opts.n_consumers, + ); - for machine in &mut network.machines_mut()[consumers] { - // check that data is indeed synced - for block in &blocks { - machine.send(Command::Get(*block.cid())); - loop { - if let Some(Event::Block(data)) = machine.recv().await { - assert_eq!(&data, block); - break; - } + let started = Instant::now(); + for machine in &mut network.machines_mut()[consumers] { + // check that data is indeed synced + for block in &blocks { + machine.send(Command::Get(*block.cid())); + let data = machine + .select_draining(|e| match e { + Event::Block(data) => Some(data), + _ => None, + }) + .deadline(started, 5) + .await + .unwrap() + .unwrap(); + assert_eq!(&data, block); } } + Ok(()) } - Ok(()) + .timeout(240) + .map(|r| r.unwrap_or_else(|e| Err(e.into()))) }) + .context("netsim") } #[cfg(not(target_os = "linux"))] diff --git a/harness/src/bin/discover_nat.rs b/harness/src/bin/discover_nat.rs new file mode 100644 index 0000000..91dd4ac --- /dev/null +++ b/harness/src/bin/discover_nat.rs @@ -0,0 +1,192 @@ +#[cfg(target_os = "linux")] +fn main() -> anyhow::Result<()> { + use anyhow::Context; + use futures::FutureExt; + use harness::{MachineExt, MultiaddrExt, MyFutureExt, NetsimExt}; + use ipfs_embed_cli::{Command, Config, Event}; + use maplit::hashmap; + use netsim_embed::{Ipv4Range, NatConfig}; + use std::time::Instant; + + harness::build_bin()?; + + harness::run_netsim(|mut sim, opts, net_a, temp_dir| { + async move { + let range_b = { + let range_a = sim.network(net_a).range(); + loop { + let r = Ipv4Range::random_local_subnet(); + if r != range_a { + break r; + } + } + }; + let net_b = sim.spawn_network(range_b); + let nat = NatConfig::default(); + sim.add_nat_route(nat, net_a, net_b); + + let consumers = opts.n_nodes..2 * opts.n_nodes; + for i in consumers.clone() { + let cfg = Config { + path: Some(temp_dir.path().join(i.to_string())), + node_name: Some(format!("consumer-{}", i)), + keypair: i as _, + listen_on: vec!["/ip4/0.0.0.0/tcp/30000".parse().unwrap()], + bootstrap: vec![], + external: vec![], + enable_mdns: opts.enable_mdns, + disable_port_reuse: opts.disable_port_reuse, + }; + let cmd = async_process::Command::from(cfg); + let machine = sim.spawn_machine(cmd, None).await; + sim.plug(machine, net_b, None).await; + let m = sim.machine(machine); + tracing::warn!( + "{} started with address {} and peer id {}", + machine, + m.addr(), + m.peer_id(), + ); + } + + let providers = sim.nodes(0..opts.n_nodes); + let consumers = sim.nodes(consumers); + + let started = Instant::now(); + for id in providers.keys().chain(consumers.keys()) { + let m = sim.machine(*id); + m.select(|e| matches!(e, Event::NewListenAddr(a) if !a.is_loopback()).then(|| ())) + .deadline(started, 5) + .await + .unwrap(); + } + + for id in consumers.keys() { + let m = sim.machine(*id); + for (peer, addr) in providers.values() { + m.send(Command::AddAddress(*peer, addr.clone())); + m.send(Command::Dial(*peer)); + } + } + + let started = Instant::now(); + for id in consumers.keys() { + let m = sim.machine(*id); + for (peer, addr) in providers.values() { + let addrs = hashmap!(addr.clone() => "Dial".to_owned()); + m.select_draining(|e| { + matches!(e, + Event::PeerInfo(p, i) if p == *peer && i.addresses == addrs + ) + .then(|| ()) + }) + .deadline(started, 5) + .await + .unwrap(); + } + tracing::info!("consumer {} done", id); + } + + let started = Instant::now(); + if opts.disable_port_reuse { + for id in providers.keys() { + let m = sim.machine(*id); + for (m_id, (peer, _addr)) in consumers.iter() { + m.select(|e| { + matches!(e, + Event::PeerInfo(p, i) if p == peer && i.addresses.is_empty() + ) + .then(|| ()) + }) + .deadline(started, 10) + .await + .unwrap(); + tracing::info!("provider {} done with {}", id, m_id); + } + } + } else { + for id in providers.keys() { + let m = sim.machine(*id); + for (m_id, (peer, _addr)) in consumers.iter() { + m.select(|e| { + matches!(e, Event::PeerInfo(p, i) if p == peer && ( + // port_reuse unfortunately means that the NATed port is added to + // listeners by GenTcp, sent via Identify, but not falsifiable because + // we can’t attempt to dial while the connection exists + i.addresses.get(&i.connections[0]).map(|s| s.as_str()) == + Some("Candidate") + // can’t check for full hashmap equality since the state where only the + // Candidate is present may be lost to output race conditions + || i.addresses.is_empty() + // if consumer sent identify first, then the NAT address wasn’t known + // and only falsifiable listen addresses are left + )) + .then(|| ()) + }) + .deadline(started, 10) + .await + .unwrap(); + tracing::info!("provider {} identified {}", id, m_id); + } + m.drain(); + } + + // now disconnect the consumers so that the providers will try to dial and + // falsify the addresses + for id in consumers.keys() { + sim.machine(*id).down(); + } + + let started = Instant::now(); + for id in providers.keys() { + let m = sim.machine(*id); + for (m_id, (peer, _addr)) in consumers.iter() { + m.select(|e| matches!(e, Event::Disconnected(p) if p == peer).then(|| ())) + .deadline(started, 30) + .await + .unwrap(); + m.drain_matching(|e| { + matches!(e, + Event::DialFailure(p, ..) | Event::Unreachable(p) if (p == peer) + ) + }); + tracing::info!("provider {} saw close from {}", id, m_id); + m.send(Command::Dial(*peer)); + let alive = m + .select(|e| match e { + Event::DialFailure(p, ..) | Event::Unreachable(p) if p == peer => { + Some(true) + } + Event::PeerRemoved(p) if p == peer => Some(false), + _ => None, + }) + .timeout(10) + .await + .unwrap() + .unwrap(); + if alive { + m.send(Command::PrunePeers); + m.select(|e| { + // prune_peers will remove the peer when a failure happens while not + // connected + matches!(e, Event::PeerRemoved(p) if p == peer).then(|| ()) + }) + .timeout(10) + .await + .unwrap(); + } + tracing::info!("provider {} done with {}", id, m_id); + } + } + } + + Ok(()) + } + .timeout(120) + .map(|r| r.unwrap_or_else(|e| Err(e.into()))) + }) + .context("netsim") +} + +#[cfg(not(target_os = "linux"))] +fn main() {} diff --git a/harness/src/bin/discover_nat_forward.rs b/harness/src/bin/discover_nat_forward.rs new file mode 100644 index 0000000..1a546db --- /dev/null +++ b/harness/src/bin/discover_nat_forward.rs @@ -0,0 +1,260 @@ +#[cfg(target_os = "linux")] +fn main() -> anyhow::Result<()> { + use anyhow::Context; + use futures::FutureExt; + use harness::{MachineExt, MultiaddrExt, MyFutureExt, NetsimExt}; + use ipfs_embed_cli::{Command, Config, Event}; + use libp2p::{multiaddr::Protocol, Multiaddr}; + use maplit::hashmap; + use netsim_embed::{Ipv4Range, NatConfig}; + use std::{net::SocketAddrV4, time::Instant}; + + fn sock_addr(m: &Multiaddr) -> SocketAddrV4 { + let mut iter = m.iter(); + let ip = match iter.next().unwrap() { + Protocol::Ip4(ip) => ip, + _ => panic!("need IPv4"), + }; + let port = match iter.next().unwrap() { + Protocol::Tcp(p) => p, + _ => panic!("need TCP"), + }; + SocketAddrV4::new(ip, port) + } + + harness::build_bin()?; + + harness::run_netsim(|mut sim, opts, net_a, temp_dir| { + async move { + let range_b = { + let range_a = sim.network(net_a).range(); + loop { + let r = Ipv4Range::random_local_subnet(); + if r != range_a { + break r; + } + } + }; + let net_b = sim.spawn_network(range_b); + + let consumers = opts.n_nodes..2 * opts.n_nodes; + for i in consumers.clone() { + let cfg = Config { + path: Some(temp_dir.path().join(i.to_string())), + node_name: Some(format!("consumer-{}", i)), + keypair: i as _, + listen_on: vec!["/ip4/0.0.0.0/tcp/30000".parse().unwrap()], + bootstrap: vec![], + external: vec![], + enable_mdns: opts.enable_mdns, + disable_port_reuse: opts.disable_port_reuse, + }; + let cmd = async_process::Command::from(cfg); + let machine = sim.spawn_machine(cmd, None).await; + sim.plug(machine, net_b, None).await; + let m = sim.machine(machine); + tracing::warn!( + "{} started with address {} and peer id {}", + machine, + m.addr(), + m.peer_id(), + ); + } + + let providers = sim.nodes(0..opts.n_nodes); + let consumers = sim.nodes(consumers); + + let m_nat = sim.machines()[opts.n_nodes].id(); + let nat = NatConfig { + forward_ports: vec![( + netsim_embed::Protocol::Tcp, + 30000, + sock_addr(&consumers[&m_nat].1), + )], + ..Default::default() + }; + sim.add_nat_route(nat, net_a, net_b); + + let started = Instant::now(); + for id in providers.keys().chain(consumers.keys()) { + let m = sim.machine(*id); + m.select(|e| matches!(e, Event::NewListenAddr(a) if !a.is_loopback()).then(|| ())) + .deadline(started, 5) + .await + .unwrap(); + } + + for id in consumers.keys() { + let m = sim.machine(*id); + for (peer, addr) in providers.values() { + m.send(Command::AddAddress(*peer, addr.clone())); + m.send(Command::Dial(*peer)); + } + } + + let started = Instant::now(); + for id in consumers.keys() { + let m = sim.machine(*id); + for (peer, addr) in providers.values() { + m.select(|e| { + matches!(e, Event::PeerInfo(p, i) + if p == peer && i.addresses == hashmap!(addr.clone() => "Dial".to_owned()) + ) + .then(|| ()) + }) + .deadline(started, 5) + .await + .unwrap(); + } + tracing::info!("consumer {} done", id); + } + + let started = Instant::now(); + if opts.disable_port_reuse { + for id in providers.keys() { + let m = sim.machine(*id); + for (m_id, (peer, _addr)) in consumers.iter() { + if *m_id == m_nat { + let a_nat = m + .select(|e| match e { + Event::PeerInfo(p, i) if p == peer => { + let a = i.connections[0].clone(); + a.replace(1, |_| Some(Protocol::Tcp(30000))) + } + _ => None, + }) + .deadline(started, 5) + .await + .unwrap() + .unwrap(); + tracing::info!("NAT addr is {}", a_nat); + let addrs = hashmap!(a_nat.clone() => "Dial".to_owned()); + m.select(|e| { + matches!(e, + Event::PeerInfo(p, i) if p == peer && i.addresses == addrs + ) + .then(|| ()) + }) + .deadline(started, 10) + .await + .unwrap(); + } else { + m.select(|e| { + matches!(e, + Event::PeerInfo(p, i) if p == peer && i.addresses.is_empty() + ) + .then(|| ()) + }) + .deadline(started, 10) + .await + .unwrap(); + } + tracing::info!("provider {} done with {}", id, m_id); + } + } + } else { + // first wait until we have seen and Identify’ed all incoming connections + for id in providers.keys() { + let m = sim.machine(*id); + for (m_id, (peer, _addr)) in consumers.iter() { + let a_1 = m + .select(|e| match e { + Event::PeerInfo(p, i) if p == peer => { + Some(i.connections[0].clone()) + } + _ => None, + }) + .timeout(1) + .await + .unwrap() + .unwrap(); + // the NAT may give us the correct port in a_1 already, so no second entry to + // check + let a_nat = a_1 + .replace(1, |_| Some(Protocol::Tcp(30000))) + .filter(|a| *m_id == m_nat && *a != a_1); + tracing::info!("first address is {}, a_nat={:?}", a_1, a_nat); + m.select(|e| { + matches!(e, Event::PeerInfo(p, i) if p == peer && ( + // port_reuse unfortunately means that the NATed port is added to + // listeners by GenTcp, sent via Identify, but not falsifiable because + // we can’t attempt to dial while the connection exists + i.addresses.get(&a_1).map(|x| x.as_str()) == Some("Candidate") && + a_nat.iter().all(|a_nat| { + i.addresses.get(a_nat).map(|x| x.as_str()) == Some("Dial") + })) + // if consumer sent identify first, then the NAT address wasn’t known + // and only falsifiable listen addresses are left + || i.addresses.is_empty() + ) + .then(|| ()) + }) + .deadline(started, 10) + .await + .unwrap(); + tracing::info!("provider {} identified {}", id, m_id); + } + m.drain(); + } + + // now disconnect the consumers so that the providers will try to dial and falsify the + // addresses + for id in consumers.keys() { + sim.machine(*id).down(); + } + + let started = Instant::now(); + for id in providers.keys() { + // first wait until all connections are down + let m = sim.machine(*id); + for (m_id, (peer, _addr)) in consumers.iter() { + m.select(|e| matches!(e, Event::Disconnected(p) if p == peer).then(|| ())) + .deadline(started, 30) + .await + .unwrap(); + tracing::info!("provider {} saw close from {}", id, m_id); + } + m.drain(); + } + + // and check behaviour + for id in providers.keys() { + let m = sim.machine(*id); + for (m_id, (peer, _addr)) in consumers.iter() { + m.send(Command::Dial(*peer)); + let alive = m + .select(|e| match e { + Event::DialFailure(p, ..) | Event::Unreachable(p) if p == peer => { + Some(true) + } + Event::PeerRemoved(p) if p == peer => Some(false), + _ => None, + }) + .timeout(10) + .await + .unwrap() + .unwrap(); + if alive { + m.send(Command::PrunePeers); + m.select(|e| { + matches!(e, Event::PeerRemoved(p) if p == peer).then(|| ()) + }) + .timeout(10) + .await + .unwrap(); + } + tracing::info!("provider {} done with {}", id, m_id); + } + } + } + + Ok(()) + } + .timeout(120) + .map(|r| r.unwrap_or_else(|e| Err(e.into()))) + }) + .context("netsim") +} + +#[cfg(not(target_os = "linux"))] +fn main() {} diff --git a/harness/src/bin/discover_plain.rs b/harness/src/bin/discover_plain.rs new file mode 100644 index 0000000..d93dbb2 --- /dev/null +++ b/harness/src/bin/discover_plain.rs @@ -0,0 +1,79 @@ +#[cfg(target_os = "linux")] +fn main() -> anyhow::Result<()> { + use anyhow::Context; + use futures::FutureExt; + use harness::{MultiaddrExt, MyFutureExt, NetsimExt, Role}; + use ipfs_embed_cli::{Command, Event}; + use maplit::hashmap; + use std::time::Instant; + + harness::build_bin()?; + + harness::run_netsim(|mut sim, opts, _net, _tmp| { + async move { + let providers = sim.role(&opts, Role::Provider); + let consumers = sim.role(&opts, Role::Consumer); + + let started = Instant::now(); + for id in providers.keys().chain(consumers.keys()) { + let m = sim.machine(*id); + m.select(|e| matches!(e, Event::NewListenAddr(a) if !a.is_loopback()).then(|| ())) + .deadline(started, 5) + .await + .unwrap(); + } + + for id in consumers.keys() { + let m = sim.machine(*id); + for (peer, addr) in providers.values() { + m.send(Command::AddAddress(*peer, addr.clone())); + m.send(Command::Dial(*peer)); + } + } + + let started = Instant::now(); + for id in consumers.keys() { + let m = sim.machine(*id); + for (peer, addr) in providers.values() { + m.select(|e| { + matches!(e, Event::PeerInfo(p, i) + if p == peer && i.addresses == hashmap!(addr.clone() => "Dial".to_owned()) + ) + .then(|| ()) + }) + .deadline(started, 5) + .await + .unwrap(); + } + } + + let expected = if opts.disable_port_reuse { + "Dial" + } else { + "Candidate" + }; + for id in providers.keys() { + let m = sim.machine(*id); + for (peer, addr) in consumers.values() { + m.select(|e| { + matches!(e, Event::PeerInfo(p, i) + if p == peer && i.addresses == hashmap!(addr.clone() => expected.to_owned()) + ) + .then(|| ()) + }) + .deadline(started, 5) + .await + .unwrap(); + } + } + + Ok(()) + } + .timeout(120) + .map(|r| r.unwrap_or_else(|e| Err(e.into()))) + }) + .context("netsim") +} + +#[cfg(not(target_os = "linux"))] +fn main() {} diff --git a/harness/src/bin/sim_open.rs b/harness/src/bin/sim_open.rs index d635f61..8a20c54 100644 --- a/harness/src/bin/sim_open.rs +++ b/harness/src/bin/sim_open.rs @@ -1,91 +1,100 @@ #[cfg(target_os = "linux")] fn main() -> anyhow::Result<()> { + use futures::FutureExt; + use harness::{MachineExt, MultiaddrExt, MyFutureExt}; use ipfs_embed_cli::{Command, Config, Event}; - use ipfs_embed_harness::{MachineExt, MultiaddrExt}; use netsim_embed::{DelayBuffer, Ipv4Range, Netsim}; - use std::time::Duration; + use std::time::{Duration, Instant}; tracing_subscriber::fmt() .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) .init(); + + harness::build_bin()?; netsim_embed::unshare_user()?; - async_global_executor::block_on(async move { - let mut sim = Netsim::new(); - let net = sim.spawn_network(Ipv4Range::random_local_subnet()); - let mut wire = DelayBuffer::new(); - wire.set_delay(Duration::from_millis(100)); - let a = sim.spawn_machine(Config::new(0).into(), Some(wire)).await; - sim.plug(a, net, None).await; + async_global_executor::block_on( + async move { + let mut sim = Netsim::new(); + let net = sim.spawn_network(Ipv4Range::random_local_subnet()); + + let mut wire = DelayBuffer::new(); + wire.set_delay(Duration::from_millis(100)); + let a = sim.spawn_machine(Config::new(0).into(), Some(wire)).await; + sim.plug(a, net, None).await; - let mut wire = DelayBuffer::new(); - wire.set_delay(Duration::from_millis(100)); - let b = sim.spawn_machine(Config::new(1).into(), Some(wire)).await; - sim.plug(b, net, None).await; + let mut wire = DelayBuffer::new(); + wire.set_delay(Duration::from_millis(100)); + let b = sim.spawn_machine(Config::new(1).into(), Some(wire)).await; + sim.plug(b, net, None).await; - let ms = sim.machines_mut(); - let (a, ms) = ms.split_at_mut(1); - let a = &mut a[0]; - let (b, _) = ms.split_at_mut(1); - let b = &mut b[0]; - let a_id = a.peer_id(); - let a_addr = a.multiaddr(); - let b_id = b.peer_id(); - let b_addr = b.multiaddr(); + let ms = sim.machines_mut(); + let (a, ms) = ms.split_at_mut(1); + let a = &mut a[0]; + let (b, _) = ms.split_at_mut(1); + let b = &mut b[0]; + let a_id = a.peer_id(); + let a_addr = a.multiaddr(); + let b_id = b.peer_id(); + let b_addr = b.multiaddr(); - loop { - if let Some(Event::NewListenAddr(addr)) = a.recv().await { - if !addr.is_loopback() { - break; + loop { + if let Some(Event::NewListenAddr(addr)) = a.recv().await { + if !addr.is_loopback() { + break; + } } } - } - loop { - if let Some(Event::NewListenAddr(addr)) = b.recv().await { - if !addr.is_loopback() { - break; + loop { + if let Some(Event::NewListenAddr(addr)) = b.recv().await { + if !addr.is_loopback() { + break; + } } } - } - a.send(Command::AddAddress(b_id, b_addr)); - b.send(Command::AddAddress(a_id, a_addr)); - a.send(Command::Dial(b_id)); - b.send(Command::Dial(a_id)); + a.send(Command::AddAddress(b_id, b_addr)); + b.send(Command::AddAddress(a_id, a_addr)); + a.send(Command::Dial(b_id)); + b.send(Command::Dial(a_id)); - loop { - match a.recv().await { - Some(Event::Connected(id)) => { - if id == b_id { - break; + let now = Instant::now(); + loop { + match a.recv().deadline(now, 15).await.unwrap() { + Some(Event::Connected(id)) => { + if id == b_id { + break; + } } - } - Some(Event::Unreachable(id)) => { - if id == b_id { - return Err(anyhow::anyhow!("sim open failed")); + Some(Event::Unreachable(id)) => { + if id == b_id { + return Err(anyhow::anyhow!("sim open failed")); + } } + _ => {} } - _ => {} } - } - loop { - match b.recv().await { - Some(Event::Connected(id)) => { - if id == a_id { - break; + loop { + match b.recv().deadline(now, 15).await.unwrap() { + Some(Event::Connected(id)) => { + if id == a_id { + break; + } } - } - Some(Event::Unreachable(id)) => { - if id == a_id { - return Err(anyhow::anyhow!("sim open failed")); + Some(Event::Unreachable(id)) => { + if id == a_id { + return Err(anyhow::anyhow!("sim open failed")); + } } + _ => {} } - _ => {} } + Ok(()) } - Ok(()) - }) + .timeout(120) + .map(|r| r.unwrap_or_else(|e| Err(e.into()))), + ) } #[cfg(not(target_os = "linux"))] diff --git a/harness/src/lib.rs b/harness/src/lib.rs index 4f17c5e..d487bfa 100644 --- a/harness/src/lib.rs +++ b/harness/src/lib.rs @@ -1,15 +1,20 @@ #![cfg(target_os = "linux")] -use anyhow::Result; -use futures::prelude::*; +use anyhow::{Context, Result}; +use async_std::future::TimeoutError; +use futures::{future::BoxFuture, prelude::*}; use ipfs_embed_cli::{Command, Config, Event}; -use libipld::cbor::DagCborCodec; -use libipld::multihash::Code; -use libipld::{Block, Cid, DagCbor, DefaultParams}; -use libp2p::{multiaddr, Multiaddr, PeerId}; -use netsim_embed::{DelayBuffer, Ipv4Range, Netsim}; +use libipld::{cbor::DagCborCodec, multihash::Code, Block, Cid, DagCbor, DefaultParams}; +use libp2p::{multiaddr, multiaddr::Protocol, Multiaddr, PeerId}; +use netsim_embed::{DelayBuffer, Ipv4Range, MachineId, Netsim, NetworkId}; use rand::RngCore; -use std::time::Duration; +use std::{ + collections::HashMap, + fmt::{Debug, Display}, + ops::Range, + str::FromStr, + time::{Duration, Instant}, +}; use structopt::StructOpt; use tempdir::TempDir; @@ -38,6 +43,9 @@ pub struct HarnessOpts { #[structopt(long, default_value = "4")] pub tree_depth: u64, + + #[structopt(long)] + pub disable_port_reuse: bool, } pub trait MachineExt { @@ -70,15 +78,52 @@ impl MultiaddrExt for Multiaddr { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Role { + Provider, + Consumer, + Idle, +} +pub trait NetsimExt { + fn role(&self, opts: &HarnessOpts, r: Role) -> HashMap; + fn nodes(&self, range: Range) -> HashMap; +} +impl NetsimExt for Netsim +where + C: Display + Send + 'static, + E: FromStr + Display + Send + 'static, + E::Err: Display + Debug + Send + Sync, +{ + fn role(&self, opts: &HarnessOpts, r: Role) -> HashMap { + let range = match r { + Role::Provider => 0..opts.n_providers, + Role::Consumer => opts.n_providers..(opts.n_providers + opts.n_consumers), + Role::Idle => (opts.n_providers + opts.n_consumers)..opts.n_nodes, + }; + self.nodes(range) + } + fn nodes(&self, range: Range) -> HashMap { + self.machines()[range] + .iter() + .map(|m| { + let peer = m.peer_id(); + let mut addr = m.multiaddr(); + addr.push(Protocol::P2p(peer.into())); + (m.id(), (peer, addr)) + }) + .collect() + } +} + pub fn run_netsim(mut f: F) -> Result<()> where - F: FnMut(Netsim, HarnessOpts) -> F2, + F: FnMut(Netsim, HarnessOpts, NetworkId, TempDir) -> F2, F2: Future>, { tracing_subscriber::fmt() .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) .init(); - netsim_embed::unshare_user()?; + netsim_embed::unshare_user().context("unshare_user")?; let opts = HarnessOpts::from_args(); let temp_dir = TempDir::new("ipfs-embed-harness")?; async_global_executor::block_on(async move { @@ -101,6 +146,7 @@ where bootstrap: vec![], external: vec![], enable_mdns: opts.enable_mdns, + disable_port_reuse: opts.disable_port_reuse, }; let mut delay = DelayBuffer::new(); delay.set_delay(Duration::from_millis(opts.delay_ms)); @@ -115,10 +161,32 @@ where m.peer_id(), ); } - f(sim, opts).await + f(sim, opts, net, temp_dir).await }) } +pub trait MyFutureExt<'a>: 'a { + type Output; + fn timeout(self, d: u64) -> BoxFuture<'a, Result>; + fn deadline(self, i: Instant, d: u64) -> BoxFuture<'a, Result>; +} +impl<'a, F: Future + Send + 'a> MyFutureExt<'a> for F { + type Output = F::Output; + fn timeout(self, dur: u64) -> BoxFuture<'a, Result> { + async_std::future::timeout(Duration::from_secs(dur), self).boxed() + } + fn deadline(self, i: Instant, d: u64) -> BoxFuture<'a, Result> { + let elapsed = i.elapsed(); + let d = Duration::from_secs(d); + let dur = if elapsed + Duration::from_millis(100) >= d { + Duration::from_millis(100) + } else { + d - elapsed + }; + async_std::future::timeout(dur, self).boxed() + } +} + #[derive(Debug, DagCbor)] pub struct Node { pub links: Vec, @@ -165,6 +233,34 @@ fn build_tree_0(width: u64, depth: u64, blocks: &mut Vec>) pub fn build_tree(width: u64, depth: u64) -> Result<(Cid, Vec>)> { let mut blocks = vec![]; - let cid = build_tree_0(width, depth, &mut blocks)?; + let cid = build_tree_0(width, depth, &mut blocks).context("build_tree")?; Ok((cid, blocks)) } + +pub fn build_bin() -> Result<()> { + use escargot::CargoBuild; + + for msg in CargoBuild::new() + .manifest_path(concat!(env!("CARGO_MANIFEST_DIR"), "/../cli/Cargo.toml")) + .bin("ipfs-embed-cli") + .current_release() + .exec()? + { + match msg?.decode()? { + escargot::format::Message::BuildFinished(x) => println!("{:?}", x), + escargot::format::Message::CompilerArtifact(x) => { + if !x.fresh { + println!("{:?}", x.package_id); + } + } + escargot::format::Message::CompilerMessage(x) => { + if let Some(msg) = x.message.rendered { + println!("{}", msg); + } + } + escargot::format::Message::BuildScriptExecuted(_) => {} + escargot::format::Message::Unknown => {} + } + } + Ok(()) +} diff --git a/harness/tests/netsim.rs b/harness/tests/netsim.rs new file mode 100644 index 0000000..1eba105 --- /dev/null +++ b/harness/tests/netsim.rs @@ -0,0 +1,107 @@ +use assert_cmd::prelude::OutputAssertExt; +use escargot::CargoBuild; +use predicates::{ + boolean::PredicateBooleanExt, + str::{contains, is_empty}, +}; +use std::io::Write; + +fn run(bin: &str, args: impl IntoIterator) -> anyhow::Result<()> { + let cmd = CargoBuild::new() + .manifest_path(concat!(env!("CARGO_MANIFEST_DIR"), "/Cargo.toml")) + .bin(bin) + .run()? + .command() + .args(args) + .env( + "RUST_LOG", + "netsim_embed_machine=debug,ipfs_embed=trace,multi=debug,info", + ) + .assert(); + let out = cmd.get_output().stdout.clone(); + let err = cmd.get_output().stderr.clone(); + let must_contain = if bin == "sim_open" { + "scheduling redial after presumed TCP simultaneous open" + } else { + "DEBUG" + }; + (|| { + cmd.try_stderr(is_empty())? + .try_stdout(contains("ERROR").not().and(contains(must_contain)))? + .try_success() + })() + .map(|_| ()) + .map_err(|e| { + eprintln!("--- stdout"); + std::io::stderr().write_all(out.as_slice()).ok(); + eprintln!("--- stderr"); + std::io::stderr().write_all(err.as_slice()).ok(); + eprintln!("---"); + e.into() + }) +} + +#[cfg(target_os = "linux")] +#[test] +fn netsim_bitswap() -> anyhow::Result<()> { + run( + "bitswap", + ["--enable-mdns", "--tree-depth=2", "--tree-width=10"], + ) +} + +#[cfg(target_os = "linux")] +#[test] +fn netsim_bitswap_no_reuse() -> anyhow::Result<()> { + run( + "bitswap", + [ + "--enable-mdns", + "--tree-depth=2", + "--disable-port-reuse", + "--tree-width=10", + ], + ) +} + +#[cfg(target_os = "linux")] +#[test] +fn netsim_discover_nat_forward() -> anyhow::Result<()> { + run("discover_nat_forward", []) +} + +#[cfg(target_os = "linux")] +#[test] +fn netsim_discover_nat_forward_no_reuse() -> anyhow::Result<()> { + run("discover_nat_forward", ["--disable-port-reuse"]) +} + +#[cfg(target_os = "linux")] +#[test] +fn netsim_discover_nat() -> anyhow::Result<()> { + run("discover_nat", []) +} + +#[cfg(target_os = "linux")] +#[test] +fn netsim_discover_nat_no_reuse() -> anyhow::Result<()> { + run("discover_nat", ["--disable-port-reuse"]) +} + +#[cfg(target_os = "linux")] +#[test] +fn netsim_discover_plain() -> anyhow::Result<()> { + run("discover_plain", []) +} + +#[cfg(target_os = "linux")] +#[test] +fn netsim_discover_plain_no_reuse() -> anyhow::Result<()> { + run("discover_plain", ["--disable-port-reuse"]) +} + +#[cfg(target_os = "linux")] +#[test] +fn netsim_sim_open() -> anyhow::Result<()> { + run("sim_open", []) +} diff --git a/src/db.rs b/src/db.rs index 3ab88b9..0446e76 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,68 +1,70 @@ -use ipfs_sqlite_block_store::cache::{CacheTracker, InMemCacheTracker}; pub use ipfs_sqlite_block_store::TempPin; use ipfs_sqlite_block_store::{ - cache::SqliteCacheTracker, BlockStore, Config, SizeTargets, Synchronous, + cache::{CacheTracker, InMemCacheTracker, SqliteCacheTracker}, + BlockStore, Config, Synchronous, }; use lazy_static::lazy_static; -use libipld::codec::References; -use libipld::store::StoreParams; -use libipld::{Block, Cid, Ipld, Result}; +use libipld::{codec::References, store::StoreParams, Block, Cid, Ipld, Result}; use parking_lot::Mutex; -use prometheus::core::{Collector, Desc}; -use prometheus::proto::MetricFamily; -use prometheus::{HistogramOpts, HistogramVec, IntCounterVec, IntGauge, Opts, Registry}; -use std::future::Future; -use std::path::PathBuf; -use std::sync::Arc; -use std::time::Duration; +use prometheus::{ + core::{Collector, Desc}, + proto::MetricFamily, + HistogramOpts, HistogramVec, IntCounterVec, IntGauge, Opts, Registry, +}; +use std::{future::Future, path::PathBuf, sync::Arc, time::Duration}; use tracing::info; use crate::executor::{Executor, JoinHandle}; +use std::collections::HashSet; /// Storage configuration. #[derive(Clone, Debug, Eq, PartialEq)] pub struct StorageConfig { - /// The path to use for the block store. If it is `None` an in-memory block store - /// will be used. + /// The path to use for the block store. If it is `None` an in-memory block + /// store will be used. pub path: Option, - /// The path to use for the database that persists block accesses times for the LRU - /// cache. If this is set to 'None', access times will not be persisted but just - /// tracked in memory. + /// The path to use for the database that persists block accesses times for + /// the LRU cache. If this is set to 'None', access times will not be + /// persisted but just tracked in memory. /// - /// You can point this to the same file as the main block store, but this is not - /// recommended. + /// You can point this to the same file as the main block store, but this is + /// not recommended. pub access_db_path: Option, /// The target number of blocks. /// /// Up to this number, the store will retain everything even if - /// not pinned. Once this number is exceeded, the store will run garbage collection - /// of all unpinned blocks until the block criterion is met again. + /// not pinned. Once this number is exceeded, the store will run garbage + /// collection of all unpinned blocks until the block criterion is met + /// again. /// - /// To completely disable storing of non-pinned blocks, set this to 0. Even then, - /// the store will never delete pinned blocks. + /// To completely disable storing of non-pinned blocks, set this to 0. Even + /// then, the store will never delete pinned blocks. pub cache_size_blocks: u64, /// The target store size. /// - /// Up to this size, the store will retain everything even if not pinned. Once this - /// size is exceeded, the store will run garbage collection of all unpinned blocks - /// until the size criterion is met again. + /// Up to this size, the store will retain everything even if not pinned. + /// Once this size is exceeded, the store will run garbage collection of + /// all unpinned blocks until the size criterion is met again. /// /// The store will never delete pinned blocks. pub cache_size_bytes: u64, /// The interval at which the garbage collector is run. /// - /// Note that this is implemented as delays between gcs, so it will not run exactly at this - /// interval, but there will be some drift if gc takes long. + /// Note that this is implemented as delays between gcs, so it will not run + /// exactly at this interval, but there will be some drift if gc takes + /// long. pub gc_interval: Duration, /// The minimum number of blocks to collect in any case. /// - /// Using this parameter, it is possible to guarantee a minimum rate with which the gc will - /// be able to keep up. It is `gc_min_blocks` / `gc_interval`. + /// Using this parameter, it is possible to guarantee a minimum rate with + /// which the gc will be able to keep up. It is `gc_min_blocks` / + /// `gc_interval`. pub gc_min_blocks: usize, /// The target maximum gc duration of a single garbage collector run. /// - /// This can not be guaranteed, since we guarantee to collect at least `gc_min_blocks`. But - /// as soon as this duration is exceeded, the incremental gc will stop doing additional work. + /// This can not be guaranteed, since we guarantee to collect at least + /// `gc_min_blocks`. But as soon as this duration is exceeded, the + /// incremental gc will stop doing additional work. pub gc_target_duration: Duration, } @@ -124,9 +126,8 @@ where Ipld: References, { pub fn open(config: StorageConfig, executor: Executor) -> Result { - let size = SizeTargets::new(config.cache_size_blocks, config.cache_size_bytes); let store_config = Config::default() - .with_size_targets(size) + .with_size_targets(config.cache_size_blocks, config.cache_size_bytes) .with_pragma_synchronous(Synchronous::Normal); let tracker: Arc = if let Some(path) = config.access_db_path { let path = if path.is_file() { @@ -139,6 +140,9 @@ where } else { Arc::new(InMemCacheTracker::new(|access, _| Some(access))) }; + + let is_memory = config.path.is_none(); + // create DB connection let store = if let Some(path) = config.path { let path = if path.is_file() { path @@ -151,45 +155,41 @@ where BlockStore::memory(store_config.with_cache_tracker(tracker))? }; let store = Arc::new(Mutex::new(store)); - let gc = store.clone(); + + // spawn GC task let gc_interval = config.gc_interval; let gc_min_blocks = config.gc_min_blocks; let gc_target_duration = config.gc_target_duration; - let gc_task = executor.spawn(async move { - enum Phase { - Gc, - Delete, - } - let mut phase = Phase::Gc; - loop { - futures_timer::Delay::new(gc_interval / 2).await; - info!("going for gc!"); - match phase { - Phase::Gc => { - tracing::trace!("gc_loop running incremental gc"); - gc.lock() - .incremental_gc(gc_min_blocks, gc_target_duration) - .map_err(|e| { - tracing::warn!("failure during incremental gc: {}", e); - e - }) - .ok(); - phase = Phase::Delete; - } - Phase::Delete => { - tracing::trace!("gc_loop running incremental delete orphaned"); - gc.lock() - .incremental_delete_orphaned(gc_min_blocks, gc_target_duration) - .map_err(|e| { - tracing::warn!("failure during delete_orphaned: {}", e); - e - }) - .ok(); - phase = Phase::Gc; - } + let gc_task = if is_memory { + let gc = store.clone(); + executor.spawn(async move { + loop { + futures_timer::Delay::new(gc_interval).await; + info!("going for gc!"); + gc.lock() + .incremental_gc(gc_min_blocks, gc_target_duration) + .map_err(|e| { + tracing::warn!("failure during incremental gc: {:#}", e); + e + }) + .ok(); } - } - }); + }) + } else { + let mut gc = store.lock().additional_connection()?; + executor.spawn(async move { + loop { + futures_timer::Delay::new(gc_interval).await; + info!("going for gc!"); + gc.incremental_gc(gc_min_blocks, gc_target_duration) + .map_err(|e| { + tracing::warn!("failure during incremental gc: {:#}", e); + e + }) + .ok(); + } + }) + }; Ok(Self { executor, gc_target_duration: config.gc_target_duration, @@ -204,32 +204,27 @@ impl StorageService where Ipld: References, { - pub fn ro) -> Result, R>( - &self, - op: &'static str, - f: F, - ) -> Result { - observe_query(op, || { - let mut lock = self.inner.store.lock(); - let mut txn = Batch(lock.transaction()?); - f(&mut txn) - }) - } - pub fn rw) -> Result, R>( &self, op: &'static str, f: F, ) -> Result { - observe_query(op, || { - let mut lock = self.inner.store.lock(); - let mut txn = Batch(lock.transaction()?); - let res = f(&mut txn); - if res.is_ok() { - txn.0.commit()?; - } - res - }) + QUERIES_TOTAL.with_label_values(&[op]).inc(); + let timer = QUERY_DURATION + .with_label_values(&["lock_wait"]) + .start_timer(); + let mut lock = self.inner.store.lock(); + let t = timer.stop_and_record(); + if t > 1.0 { + tracing::warn!(op, "very long storage lock wait time of {:.1}s", t); + } + let _timer = QUERY_DURATION.with_label_values(&[op]).start_timer(); + let mut txn = Batch(lock.transaction()); + let res = f(&mut txn); + if res.is_ok() { + txn.0.commit()?; + } + res } pub fn create_temp_pin(&self) -> Result { @@ -238,25 +233,25 @@ where pub fn temp_pin( &self, - temp: &TempPin, + temp: &mut TempPin, iter: impl IntoIterator + Send + 'static, ) -> Result<()> { self.rw("temp_pin", |x| x.temp_pin(temp, iter)) } pub fn iter(&self) -> Result> { - self.ro("iter", |x| x.iter()) + self.rw("iter", |x| x.iter()) } pub fn contains(&self, cid: &Cid) -> Result { - self.ro("contains", |x| x.contains(cid)) + self.rw("contains", |x| x.contains(cid)) } pub fn get(&self, cid: &Cid) -> Result>> { - self.ro("get", |x| x.get(cid)) + self.rw("get", |x| x.get(cid)) } - pub fn insert(&self, block: &Block) -> Result<()> { + pub fn insert(&self, block: Block) -> Result<()> { self.rw("insert", |x| x.insert(block)) } @@ -264,46 +259,45 @@ where self.rw("alias", |x| x.alias(alias, cid)) } + pub fn aliases(&self) -> Result, Cid)>> { + self.rw("aliases", |x| x.aliases()) + } + pub fn resolve(&self, alias: &[u8]) -> Result> { - self.ro("resolve", |x| x.resolve(alias)) + self.rw("resolve", |x| x.resolve(alias)) } - pub fn reverse_alias(&self, cid: &Cid) -> Result>>> { - self.ro("reverse_alias", |x| x.reverse_alias(cid)) + pub fn reverse_alias(&self, cid: &Cid) -> Result>>> { + self.rw("reverse_alias", |x| x.reverse_alias(cid)) } pub fn missing_blocks(&self, cid: &Cid) -> Result> { - self.ro("missing_blocks", |x| x.missing_blocks(cid)) + self.rw("missing_blocks", |x| x.missing_blocks(cid)) } - pub async fn evict(&self) -> Result<()> { + pub fn evict(&self) -> impl Future> { let store = self.inner.store.clone(); let gc_min_blocks = self.inner.gc_min_blocks; let gc_target_duration = self.inner.gc_target_duration; - self.inner - .executor - .spawn_blocking(move || { - while !store - .lock() - .incremental_gc(gc_min_blocks, gc_target_duration)? - {} - while !store - .lock() - .incremental_delete_orphaned(gc_min_blocks, gc_target_duration)? - { - } - Ok(()) - }) - .await? + let evict = self.inner.executor.spawn_blocking(move || { + while !store + .lock() + .incremental_gc(gc_min_blocks, gc_target_duration)? + { + tracing::trace!("x"); + } + Ok(()) + }); + async { evict.await? } } - pub async fn flush(&self) -> Result<()> { + pub fn flush(&self) -> impl Future> { let store = self.inner.store.clone(); let flush = self .inner .executor .spawn_blocking(move || store.lock().flush()); - Ok(observe_future("flush", flush).await??) + async { Ok(observe_future("flush", flush).await??) } } pub fn register_metrics(&self, registry: &Registry) -> Result<()> { @@ -329,27 +323,16 @@ lazy_static! { HistogramOpts::new( "block_store_query_duration", "Duration of store queries labelled by type.", - ), + ) + .buckets(vec![ + 0.0001, 0.0002, 0.0005, 0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1.0, 2.0, + 5.0, 10.0 + ]), &["type"], ) .unwrap(); } -fn observe_query(name: &'static str, query: F) -> Result -where - F: FnOnce() -> Result, -{ - QUERIES_TOTAL.with_label_values(&[name]).inc(); - let timer = QUERY_DURATION.with_label_values(&[name]).start_timer(); - let res = query(); - if res.is_ok() { - timer.observe_duration(); - } else { - timer.stop_and_discard(); - } - res -} - async fn observe_future(name: &'static str, query: F) -> Result where F: Future>, @@ -362,7 +345,7 @@ where } else { timer.stop_and_discard(); } - Ok(res?) + res } struct SqliteStoreCollector { @@ -418,13 +401,13 @@ where S: StoreParams, Ipld: References, { - pub fn create_temp_pin(&self) -> Result { + pub fn create_temp_pin(&mut self) -> Result { Ok(self.0.temp_pin()) } pub fn temp_pin( - &self, - temp: &TempPin, + &mut self, + temp: &mut TempPin, iter: impl IntoIterator + Send + 'static, ) -> Result<()> { for link in iter { @@ -433,12 +416,12 @@ where Ok(()) } - pub fn iter(&self) -> Result> { + pub fn iter(&mut self) -> Result> { let cids = self.0.get_block_cids::>()?; Ok(cids.into_iter()) } - pub fn contains(&self, cid: &Cid) -> Result { + pub fn contains(&mut self, cid: &Cid) -> Result { Ok(self.0.has_block(cid)?) } @@ -446,11 +429,11 @@ where Ok(self.0.get_block(cid)?) } - pub fn insert(&mut self, block: &Block) -> Result<()> { + pub fn insert(&mut self, block: Block) -> Result<()> { Ok(self.0.put_block(block, None)?) } - pub fn resolve(&self, alias: &[u8]) -> Result> { + pub fn resolve(&mut self, alias: &[u8]) -> Result> { Ok(self.0.resolve(alias)?) } @@ -458,11 +441,15 @@ where Ok(self.0.alias(alias, cid)?) } - pub fn reverse_alias(&self, cid: &Cid) -> Result>>> { + pub fn aliases(&mut self) -> Result, Cid)>> { + Ok(self.0.aliases()?) + } + + pub fn reverse_alias(&mut self, cid: &Cid) -> Result>>> { Ok(self.0.reverse_alias(cid)?) } - pub fn missing_blocks(&self, cid: &Cid) -> Result> { + pub fn missing_blocks(&mut self, cid: &Cid) -> Result> { Ok(self.0.get_missing_blocks(cid)?) } } @@ -472,10 +459,7 @@ mod tests { use crate::executor::Executor; use super::*; - use libipld::cbor::DagCborCodec; - use libipld::multihash::Code; - use libipld::store::DefaultParams; - use libipld::{alias, ipld}; + use libipld::{alias, cbor::DagCborCodec, ipld, multihash::Code, store::DefaultParams}; fn create_block(ipld: &Ipld) -> Block { Block::encode(DagCborCodec, Code::Blake3_256, ipld).unwrap() @@ -527,31 +511,29 @@ mod tests { async fn test_store_evict() { tracing_try_init(); let store = create_store(); - let blocks = [ - create_block(&ipld!(0)), - create_block(&ipld!(1)), - create_block(&ipld!(2)), - create_block(&ipld!(3)), - ]; - store.insert(&blocks[0]).unwrap(); - store.insert(&blocks[1]).unwrap(); + let a = create_block(&ipld!(0)); + let b = create_block(&ipld!(1)); + let c = create_block(&ipld!(2)); + let d = create_block(&ipld!(3)); + store.insert(a.clone()).unwrap(); + store.insert(b.clone()).unwrap(); store.flush().await.unwrap(); store.evict().await.unwrap(); - assert_unpinned!(&store, &blocks[0]); - assert_unpinned!(&store, &blocks[1]); - store.insert(&blocks[2]).unwrap(); + assert_unpinned!(&store, &a); + assert_unpinned!(&store, &b); + store.insert(c.clone()).unwrap(); store.flush().await.unwrap(); store.evict().await.unwrap(); - assert_evicted!(&store, &blocks[0]); - assert_unpinned!(&store, &blocks[1]); - assert_unpinned!(&store, &blocks[2]); - store.get(blocks[1].cid()).unwrap(); - store.insert(&blocks[3]).unwrap(); + assert_evicted!(&store, &a); + assert_unpinned!(&store, &b); + assert_unpinned!(&store, &c); + store.get(b.cid()).unwrap(); + store.insert(d.clone()).unwrap(); store.flush().await.unwrap(); store.evict().await.unwrap(); - assert_unpinned!(&store, &blocks[1]); - assert_evicted!(&store, &blocks[2]); - assert_unpinned!(&store, &blocks[3]); + assert_unpinned!(&store, &b); + assert_evicted!(&store, &c); + assert_unpinned!(&store, &d); } #[async_std::test] @@ -564,9 +546,9 @@ mod tests { let c = create_block(&ipld!({ "c": [a.cid()] })); let x = alias!(x).as_bytes().to_vec(); let y = alias!(y).as_bytes().to_vec(); - store.insert(&a).unwrap(); - store.insert(&b).unwrap(); - store.insert(&c).unwrap(); + store.insert(a.clone()).unwrap(); + store.insert(b.clone()).unwrap(); + store.insert(c.clone()).unwrap(); store.alias(&x, Some(b.cid())).unwrap(); store.alias(&y, Some(c.cid())).unwrap(); store.flush().await.unwrap(); @@ -594,8 +576,8 @@ mod tests { let b = create_block(&ipld!({ "b": [a.cid()] })); let x = alias!(x).as_bytes().to_vec(); let y = alias!(y).as_bytes().to_vec(); - store.insert(&a).unwrap(); - store.insert(&b).unwrap(); + store.insert(a.clone()).unwrap(); + store.insert(b.clone()).unwrap(); store.alias(&x, Some(b.cid())).unwrap(); store.alias(&y, Some(b.cid())).unwrap(); store.flush().await.unwrap(); diff --git a/src/executor.rs b/src/executor.rs index bc25059..a199813 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,10 +1,12 @@ -//! ipfs-embed supports different configuration of the used async executor to spawn its background -//! tasks (network, garbage collection, etc.). Those can be configured with the following feature -//! flags: -//! * `async_global`: Uses the `async-global-executor` crate (with async-std). This is the default. -//! * `tokio`: Uses a user provided tokio >= 1.0 runtime to spawn its background tasks. Note, that -//! for this to work `ipfs-embed` needs to be executed within the context of a tokio runtime. -//! ipfs-embed won't spawn any on its own. +//! ipfs-embed supports different configuration of the used async executor to +//! spawn its background tasks (network, garbage collection, etc.). Those can be +//! configured with the following feature flags: +//! * `async_global`: Uses the `async-global-executor` crate (with async-std). +//! This is the default. +//! * `tokio`: Uses a user provided tokio >= 1.0 runtime to spawn its background +//! tasks. Note, that +//! for this to work `ipfs-embed` needs to be executed within the context of a +//! tokio runtime. ipfs-embed won't spawn any on its own. use futures::{Future, FutureExt}; use pin_project::pin_project; @@ -63,6 +65,12 @@ impl Executor { } } +impl Default for Executor { + fn default() -> Self { + Self::new() + } +} + #[pin_project(project = EnumProj)] pub enum JoinHandle { #[cfg(feature = "tokio")] @@ -82,8 +90,9 @@ impl JoinHandle { } #[allow(unused_mut)] pub fn abort(mut self) { - #[cfg(feature = "tokio")] - if let Self::Tokio(t) = self { + #[cfg(all(feature = "tokio", not(feature = "async_global")))] + { + let Self::Tokio(t) = self; t.abort() } // async-global-executor task cancels when drop @@ -120,7 +129,8 @@ impl Future for JoinHandle { #[cfg(test)] mod test { - use crate::{generate_keypair, Config, DefaultParams, Ipfs}; + use crate::{Config, DefaultParams, Ipfs}; + use libp2p::identity::ed25519::Keypair; use tempdir::TempDir; #[test] @@ -129,7 +139,7 @@ mod test { let tmp = TempDir::new("ipfs-embed").unwrap(); block_on(Ipfs::::new(Config::new( tmp.path(), - generate_keypair(), + Keypair::generate(), ))) .unwrap(); } @@ -137,13 +147,13 @@ mod test { #[cfg(feature = "tokio")] #[test] #[should_panic( - expected = "here is no reactor running, must be called from the context of a Tokio 1.x runtime" + expected = "no reactor running, must be called from the context of a Tokio 1.x runtime" )] fn should_panic_without_a_tokio_runtime() { use futures::executor::block_on; let tmp = TempDir::new("ipfs-embed").unwrap(); let _ = block_on(Ipfs::::new0( - Config::new(tmp.path(), generate_keypair()), + Config::new(tmp.path(), Keypair::generate()), crate::Executor::Tokio, )); } @@ -156,7 +166,7 @@ mod test { .build() .unwrap(); rt.block_on(Ipfs::::new0( - Config::new(tmp.path(), generate_keypair()), + Config::new(tmp.path(), Keypair::generate()), crate::Executor::Tokio, )) .unwrap(); diff --git a/src/lib.rs b/src/lib.rs index dfef114..d7d0700 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,42 +1,14 @@ -//! Ipfs embed is a small, fast and reliable ipfs implementation designed for embedding in to -//! complex p2p applications. +//! Ipfs embed is a small, fast and reliable ipfs implementation designed for +//! embedding in to complex p2p applications. //! //! ```no_run //! # #[async_std::main] //! # async fn main() -> Result<(), Box> { //! # use ipfs_embed::{Config, DefaultParams, Ipfs}; -//! let ipfs = Ipfs::::new(Config::default()).await?; -//! ipfs.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?; +//! let mut ipfs = Ipfs::::new(Config::default()).await?; +//! ipfs.listen_on("/ip4/0.0.0.0/tcp/0".parse()?); //! # Ok(()) } //! ``` -use crate::db::StorageService; -pub use crate::db::{StorageConfig, TempPin}; -pub use crate::net::{ - generate_keypair, AddressRecord, AddressSource, BitswapConfig, BroadcastConfig, DnsConfig, - DocId, Event, GossipEvent, GossipsubConfig, Head, IdentifyConfig, KadConfig, Key, Keypair, - ListenerEvent, ListenerId, LocalStreamWriter, MdnsConfig, Multiaddr, NetworkConfig, PeerId, - PeerInfo, PeerRecord, PingConfig, PublicKey, Quorum, Record, SecretKey, SignedHead, StreamId, - StreamReader, SwarmEvents, SyncEvent, SyncQuery, ToLibp2p, TransportConfig, -}; -use crate::net::{BitswapStore, NetworkService}; -#[cfg(feature = "telemetry")] -pub use crate::telemetry::telemetry; -use async_trait::async_trait; -pub use db::Batch; -use executor::Executor; -use futures::stream::Stream; -use libipld::codec::References; -use libipld::error::BlockNotFound; -pub use libipld::store::DefaultParams; -use libipld::store::{Store, StoreParams}; -pub use libipld::{Block, Cid}; -use libipld::{Ipld, Result}; -use libp2p::kad::kbucket::Key as BucketKey; -pub use libp2p::multiaddr; -use prometheus::Registry; -use std::collections::HashSet; -use std::path::Path; -use std::sync::Arc; mod db; mod executor; @@ -45,6 +17,57 @@ mod net; mod telemetry; #[cfg(test)] mod test_util; +mod variable; + +/// convenience re-export of configuration types from libp2p +pub mod config { + pub use libp2p::{ + dns::{ResolverConfig, ResolverOpts}, + gossipsub::GossipsubConfig, + identify::Config as IdentifyConfig, + kad::record::store::MemoryStoreConfig as KadConfig, + mdns::Config as MdnsConfig, + ping::Config as PingConfig, + }; + pub use libp2p_bitswap::BitswapConfig; + pub use libp2p_broadcast::BroadcastConfig; +} + +#[cfg(feature = "telemetry")] +pub use crate::telemetry::telemetry; +pub use crate::{ + db::{Batch, StorageConfig, StorageService, TempPin}, + executor::Executor, + net::{ + AddressSource, ConnectionFailure, Direction, DnsConfig, Event, GossipEvent, ListenerEvent, + NetworkConfig, PeerInfo, Rtt, SwarmEvents, SyncEvent, SyncQuery, + }, +}; + +pub use libipld::{store::DefaultParams, Block, Cid}; +pub use libp2p::{ + core::{transport::ListenerId, ConnectedPoint, Multiaddr, PeerId}, + identity, + kad::{kbucket::Key as BucketKey, record::Key, PeerRecord, Quorum, Record}, + multiaddr, + swarm::{AddressRecord, AddressScore}, +}; + +use crate::net::NetworkService; +use async_trait::async_trait; +use chrono::{DateTime, Utc}; +use futures::{stream::Stream, Future}; +use libipld::{ + codec::References, + error::BlockNotFound, + store::{Store, StoreParams}, + Ipld, Result, +}; +use libp2p::identity::ed25519::{Keypair, PublicKey}; +use libp2p_bitswap::BitswapStore; +use parking_lot::Mutex; +use prometheus::Registry; +use std::{collections::HashSet, path::Path, sync::Arc, time::Duration}; /// Ipfs configuration. #[derive(Debug)] @@ -56,19 +79,19 @@ pub struct Config { } impl Config { - /// Creates a default configuration from a `path` and a `cache_size`. If the `path` is `None`, - /// ipfs will use an in-memory block store. + /// Creates a default configuration from a `path` and a `cache_size`. If the + /// `path` is `None`, ipfs will use an in-memory block store. pub fn new(path: &Path, keypair: Keypair) -> Self { let sweep_interval = std::time::Duration::from_millis(10000); let storage = StorageConfig::new(Some(path.join("blocks")), None, 0, sweep_interval); - let network = NetworkConfig::new(path.join("streams"), keypair); + let network = NetworkConfig::new(keypair); Self { storage, network } } } impl Default for Config { fn default() -> Self { - Self::new(Path::new("."), generate_keypair()) + Self::new(Path::new("."), Keypair::generate()) } } @@ -76,7 +99,7 @@ impl Default for Config { #[derive(Clone)] pub struct Ipfs { storage: StorageService

, - network: NetworkService

, + network: NetworkService, } impl std::fmt::Debug for Ipfs

{ @@ -102,7 +125,7 @@ where } fn insert(&mut self, block: &Block

) -> Result<()> { - self.0.insert(block) + self.0.insert(block.clone()) } fn missing_blocks(&mut self, cid: &Cid) -> Result> { @@ -116,8 +139,8 @@ where { /// Creates a new `Ipfs` from a `Config`. /// - /// This starts three background tasks. The swarm, garbage collector and the dht cleanup - /// tasks run in the background. + /// This starts three background tasks. The swarm, garbage collector and the + /// dht cleanup tasks run in the background. pub async fn new(config: Config) -> Result { let executor = Executor::new(); Self::new0(config, executor).await @@ -145,7 +168,7 @@ where } /// Listens on a new `Multiaddr`. - pub fn listen_on(&self, addr: Multiaddr) -> Result> { + pub fn listen_on(&mut self, addr: Multiaddr) -> impl Stream { self.network.listen_on(addr) } @@ -155,7 +178,7 @@ where } /// Adds an external address. - pub fn add_external_address(&self, addr: Multiaddr) { + pub fn add_external_address(&mut self, addr: Multiaddr) { self.network.add_external_address(addr) } @@ -165,34 +188,44 @@ where } /// Adds a known `Multiaddr` for a `PeerId`. - pub fn add_address(&self, peer: &PeerId, addr: Multiaddr) { + pub fn add_address(&mut self, peer: PeerId, addr: Multiaddr) { self.network.add_address(peer, addr) } + /// Adds a batch of known `Multiaddr` & `PeerId` pairs. + pub fn add_addresses(&mut self, addresses: Vec<(PeerId, Multiaddr)>) { + self.network.add_addresses(addresses) + } + /// Removes a `Multiaddr` for a `PeerId`. - pub fn remove_address(&self, peer: &PeerId, addr: &Multiaddr) { + pub fn remove_address(&mut self, peer: PeerId, addr: Multiaddr) { self.network.remove_address(peer, addr) } + /// Removes all unconnected peers without addresses which have been + /// in this state for at least the given duration + pub fn prune_peers(&mut self, min_age: Duration) { + self.network.prune_peers(min_age); + } + /// Dials a `PeerId` using a known address. - pub fn dial(&self, peer: &PeerId) { + pub fn dial(&mut self, peer: PeerId) { self.network.dial(peer); } /// Dials a `PeerId` using `Multiaddr`. - pub fn dial_address(&self, peer: &PeerId, addr: Multiaddr) { - self.network.add_address(peer, addr); - self.network.dial(peer); + pub fn dial_address(&mut self, peer: PeerId, addr: Multiaddr) { + self.network.dial_address(peer, addr); } /// Bans a `PeerId` from the swarm, dropping all existing connections and /// preventing new connections from the peer. - pub fn ban(&self, peer: PeerId) { + pub fn ban(&mut self, peer: PeerId) { self.network.ban(peer) } /// Unbans a previously banned `PeerId`. - pub fn unban(&self, peer: PeerId) { + pub fn unban(&mut self, peer: PeerId) { self.network.unban(peer) } @@ -202,7 +235,7 @@ where } /// Returns a list of connected peers. - pub fn connections(&self) -> Vec<(PeerId, Multiaddr)> { + pub fn connections(&self) -> Vec<(PeerId, Multiaddr, DateTime, Direction)> { self.network.connections() } @@ -216,11 +249,13 @@ where self.network.peer_info(peer) } - /// Bootstraps the dht using a set of bootstrap nodes. After bootstrap completes it - /// provides all blocks in the block store. - pub async fn bootstrap(&self, nodes: &[(PeerId, Multiaddr)]) -> Result<()> { - self.network.bootstrap(nodes).await?; - Ok(()) + /// Bootstraps the dht using a set of bootstrap nodes. After bootstrap + /// completes it provides all blocks in the block store. + pub fn bootstrap( + &mut self, + nodes: Vec<(PeerId, Multiaddr)>, + ) -> impl Future> { + self.network.bootstrap(nodes) } /// Returns true if the dht was bootstrapped. @@ -228,69 +263,83 @@ where self.network.is_bootstrapped() } - /// Gets the closest peer to a key. Useful for finding the `Multiaddr` of a `PeerId`. - pub async fn get_closest_peers(&self, key: K) -> Result<()> - where - K: Into> + Into> + Clone, - { - self.network.get_closest_peers(key).await?; - Ok(()) - } + /// Gets the closest peer to a key. Useful for finding the `Multiaddr` of a + /// `PeerId`. + // pub async fn get_closest_peers(&self, key: K) -> Result<()> + // where + // K: Into> + Into> + Clone, + // { + // self.network.get_closest_peers(key).await?; + // Ok(()) + // } /// Gets providers of a key from the dht. - pub async fn providers(&self, key: Key) -> Result> { - self.network.providers(key).await + pub fn providers(&mut self, key: Key) -> impl Future>> { + self.network.providers(key) } /// Provides a key in the dht. - pub async fn provide(&self, key: Key) -> Result<()> { - self.network.provide(key).await + pub fn provide(&mut self, key: Key) -> impl Future> { + self.network.provide(key) } /// Stops providing a key in the dht. - pub fn unprovide(&self, key: &Key) { + pub fn unprovide(&mut self, key: Key) -> Result<()> { self.network.unprovide(key) } /// Gets a record from the dht. - pub async fn get_record(&self, key: &Key, quorum: Quorum) -> Result> { - self.network.get_record(key, quorum).await + pub fn get_record( + &mut self, + key: Key, + quorum: Quorum, + ) -> impl Future>> { + self.network.get_record(key, quorum) } /// Puts a new record in the dht. - pub async fn put_record(&self, record: Record, quorum: Quorum) -> Result<()> { - self.network.put_record(record, quorum).await + pub fn put_record( + &mut self, + record: Record, + quorum: Quorum, + ) -> impl Future> { + self.network.put_record(record, quorum) } /// Removes a record from the dht. - pub fn remove_record(&self, key: &Key) { + pub fn remove_record(&mut self, key: Key) -> Result<()> { self.network.remove_record(key) } - /// Subscribes to a `topic` returning a `Stream` of messages. If all `Stream`s for - /// a topic are dropped it unsubscribes from the `topic`. - pub fn subscribe(&self, topic: &str) -> Result> { + /// Subscribes to a `topic` returning a `Stream` of messages. If all + /// `Stream`s for a topic are dropped it unsubscribes from the `topic`. + pub fn subscribe( + &mut self, + topic: String, + ) -> impl Future>> { self.network.subscribe(topic) } - /// Publishes a new message in a `topic`, sending the message to all subscribed peers. - pub fn publish(&self, topic: &str, msg: Vec) -> Result<()> { + /// Publishes a new message in a `topic`, sending the message to all + /// subscribed peers. + pub fn publish(&mut self, topic: String, msg: Vec) -> impl Future> { self.network.publish(topic, msg) } - /// Publishes a new message in a `topic`, sending the message to all subscribed connected peers. - pub fn broadcast(&self, topic: &str, msg: Vec) -> Result<()> { + /// Publishes a new message in a `topic`, sending the message to all + /// subscribed connected peers. + pub fn broadcast(&mut self, topic: String, msg: Vec) -> impl Future> { self.network.broadcast(topic, msg) } - /// Creates a temporary pin in the block store. A temporary pin is not persisted to disk - /// and is released once it is dropped. + /// Creates a temporary pin in the block store. A temporary pin is not + /// persisted to disk and is released once it is dropped. pub fn create_temp_pin(&self) -> Result { self.storage.create_temp_pin() } /// Adds a new root to a temporary pin. - pub fn temp_pin(&self, tmp: &TempPin, cid: &Cid) -> Result<()> { + pub fn temp_pin(&self, tmp: &mut TempPin, cid: &Cid) -> Result<()> { self.storage.temp_pin(tmp, std::iter::once(*cid)) } @@ -314,15 +363,15 @@ where } } - /// Either returns a block if it's in the block store or tries to retrieve it from - /// a peer. + /// Either returns a block if it's in the block store or tries to retrieve + /// it from a peer. pub async fn fetch(&self, cid: &Cid, providers: Vec) -> Result> { if let Some(data) = self.storage.get(cid)? { let block = Block::new_unchecked(*cid, data); return Ok(block); } if !providers.is_empty() { - self.network.get(*cid, providers.into_iter()).await?; + self.network.get(*cid, providers).await?.await?; if let Some(data) = self.storage.get(cid)? { let block = Block::new_unchecked(*cid, data); return Ok(block); @@ -333,20 +382,25 @@ where } /// Inserts a block in to the block store. - pub fn insert(&self, block: &Block

) -> Result<()> { + pub fn insert(&self, block: Block

) -> Result<()> { self.storage.insert(block)?; Ok(()) } - /// Manually runs garbage collection to completion. This is mainly useful for testing and - /// administrative interfaces. During normal operation, the garbage collector automatically - /// runs in the background. - pub async fn evict(&self) -> Result<()> { - self.storage.evict().await + /// Manually runs garbage collection to completion. This is mainly useful + /// for testing and administrative interfaces. During normal operation, + /// the garbage collector automatically runs in the background. + pub fn evict(&self) -> impl Future> { + self.storage.evict() } - pub fn sync(&self, cid: &Cid, providers: Vec) -> SyncQuery

{ + pub fn sync( + &self, + cid: &Cid, + providers: Vec, + ) -> impl Future> { let missing = self.storage.missing_blocks(cid).ok().unwrap_or_default(); + tracing::trace!(cid = %cid, missing = %missing.len(), "sync"); self.network.sync(*cid, providers, missing) } @@ -355,99 +409,47 @@ where self.storage.alias(alias.as_ref(), cid) } + /// List all known aliases. + pub fn aliases(&self) -> Result, Cid)>> { + self.storage.aliases() + } + /// Returns the root of an alias. pub fn resolve + Send + Sync>(&self, alias: T) -> Result> { self.storage.resolve(alias.as_ref()) } - /// Returns a list of aliases preventing a `Cid` from being garbage collected. - pub fn reverse_alias(&self, cid: &Cid) -> Result>>> { + /// Returns a list of aliases preventing a `Cid` from being garbage + /// collected. + pub fn reverse_alias(&self, cid: &Cid) -> Result>>> { self.storage.reverse_alias(cid) } - /// Flushes the block store. After `flush` completes successfully it is guaranteed that - /// all writes have been persisted to disk. - pub async fn flush(&self) -> Result<()> { - self.storage.flush().await - } - - /// Perform a set of storage operations in a batch, and discards the result. - /// - /// You should keep a batch open for as short as possible. - /// You *must not* create a batch inside a batch, or use the outer ipfs. - pub fn read_batch) -> Result, R>(&self, f: F) -> Result { - self.storage.ro("read_batch", f) + /// Flushes the block store. After `flush` completes successfully it is + /// guaranteed that all writes have been persisted to disk. + pub fn flush(&self) -> impl Future> { + self.storage.flush() } - /// Perform a set of storage operations in a batch, and stores the result. + /// Perform a set of storage operations in a batch /// - /// You should keep a batch open for as short as possible. - /// You *must not* create a batch inside a batch, or use the outer ipfs. - pub fn write_batch(&self, f: impl FnOnce(&mut Batch<'_, P>) -> Result) -> Result { - self.storage.rw("write_batch", f) + /// The batching concerns only the CacheTracker, it implies no atomicity + /// guarantees! + pub fn batch_ops(&self, f: impl FnOnce(&mut Batch<'_, P>) -> Result) -> Result { + self.storage.rw("batch_ops", f) } /// Registers prometheus metrics in a registry. pub fn register_metrics(&self, registry: &Registry) -> Result<()> { self.storage.register_metrics(registry)?; - self.network.register_metrics(registry)?; + net::register_metrics(registry)?; Ok(()) } /// Subscribes to the swarm event stream. - pub fn swarm_events(&self) -> SwarmEvents { + pub fn swarm_events(&mut self) -> impl Future> { self.network.swarm_events() } - - /// Returns the documents both local and replicated. - pub fn docs(&self) -> Result> { - self.network.docs() - } - - /// Returns the streams both local and replicated. - pub fn streams(&self) -> Result> { - self.network.streams() - } - - /// Returns the streams both local and replicated for the given document id. - pub fn substreams(&self, doc: DocId) -> Result> { - self.network.substreams(doc) - } - - /// Adds the peers to a replicated stream. - pub fn stream_add_peers(&self, doc: DocId, peers: impl Iterator) { - self.network.stream_add_peers(doc, peers) - } - - /// Returns the current head of a stream. - pub fn stream_head(&self, id: &StreamId) -> Result> { - self.network.stream_head(id) - } - - /// Returns a reader for the slice. - pub fn stream_slice(&self, id: &StreamId, start: u64, len: u64) -> Result { - self.network.stream_slice(id, start, len) - } - - /// Removes a local or replicated stream. - pub fn stream_remove(&self, id: &StreamId) -> Result<()> { - self.network.stream_remove(id) - } - - /// Returns a writter to append to a local stream. - pub fn stream_append(&self, id: DocId) -> Result { - self.network.stream_append(id) - } - - /// Subscribes to a replicated stream. - pub fn stream_subscribe(&self, id: &StreamId) -> Result<()> { - self.network.stream_subscribe(id) - } - - /// Updates the head of a replicated stream. - pub fn stream_update_head(&self, head: SignedHead) { - self.network.stream_update_head(head) - } } #[async_trait] @@ -456,14 +458,14 @@ where Ipld: References, { type Params = P; - type TempPin = Arc; + type TempPin = Arc>; fn create_temp_pin(&self) -> Result { - Ok(Arc::new(Ipfs::create_temp_pin(self)?)) + Ok(Arc::new(Mutex::new(Ipfs::create_temp_pin(self)?))) } fn temp_pin(&self, tmp: &Self::TempPin, cid: &Cid) -> Result<()> { - Ipfs::temp_pin(self, tmp, cid) + Ipfs::temp_pin(self, &mut tmp.lock(), cid) } fn contains(&self, cid: &Cid) -> Result { @@ -475,7 +477,7 @@ where } fn insert(&self, block: &Block

) -> Result<()> { - let _ = Ipfs::insert(self, block)?; + Ipfs::insert(self, block.clone())?; Ok(()) } @@ -488,7 +490,7 @@ where } fn reverse_alias(&self, cid: &Cid) -> Result>>> { - Ipfs::reverse_alias(self, cid) + Ipfs::reverse_alias(self, cid).map(|x| x.map(|x| x.into_iter().collect())) } async fn flush(&self) -> Result<()> { @@ -500,7 +502,7 @@ where } async fn sync(&self, cid: &Cid) -> Result<()> { - Ipfs::sync(self, cid, self.peers()).await + Ipfs::sync(self, cid, self.peers()).await?.await } } @@ -508,13 +510,10 @@ where mod tests { use super::*; use async_std::future::timeout; - use futures::join; - use futures::stream::StreamExt; - use libipld::cbor::DagCborCodec; - use libipld::multihash::Code; - use libipld::raw::RawCodec; - use libipld::store::DefaultParams; - use libipld::{alias, ipld}; + use futures::{join, stream::StreamExt}; + use libipld::{ + alias, cbor::DagCborCodec, ipld, multihash::Code, raw::RawCodec, store::DefaultParams, + }; use std::time::Duration; use tempdir::TempDir; @@ -530,13 +529,13 @@ mod tests { let sweep_interval = Duration::from_millis(10000); let storage = StorageConfig::new(None, None, 10, sweep_interval); - let mut network = NetworkConfig::new(tmp.path().into(), generate_keypair()); + let mut network = NetworkConfig::new(Keypair::generate()); if !enable_mdns { network.mdns = None; } - let ipfs = Ipfs::new(Config { storage, network }).await?; - ipfs.listen_on("/ip4/127.0.0.1/tcp/0".parse().unwrap())? + let mut ipfs = Ipfs::new(Config { storage, network }).await?; + ipfs.listen_on("/ip4/127.0.0.1/tcp/0".parse().unwrap()) .next() .await .unwrap(); @@ -552,9 +551,9 @@ mod tests { tracing_try_init(); let (store, _tmp) = create_store(false).await?; let block = create_block(b"test_local_store")?; - let tmp = store.create_temp_pin()?; - store.temp_pin(&tmp, block.cid())?; - let _ = store.insert(&block)?; + let mut tmp = store.create_temp_pin()?; + store.temp_pin(&mut tmp, block.cid())?; + store.insert(block.clone())?; let block2 = store.get(block.cid())?; assert_eq!(block.data(), block2.data()); Ok(()) @@ -567,12 +566,12 @@ mod tests { let (store1, _tmp) = create_store(true).await?; let (store2, _tmp) = create_store(true).await?; let block = create_block(b"test_exchange_mdns")?; - let tmp1 = store1.create_temp_pin()?; - store1.temp_pin(&tmp1, block.cid())?; - let _ = store1.insert(&block)?; + let mut tmp1 = store1.create_temp_pin()?; + store1.temp_pin(&mut tmp1, block.cid())?; + store1.insert(block.clone())?; store1.flush().await?; - let tmp2 = store2.create_temp_pin()?; - store2.temp_pin(&tmp2, block.cid())?; + let mut tmp2 = store2.create_temp_pin()?; + store2.temp_pin(&mut tmp2, block.cid())?; let block2 = store2 .fetch(block.cid(), vec![store1.local_peer_id()]) .await?; @@ -585,29 +584,29 @@ mod tests { async fn test_exchange_kad() -> Result<()> { tracing_try_init(); let (store, _tmp) = create_store(false).await?; - let (store1, _tmp) = create_store(false).await?; - let (store2, _tmp) = create_store(false).await?; + let (mut store1, _tmp) = create_store(false).await?; + let (mut store2, _tmp) = create_store(false).await?; let addr = store.listeners()[0].clone(); let peer_id = store.local_peer_id(); let nodes = [(peer_id, addr)]; - let b1 = store1.bootstrap(&nodes); - let b2 = store2.bootstrap(&nodes); + let b1 = store1.bootstrap(nodes[..].into()); + let b2 = store2.bootstrap(nodes[..].into()); let (r1, r2) = join!(b1, b2); r1.unwrap(); r2.unwrap(); let block = create_block(b"test_exchange_kad")?; let key = Key::new(&block.cid().to_bytes()); - let tmp1 = store1.create_temp_pin()?; - store1.temp_pin(&tmp1, block.cid())?; - store1.insert(&block)?; + let mut tmp1 = store1.create_temp_pin()?; + store1.temp_pin(&mut tmp1, block.cid())?; + store1.insert(block.clone())?; store1.provide(key.clone()).await?; store1.flush().await?; - let tmp2 = store2.create_temp_pin()?; - store2.temp_pin(&tmp2, block.cid())?; + let mut tmp2 = store2.create_temp_pin()?; + store2.temp_pin(&mut tmp2, block.cid())?; let providers = store2.providers(key).await?; let block2 = store2 .fetch(block.cid(), providers.into_iter().collect()) @@ -664,10 +663,10 @@ mod tests { #[async_std::test] async fn test_sync() -> Result<()> { tracing_try_init(); - let (local1, _tmp) = create_store(false).await?; - let (local2, _tmp) = create_store(false).await?; - local1.add_address(&local2.local_peer_id(), local2.listeners()[0].clone()); - local2.add_address(&local1.local_peer_id(), local1.listeners()[0].clone()); + let (mut local1, _tmp) = create_store(false).await?; + let (mut local2, _tmp) = create_store(false).await?; + local1.add_address(local2.local_peer_id(), local2.listeners()[0].clone()); + local2.add_address(local1.local_peer_id(), local1.listeners()[0].clone()); let a1 = create_ipld_block(&ipld!({ "a": 0 }))?; let b1 = create_ipld_block(&ipld!({ "b": 0 }))?; @@ -676,9 +675,9 @@ mod tests { let c2 = create_ipld_block(&ipld!({ "c": [a1.cid(), b2.cid()] }))?; let x = alias!(x); - let _ = local1.insert(&a1)?; - let _ = local1.insert(&b1)?; - let _ = local1.insert(&c1)?; + local1.insert(a1.clone())?; + local1.insert(b1.clone())?; + local1.insert(c1.clone())?; local1.alias(x, Some(c1.cid()))?; local1.flush().await?; assert_pinned!(&local1, &a1); @@ -686,14 +685,17 @@ mod tests { assert_pinned!(&local1, &c1); local2.alias(&x, Some(c1.cid()))?; - local2.sync(c1.cid(), vec![local1.local_peer_id()]).await?; + local2 + .sync(c1.cid(), vec![local1.local_peer_id()]) + .await? + .await?; local2.flush().await?; assert_pinned!(&local2, &a1); assert_pinned!(&local2, &b1); assert_pinned!(&local2, &c1); - let _ = local2.insert(&b2)?; - let _ = local2.insert(&c2)?; + local2.insert(b2.clone())?; + local2.insert(c2.clone())?; local2.alias(x, Some(c2.cid()))?; local2.flush().await?; assert_pinned!(&local2, &a1); @@ -703,7 +705,10 @@ mod tests { assert_pinned!(&local2, &c2); local1.alias(x, Some(c2.cid()))?; - local1.sync(c2.cid(), vec![local2.local_peer_id()]).await?; + local1 + .sync(c2.cid(), vec![local2.local_peer_id()]) + .await? + .await?; local1.flush().await?; assert_pinned!(&local1, &a1); assert_unpinned!(&local1, &b1); @@ -730,25 +735,26 @@ mod tests { } #[async_std::test] - #[allow(clippy::eval_order_dependence)] async fn test_dht_record() -> Result<()> { tracing_try_init(); - let stores = [create_store(false).await?, create_store(false).await?]; + let mut stores = [create_store(false).await?, create_store(false).await?]; async_std::task::sleep(Duration::from_millis(100)).await; stores[0] .0 - .bootstrap(&[( + .bootstrap(vec![( stores[1].0.local_peer_id(), stores[1].0.listeners()[0].clone(), )]) .await?; stores[1] .0 - .bootstrap(&[( + .bootstrap(vec![( stores[0].0.local_peer_id(), stores[0].0.listeners()[0].clone(), )]) .await?; + + async_std::task::sleep(Duration::from_millis(500)).await; let key: Key = b"key".to_vec().into(); stores[0] @@ -758,16 +764,15 @@ mod tests { Quorum::One, ) .await?; - let records = stores[1].0.get_record(&key, Quorum::One).await?; + let records = stores[1].0.get_record(key, Quorum::One).await?; assert_eq!(records.len(), 1); Ok(()) } #[async_std::test] - #[allow(clippy::eval_order_dependence)] async fn test_gossip_and_broadcast() -> Result<()> { tracing_try_init(); - let stores = [ + let mut stores = [ create_store(false).await?, create_store(false).await?, create_store(false).await?, @@ -776,27 +781,48 @@ mod tests { create_store(false).await?, ]; let mut subscriptions = vec![]; - let topic = "topic"; - for (store, _) in &stores { - for (other, _) in &stores { - if store.local_peer_id() != other.local_peer_id() { - store.dial_address(&other.local_peer_id(), other.listeners()[0].clone()); + let topic = "topic".to_owned(); + let others = stores + .iter() + .map(|store| { + ( + store.0.local_peer_id(), + store.0.listeners().into_iter().next().unwrap(), + ) + }) + .collect::>(); + for (store, _) in &mut stores { + for (peer, addr) in &others { + if store.local_peer_id() != *peer { + store.dial_address(*peer, addr.clone()); } } } - async_std::task::sleep(Duration::from_millis(500)).await; - // Make sure everyone is peered before subscribing + // TCP sim open redials may take a second + async_std::task::sleep(Duration::from_millis(2500)).await; for (store, _) in &stores { - subscriptions.push(store.subscribe(topic)?); + for (peer, _) in &others { + assert!(store.is_connected(peer)); + } + } + for (store, _) in &mut stores { + subscriptions.push(store.subscribe(topic.clone()).await?); } async_std::task::sleep(Duration::from_millis(500)).await; stores[0] .0 - .publish(topic, b"hello gossip".to_vec()) + .publish(topic.clone(), b"hello gossip".to_vec()) + .await .unwrap(); + /* + * This test used to assume that calling subscribe immediately updates the local subscription + * while sending that subscription over the network takes some time, meaning that all participants + * received all Subscribed messages. With the new asynchronous NetworkCommand this is no longer + * true, so Subscribed messages are sometimes missed. + */ for (idx, subscription) in subscriptions.iter_mut().enumerate() { let mut expected = stores .iter() @@ -822,42 +848,58 @@ mod tests { Box::new(std::iter::empty()) }) .collect::>(); - while !expected.is_empty() { + while expected + .iter() + .any(|msg| matches!(msg, GossipEvent::Message(..))) + { let ev = timeout(Duration::from_millis(100), subscription.next()) .await - .unwrap() + .unwrap_or_else(|_| panic!("idx {} timeout waiting for {:?}", idx, expected)) .unwrap(); - assert!(expected.contains(&ev)); + assert!(expected.contains(&ev), ", received {:?}", ev); if let Some(idx) = expected.iter().position(|e| e == &ev) { // Can't retain, as there might be multiple messages expected.remove(idx); } } + if idx != 0 { + assert!( + expected.len() < (stores.len() - 1) * 2, + ", idx {} did not receive any Subscribed message", + idx + ); + } } // Check broadcast subscription stores[0] .0 - .broadcast(topic, b"hello broadcast".to_vec()) + .broadcast(topic.clone(), b"hello broadcast".to_vec()) + .await .unwrap(); for subscription in &mut subscriptions[1..] { - if let GossipEvent::Message(p, data) = subscription.next().await.unwrap() { - assert_eq!(p, stores[0].0.local_peer_id()); - assert_eq!(data[..], b"hello broadcast"[..]); - } else { - panic!() + match subscription.next().await.unwrap() { + GossipEvent::Message(p, data) => { + assert_eq!(p, stores[0].0.local_peer_id()); + assert_eq!(data[..], b"hello broadcast"[..]); + } + x => { + panic!("received unexpected message: {:?}", x); + } } } // trigger cleanup + let mut last_sub = subscriptions.drain(..1).next().unwrap(); + drop(subscriptions); + stores[0] .0 .broadcast(topic, b"r u still listening?".to_vec()) + .await .unwrap(); - let mut last_sub = subscriptions.drain(..1).next().unwrap(); - drop(subscriptions); let mut expected = stores[1..] .iter() .map(|s| s.0.local_peer_id()) @@ -871,7 +913,12 @@ mod tests { .await .unwrap() .unwrap(); - assert!(expected.contains(&ev)); + // this is idx==0 which didn’t have a message to receive in the gossipsub round, + // so the Subscribed messages are still lingering in this channel + if let GossipEvent::Subscribed(..) = ev { + continue; + } + assert!(expected.contains(&ev), ", received {:?}", ev); if let Some(idx) = expected.iter().position(|e| e == &ev) { // Can't retain, as there might be multiple messages expected.remove(idx); @@ -883,16 +930,14 @@ mod tests { #[async_std::test] async fn test_batch_read() -> Result<()> { tracing_try_init(); - let tmp = TempDir::new("ipfs-embed")?; - let network = NetworkConfig::new(tmp.path().into(), generate_keypair()); + let network = NetworkConfig::new(Keypair::generate()); let storage = StorageConfig::new(None, None, 1000000, Duration::from_secs(3600)); let ipfs = Ipfs::::new(Config { storage, network }).await?; let a = create_block(b"a")?; let b = create_block(b"b")?; - ipfs.insert(&a)?; - ipfs.insert(&b)?; - let has_blocks = - ipfs.read_batch(|db| Ok(db.contains(a.cid())? && db.contains(b.cid())?))?; + ipfs.insert(a.clone())?; + ipfs.insert(b.clone())?; + let has_blocks = ipfs.batch_ops(|db| Ok(db.contains(a.cid())? && db.contains(b.cid())?))?; assert!(has_blocks); Ok(()) } @@ -900,26 +945,26 @@ mod tests { #[async_std::test] async fn test_batch_write() -> Result<()> { tracing_try_init(); - let tmp = TempDir::new("ipfs-embed")?; - let network = NetworkConfig::new(tmp.path().into(), generate_keypair()); + let network = NetworkConfig::new(Keypair::generate()); let storage = StorageConfig::new(None, None, 1000000, Duration::from_secs(3600)); let ipfs = Ipfs::::new(Config { storage, network }).await?; let a = create_block(b"a")?; let b = create_block(b"b")?; let c = create_block(b"c")?; let d = create_block(b"d")?; - ipfs.write_batch(|db| { - db.insert(&a)?; - db.insert(&b)?; + ipfs.batch_ops(|db| { + db.insert(a.clone())?; + db.insert(b.clone())?; Ok(()) })?; assert!(ipfs.contains(a.cid())? && ipfs.contains(b.cid())?); - let _: anyhow::Result<()> = ipfs.write_batch(|db| { - db.insert(&c)?; - db.insert(&d)?; + #[allow(unreachable_code)] + let _: anyhow::Result<()> = ipfs.batch_ops(|db| { + db.insert(c.clone())?; anyhow::bail!("nope!"); + db.insert(d.clone())?; }); - assert!(!ipfs.contains(c.cid())? && ipfs.contains(b.cid())?); + assert!(!ipfs.contains(d.cid())? && ipfs.contains(c.cid())? && ipfs.contains(b.cid())?); Ok(()) } @@ -939,13 +984,13 @@ mod tests { let size: usize = blocks.iter().map(|block| block.data().len()).sum(); tracing::info!("chain built {} blocks, {} bytes", blocks.len(), size); for block in blocks.iter() { - let _ = a.insert(block)?; + a.insert(block.clone())?; } a.flush().await?; let t0 = Instant::now(); - let _ = b - .sync(&cid, vec![a.local_peer_id()]) + b.sync(&cid, vec![a.local_peer_id()]) + .await? .for_each(|x| async move { tracing::debug!("sync progress {:?}", x) }) .await; b.flush().await?; @@ -979,13 +1024,13 @@ mod tests { let size: usize = blocks.iter().map(|block| block.data().len()).sum(); tracing::info!("chain built {} blocks, {} bytes", blocks.len(), size); for block in blocks.iter() { - let _ = a.insert(block)?; + a.insert(block.clone())?; } a.flush().await?; let t0 = Instant::now(); - let _ = b - .sync(&cid, vec![a.local_peer_id()]) + b.sync(&cid, vec![a.local_peer_id()]) + .await? .for_each(|x| async move { tracing::debug!("sync progress {:?}", x) }) .await; b.flush().await?; diff --git a/src/net/address_handler.rs b/src/net/address_handler.rs new file mode 100644 index 0000000..fd05434 --- /dev/null +++ b/src/net/address_handler.rs @@ -0,0 +1,120 @@ +use libp2p::{ + core::{upgrade::DeniedUpgrade, ConnectedPoint}, + multiaddr::Protocol, + swarm::{ + handler::{InboundUpgradeSend, OutboundUpgradeSend}, + ConnectionHandler, IntoConnectionHandler, KeepAlive, SubstreamProtocol, + }, + Multiaddr, PeerId, +}; +use std::{ + convert::TryInto, + task::{Context, Poll}, +}; +use void::Void; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct IntoAddressHandler(pub Option<(Multiaddr, u8)>, pub bool); + +impl IntoAddressHandler { + pub fn peer_id(&self) -> Option { + let (addr, _retries) = self.0.as_ref()?; + match addr.iter().last() { + Some(Protocol::P2p(p)) => p.try_into().ok(), + _ => None, + } + } +} + +impl IntoConnectionHandler for IntoAddressHandler { + type Handler = AddressHandler; + + fn into_handler( + self, + remote_peer_id: &libp2p::PeerId, + connected_point: &libp2p::core::ConnectedPoint, + ) -> Self::Handler { + AddressHandler { + own_dial: self.0, + remote_peer_id: *remote_peer_id, + connected_point: connected_point.clone(), + keep_alive: self.1, + } + } + + fn inbound_protocol(&self) -> DeniedUpgrade { + DeniedUpgrade + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AddressHandler { + pub own_dial: Option<(Multiaddr, u8)>, + pub remote_peer_id: PeerId, + pub connected_point: ConnectedPoint, + pub keep_alive: bool, +} + +impl ConnectionHandler for AddressHandler { + type InEvent = Void; + type OutEvent = Void; + type Error = Void; + type InboundProtocol = DeniedUpgrade; + type OutboundProtocol = DeniedUpgrade; + type InboundOpenInfo = (); + type OutboundOpenInfo = Void; + + fn listen_protocol( + &self, + ) -> libp2p::swarm::SubstreamProtocol { + SubstreamProtocol::new(DeniedUpgrade, ()) + } + + fn inject_fully_negotiated_inbound( + &mut self, + _protocol: ::Output, + _info: Self::InboundOpenInfo, + ) { + } + + fn inject_fully_negotiated_outbound( + &mut self, + _protocol: ::Output, + _info: Self::OutboundOpenInfo, + ) { + } + + fn inject_event(&mut self, _event: Self::InEvent) {} + + fn inject_dial_upgrade_error( + &mut self, + _info: Self::OutboundOpenInfo, + _error: libp2p::swarm::ConnectionHandlerUpgrErr< + ::Error, + >, + ) { + } + + fn connection_keep_alive(&self) -> KeepAlive { + if self.keep_alive { + KeepAlive::Yes + } else { + KeepAlive::No + } + } + + #[allow(clippy::type_complexity)] + fn poll( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll< + libp2p::swarm::ConnectionHandlerEvent< + Self::OutboundProtocol, + Self::OutboundOpenInfo, + Self::OutEvent, + Self::Error, + >, + > { + Poll::Pending + } +} diff --git a/src/net/behaviour.rs b/src/net/behaviour.rs index c11a8e0..380c23c 100644 --- a/src/net/behaviour.rs +++ b/src/net/behaviour.rs @@ -1,34 +1,40 @@ -use crate::net::config::NetworkConfig; -use crate::net::peers::{AddressBook, AddressSource, Event, PeerInfo, SwarmEvents}; -use fnv::FnvHashMap; -use futures::channel::{mpsc, oneshot}; -use futures::stream::Stream; -use libipld::store::StoreParams; -use libipld::{Cid, Result}; -use libp2p::gossipsub::{ - Gossipsub, GossipsubEvent, GossipsubMessage, IdentTopic, MessageAuthenticity, +use crate::{ + net::{ + config::NetworkConfig, + peers::{AddressBook, Event}, + }, + variable::Writer, + AddressSource, PeerInfo, }; -use libp2p::identify::{Identify, IdentifyEvent}; -use libp2p::kad::kbucket::Key as BucketKey; -use libp2p::kad::record::store::MemoryStore; -use libp2p::kad::record::{Key, Record}; -use libp2p::kad::{ - AddProviderOk, BootstrapOk, GetClosestPeersOk, GetProvidersOk, GetRecordOk, Kademlia, - KademliaEvent, PeerRecord, PutRecordOk, QueryResult, Quorum, +use fnv::{FnvHashMap, FnvHashSet}; +use futures::channel::{ + mpsc::{self, UnboundedSender}, + oneshot, +}; +use libipld::{store::StoreParams, Cid, DefaultParams, Result}; +#[cfg(feature = "async_global")] +use libp2p::mdns::async_io::Behaviour as Mdns; +#[cfg(all(feature = "tokio", not(feature = "async_global")))] +use libp2p::mdns::tokio::Behaviour as Mdns; +use libp2p::{ + core::ConnectedPoint, + gossipsub::{Gossipsub, GossipsubEvent, GossipsubMessage, IdentTopic, MessageAuthenticity}, + identify, + kad::{ + record::{store::MemoryStore, Key, Record}, + AddProviderOk, BootstrapOk, GetClosestPeersOk, GetProvidersOk, GetRecordOk, Kademlia, + KademliaEvent, PeerRecord, ProgressStep, PutRecordOk, QueryResult, Quorum, K_VALUE, + }, + mdns, ping, + swarm::{ + behaviour::toggle::Toggle, AddressRecord, ConnectionError, ConnectionHandler, + IntoConnectionHandler, NetworkBehaviour, + }, + Multiaddr, PeerId, }; -use libp2p::mdns::{Mdns, MdnsEvent}; -use libp2p::ping::{Ping, PingEvent, PingFailure, PingSuccess}; -use libp2p::swarm::toggle::Toggle; -use libp2p::swarm::NetworkBehaviourEventProcess; -use libp2p::NetworkBehaviour; -use libp2p::{Multiaddr, PeerId}; use libp2p_bitswap::{Bitswap, BitswapEvent, BitswapStore}; -use libp2p_blake_streams::{StreamSync, StreamSyncEvent}; use libp2p_broadcast::{Broadcast, BroadcastEvent, Topic}; -use libp2p_quic::{PublicKey, ToLibp2p}; -use prometheus::Registry; -use std::collections::HashSet; -use std::sync::Arc; +use std::{collections::HashSet, sync::Arc, time::Duration}; use thiserror::Error; #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -55,68 +61,64 @@ impl From for QueryId { /// An event of a sync query. #[derive(Debug)] pub enum SyncEvent { - /// Signals that the sync query made progress and counts the amount of subtrees to - /// sync. If it is syncing a linked list, it will always be 1. + /// Signals that the sync query made progress and counts the amount of + /// subtrees to sync. If it is syncing a linked list, it will always be + /// 1. Progress { missing: usize }, - /// Signals completion of the sync query and if it was completed successfully. + /// Signals completion of the sync query and if it was completed + /// successfully. Complete(Result<()>), } pub type GetChannel = oneshot::Receiver>; pub type SyncChannel = mpsc::UnboundedReceiver; -pub type BootstrapChannel = oneshot::Receiver>; -pub type GetClosestPeersChannel = oneshot::Receiver>>; -pub type GetProvidersChannel = oneshot::Receiver>>; -pub type StartProvidingChannel = oneshot::Receiver>; -pub type GetRecordChannel = oneshot::Receiver>>; -pub type PutRecordChannel = oneshot::Receiver>; - -enum QueryChannel { + +pub enum QueryChannel { Get(oneshot::Sender>), Sync(mpsc::UnboundedSender), Bootstrap(oneshot::Sender>), + #[allow(dead_code)] GetClosestPeers(oneshot::Sender>>), - GetProviders(oneshot::Sender>>), + GetProviders(HashSet, oneshot::Sender>>), StartProviding(oneshot::Sender>), - GetRecord(oneshot::Sender>>), + GetRecord( + usize, + Vec, + oneshot::Sender>>, + ), PutRecord(oneshot::Sender>), } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum GossipEvent { Subscribed(PeerId), Message(PeerId, Arc<[u8]>), Unsubscribed(PeerId), } -/// Behaviour type. + +pub(crate) type MyHandlerError = << as NetworkBehaviour> + ::ConnectionHandler as IntoConnectionHandler>::Handler as ConnectionHandler>::Error; + #[derive(NetworkBehaviour)] pub struct NetworkBackendBehaviour { peers: AddressBook, kad: Toggle>, mdns: Toggle, - ping: Toggle, - identify: Toggle, + ping: Toggle, + identify: Toggle, bitswap: Toggle>, gossipsub: Toggle, broadcast: Toggle, - streams: Toggle, - - #[behaviour(ignore)] - bootstrap_complete: bool, - #[behaviour(ignore)] - queries: FnvHashMap, - #[behaviour(ignore)] - subscriptions: FnvHashMap>>, } -impl NetworkBehaviourEventProcess for NetworkBackendBehaviour

{ - fn inject_event(&mut self, event: MdnsEvent) { +impl NetworkBackendBehaviour

{ + pub fn inject_mdns_event(&mut self, event: mdns::Event) { match event { - MdnsEvent::Discovered(list) => { + mdns::Event::Discovered(list) => { for (peer_id, addr) in list { self.add_address(&peer_id, addr, AddressSource::Mdns); } } - MdnsEvent::Expired(_) => { + mdns::Event::Expired(_) => { // Ignore expired addresses } } @@ -159,16 +161,27 @@ pub struct KadGetRecordError(pub libp2p::kad::GetRecordError); #[error("{0:?}")] pub struct KadPutRecordError(pub libp2p::kad::PutRecordError); -impl NetworkBehaviourEventProcess for NetworkBackendBehaviour

{ - fn inject_event(&mut self, event: KademliaEvent) { +impl NetworkBackendBehaviour

{ + pub fn inject_kad_event( + &mut self, + event: KademliaEvent, + bootstrap_complete: &mut bool, + queries: &mut FnvHashMap, + ) { tracing::trace!("kademlia event {:?}", event); - if let KademliaEvent::OutboundQueryCompleted { id, result, .. } = event { + if let KademliaEvent::OutboundQueryProgressed { + id, + result, + step: ProgressStep { last, .. }, + .. + } = event + { match result { QueryResult::Bootstrap(Ok(BootstrapOk { num_remaining, .. })) => { tracing::trace!("remaining {}", num_remaining); if num_remaining == 0 { - self.bootstrap_complete = true; - if let Some(QueryChannel::Bootstrap(ch)) = self.queries.remove(&id.into()) { + *bootstrap_complete = true; + if let Some(QueryChannel::Bootstrap(ch)) = queries.remove(&id.into()) { ch.send(Ok(())).ok(); } self.peers.notify(Event::Bootstrapped); @@ -176,44 +189,52 @@ impl NetworkBehaviourEventProcess for NetworkBack } QueryResult::Bootstrap(Err(err)) => { tracing::trace!("{:?}", err); - if let Some(QueryChannel::Bootstrap(ch)) = self.queries.remove(&id.into()) { + if let Some(QueryChannel::Bootstrap(ch)) = queries.remove(&id.into()) { ch.send(Err(KadBootstrapError(err).into())).ok(); } } QueryResult::GetClosestPeers(Ok(GetClosestPeersOk { peers, .. })) => { - if let Some(QueryChannel::GetClosestPeers(ch)) = self.queries.remove(&id.into()) - { + if let Some(QueryChannel::GetClosestPeers(ch)) = queries.remove(&id.into()) { ch.send(Ok(peers)).ok(); } } QueryResult::GetClosestPeers(Err(err)) => { tracing::trace!("{:?}", err); - if let Some(QueryChannel::GetClosestPeers(ch)) = self.queries.remove(&id.into()) - { + if let Some(QueryChannel::GetClosestPeers(ch)) = queries.remove(&id.into()) { ch.send(Err(KadGetClosestPeersError(err).into())).ok(); } } - QueryResult::GetProviders(Ok(GetProvidersOk { providers, .. })) => { - if let Some(QueryChannel::GetProviders(ch)) = self.queries.remove(&id.into()) { - ch.send(Ok(providers)).ok(); + QueryResult::GetProviders(Ok(res)) => { + if let Some(QueryChannel::GetProviders(set, ..)) = queries.get_mut(&id.into()) { + match res { + GetProvidersOk::FoundProviders { providers, .. } => { + set.extend(providers) + } + GetProvidersOk::FinishedWithNoAdditionalRecord { .. } => {} + } + if last { + if let Some(QueryChannel::GetProviders(set, ch)) = + queries.remove(&id.into()) + { + ch.send(Ok(set)).ok(); + } + } } } QueryResult::GetProviders(Err(err)) => { tracing::trace!("{:?}", err); - if let Some(QueryChannel::GetProviders(ch)) = self.queries.remove(&id.into()) { + if let Some(QueryChannel::GetProviders(_, ch)) = queries.remove(&id.into()) { ch.send(Err(KadGetProvidersError(err).into())).ok(); } } QueryResult::StartProviding(Ok(AddProviderOk { .. })) => { - if let Some(QueryChannel::StartProviding(ch)) = self.queries.remove(&id.into()) - { + if let Some(QueryChannel::StartProviding(ch)) = queries.remove(&id.into()) { ch.send(Ok(())).ok(); } } QueryResult::StartProviding(Err(err)) => { tracing::trace!("{:?}", err); - if let Some(QueryChannel::StartProviding(ch)) = self.queries.remove(&id.into()) - { + if let Some(QueryChannel::StartProviding(ch)) = queries.remove(&id.into()) { ch.send(Err(KadAddProviderError(err).into())).ok(); } } @@ -221,25 +242,42 @@ impl NetworkBehaviourEventProcess for NetworkBack QueryResult::RepublishProvider(Err(err)) => { tracing::trace!("{:?}", err); } - QueryResult::GetRecord(Ok(GetRecordOk { records, .. })) => { - if let Some(QueryChannel::GetRecord(ch)) = self.queries.remove(&id.into()) { - ch.send(Ok(records)).ok(); + QueryResult::GetRecord(Ok(res)) => { + if let Some(QueryChannel::GetRecord(quorum, records, ..)) = + queries.get_mut(&id.into()) + { + match res { + GetRecordOk::FoundRecord(record) => records.push(record), + GetRecordOk::FinishedWithNoAdditionalRecord { .. } => {} + } + if last || records.len() >= *quorum { + if let Some(mut q) = + self.kad.as_mut().and_then(|kad| kad.query_mut(&id)) + { + q.finish(); + } + if let Some(QueryChannel::GetRecord(_, records, ch)) = + queries.remove(&id.into()) + { + ch.send(Ok(records)).ok(); + } + } } } QueryResult::GetRecord(Err(err)) => { tracing::trace!("{:?}", err); - if let Some(QueryChannel::GetRecord(ch)) = self.queries.remove(&id.into()) { + if let Some(QueryChannel::GetRecord(_, _, ch)) = queries.remove(&id.into()) { ch.send(Err(KadGetRecordError(err).into())).ok(); } } QueryResult::PutRecord(Ok(PutRecordOk { .. })) => { - if let Some(QueryChannel::PutRecord(ch)) = self.queries.remove(&id.into()) { + if let Some(QueryChannel::PutRecord(ch)) = queries.remove(&id.into()) { ch.send(Ok(())).ok(); } } QueryResult::PutRecord(Err(err)) => { tracing::trace!("{:?}", err); - if let Some(QueryChannel::PutRecord(ch)) = self.queries.remove(&id.into()) { + if let Some(QueryChannel::PutRecord(ch)) = queries.remove(&id.into()) { ch.send(Err(KadPutRecordError(err).into())).ok(); } } @@ -252,15 +290,19 @@ impl NetworkBehaviourEventProcess for NetworkBack } } -impl NetworkBehaviourEventProcess for NetworkBackendBehaviour

{ - fn inject_event(&mut self, event: BitswapEvent) { +impl NetworkBackendBehaviour

{ + pub fn inject_bitswap_event( + &mut self, + event: BitswapEvent, + queries: &mut FnvHashMap, + ) { match event { BitswapEvent::Progress(id, missing) => { - if let Some(QueryChannel::Sync(ch)) = self.queries.get(&id.into()) { + if let Some(QueryChannel::Sync(ch)) = queries.get(&id.into()) { ch.unbounded_send(SyncEvent::Progress { missing }).ok(); } } - BitswapEvent::Complete(id, result) => match self.queries.remove(&id.into()) { + BitswapEvent::Complete(id, result) => match queries.remove(&id.into()) { Some(QueryChannel::Get(ch)) => { ch.send(result).ok(); } @@ -273,59 +315,40 @@ impl NetworkBehaviourEventProcess for NetworkBacke } } -impl NetworkBehaviourEventProcess for NetworkBackendBehaviour

{ - fn inject_event(&mut self, event: PingEvent) { - // Don't really need to do anything here as ping handles disconnecting automatically. - match event { - PingEvent { - peer, - result: Result::Ok(PingSuccess::Ping { rtt }), - } => { +impl NetworkBackendBehaviour

{ + pub fn inject_ping_event(&mut self, event: ping::Event) { + // Don't really need to do anything here as ping handles disconnecting + // automatically. + let peer = event.peer; + match event.result { + Ok(ping::Success::Ping { rtt }) => { //tracing::trace!("ping: rtt to {} is {} ms", peer, rtt.as_millis()); self.peers.set_rtt(&peer, Some(rtt)); } - PingEvent { - peer: _, - result: Result::Ok(PingSuccess::Pong), - } => { + Ok(ping::Success::Pong) => { //tracing::trace!("ping: pong from {}", peer); } - PingEvent { - peer, - result: Result::Err(PingFailure::Timeout), - } => { - tracing::trace!("ping: timeout to {}", peer); + Err(ping::Failure::Timeout) => { + tracing::debug!("ping: timeout to {}", peer); self.peers.set_rtt(&peer, None); } - PingEvent { - peer, - result: Result::Err(PingFailure::Other { error }), - } => { - tracing::trace!("ping: failure with {}: {}", peer, error); + Err(ping::Failure::Other { error }) => { + tracing::info!("ping: failure with {}: {}", peer, error); self.peers.set_rtt(&peer, None); } - PingEvent { - peer, - result: Result::Err(PingFailure::Unsupported), - } => { + Err(ping::Failure::Unsupported) => { tracing::warn!("ping: {} does not support the ping protocol", peer); } } } } -impl NetworkBehaviourEventProcess for NetworkBackendBehaviour

{ - fn inject_event(&mut self, event: IdentifyEvent) { - // When a peer opens a connection we only have it's outgoing address. The identify - // protocol sends the listening address which needs to be registered with kademlia. - if let IdentifyEvent::Received { peer_id, info } = event { - let local_peer_id = *self.peers.local_peer_id(); - // source doesn't matter as it won't be added to address book. - self.add_address( - &local_peer_id, - info.observed_addr.clone(), - AddressSource::Peer, - ); +impl NetworkBackendBehaviour

{ + pub fn inject_id_event(&mut self, event: identify::Event) { + // When a peer opens a connection we only have it's outgoing address. The + // identify protocol sends the listening address which needs to be + // registered with kademlia. + if let identify::Event::Received { peer_id, info } = event { self.peers.set_info(&peer_id, info); } } @@ -335,8 +358,12 @@ impl NetworkBehaviourEventProcess for NetworkBack #[error("{0:?}")] pub struct GossipsubPublishError(pub libp2p::gossipsub::error::PublishError); -impl NetworkBehaviourEventProcess for NetworkBackendBehaviour

{ - fn inject_event(&mut self, event: GossipsubEvent) { +impl NetworkBackendBehaviour

{ + pub fn inject_gossip_event( + &mut self, + event: GossipsubEvent, + subscriptions: &mut FnvHashMap>>, + ) { match event { GossipsubEvent::Message { message: @@ -350,75 +377,81 @@ impl NetworkBehaviourEventProcess for NetworkBac .. } => { self.notify_subscribers( - &*topic.to_string(), + &topic.to_string(), GossipEvent::Message(source.unwrap_or(propagation_source), data.into()), + subscriptions, ); } GossipsubEvent::Subscribed { peer_id, topic, .. } => { self.peers .notify(Event::Subscribed(peer_id, topic.to_string())); - self.notify_subscribers(&*topic.to_string(), GossipEvent::Subscribed(peer_id)); + self.notify_subscribers( + &topic.to_string(), + GossipEvent::Subscribed(peer_id), + subscriptions, + ); } GossipsubEvent::Unsubscribed { peer_id, topic, .. } => { self.peers .notify(Event::Unsubscribed(peer_id, topic.to_string())); - self.notify_subscribers(&*topic.to_string(), GossipEvent::Unsubscribed(peer_id)); + self.notify_subscribers( + &topic.to_string(), + GossipEvent::Unsubscribed(peer_id), + subscriptions, + ); } + GossipsubEvent::GossipsubNotSupported { .. } => {} } } } -impl NetworkBehaviourEventProcess for NetworkBackendBehaviour

{ - fn inject_event(&mut self, event: BroadcastEvent) { +impl NetworkBackendBehaviour

{ + pub fn inject_broadcast_event( + &mut self, + event: BroadcastEvent, + subscriptions: &mut FnvHashMap>>, + ) { match event { BroadcastEvent::Received(peer_id, topic, data) => { let topic = std::str::from_utf8(&topic).unwrap(); - self.notify_subscribers(topic, GossipEvent::Message(peer_id, data)); + self.notify_subscribers(topic, GossipEvent::Message(peer_id, data), subscriptions); } BroadcastEvent::Subscribed(peer_id, topic) => { if let Ok(topic) = std::str::from_utf8(&topic) { self.peers.notify(Event::Subscribed(peer_id, topic.into())); - self.notify_subscribers(topic, GossipEvent::Subscribed(peer_id)); + self.notify_subscribers(topic, GossipEvent::Subscribed(peer_id), subscriptions); } } BroadcastEvent::Unsubscribed(peer_id, topic) => { if let Ok(topic) = std::str::from_utf8(&topic) { self.peers .notify(Event::Unsubscribed(peer_id, topic.into())); - self.notify_subscribers(topic, GossipEvent::Unsubscribed(peer_id)); + self.notify_subscribers( + topic, + GossipEvent::Unsubscribed(peer_id), + subscriptions, + ); } } } } } -impl NetworkBehaviourEventProcess for NetworkBackendBehaviour

{ - fn inject_event(&mut self, event: StreamSyncEvent) { - match event { - StreamSyncEvent::NewHead(head) => { - self.peers.notify(Event::NewHead(head)); - } - } - } -} - -impl NetworkBehaviourEventProcess for NetworkBackendBehaviour

{ - fn inject_event(&mut self, _event: void::Void) {} -} - impl NetworkBackendBehaviour

{ /// Create a Kademlia behaviour with the IPFS bootstrap nodes. - pub async fn new>( + pub fn new>( config: &mut NetworkConfig, store: S, + listeners: Writer>, + peers: Writer>, + external: Writer>, ) -> Result { - let public = config.node_key.public; - let node_key = config.node_key.to_keypair(); + let node_key = libp2p::identity::Keypair::Ed25519(config.node_key.clone()); let node_name = config.node_name.clone(); let peer_id = node_key.public().to_peer_id(); let mdns = if let Some(config) = config.mdns.take() { - Some(Mdns::new(config).await?) + Some(Mdns::new(config)?) } else { None }; @@ -428,11 +461,11 @@ impl NetworkBackendBehaviour

{ } else { None }; - let ping = config.ping.take().map(Ping::new); + let ping = config.ping.take().map(ping::Behaviour::new); let identify = if let Some(mut config) = config.identify.take() { config.local_public_key = node_key.public(); - config.agent_version = node_name.clone(); - Some(Identify::new(config)) + config.agent_version = node_name; + Some(identify::Behaviour::new(config)) } else { None }; @@ -448,19 +481,15 @@ impl NetworkBackendBehaviour

{ .bitswap .take() .map(|config| Bitswap::new(config, store)); - let streams = if let Some(config) = config.streams.take() { - Some(StreamSync::new(config)?) - } else { - None - }; Ok(Self { - bootstrap_complete: false, peers: AddressBook::new( peer_id, - node_name, - public, + config.port_reuse, config.enable_loopback, - config.prune_addresses, + config.keep_alive, + listeners, + peers, + external, ), mdns: mdns.into(), kad: kad.into(), @@ -469,20 +498,9 @@ impl NetworkBackendBehaviour

{ bitswap: bitswap.into(), gossipsub: gossipsub.into(), broadcast: broadcast.into(), - streams: streams.into(), - queries: Default::default(), - subscriptions: Default::default(), }) } - pub fn local_public_key(&self) -> &PublicKey { - self.peers.local_public_key() - } - - pub fn local_node_name(&self) -> &str { - self.peers.local_node_name() - } - pub fn add_address(&mut self, peer_id: &PeerId, addr: Multiaddr, source: AddressSource) { if let Some(kad) = self.kad.as_mut() { kad.add_address(peer_id, addr.clone()); @@ -497,32 +515,38 @@ impl NetworkBackendBehaviour

{ } } - pub fn dial(&mut self, peer_id: &PeerId) { - self.peers.dial(peer_id); + pub fn prune_peers(&mut self, min_age: Duration) { + self.peers.prune_peers(min_age); } - pub fn peers(&self) -> impl Iterator + '_ { - self.peers.peers() - } - - pub fn info(&self, peer_id: &PeerId) -> Option<&PeerInfo> { - self.peers.info(peer_id) + pub fn dial(&mut self, peer_id: &PeerId) { + self.peers.dial(peer_id); } - pub fn connections(&self) -> impl Iterator + '_ { - self.peers.connections() + pub fn dial_address(&mut self, peer_id: &PeerId, addr: Multiaddr) { + self.peers.dial_address(peer_id, addr); } - pub fn is_connected(&self, peer: &PeerId) -> bool { - self.peers.is_connected(peer) + pub(crate) fn connection_closed( + &mut self, + peer: PeerId, + cp: ConnectedPoint, + num_established: u32, + error: Option>, + ) { + self.peers + .connection_closed(peer, cp, num_established, error); } - pub fn bootstrap(&mut self) -> BootstrapChannel { - let (tx, rx) = oneshot::channel(); + pub fn bootstrap( + &mut self, + queries: &mut FnvHashMap, + tx: oneshot::Sender>, + ) { if let Some(kad) = self.kad.as_mut() { match kad.bootstrap() { Ok(id) => { - self.queries.insert(id.into(), QueryChannel::Bootstrap(tx)); + queries.insert(id.into(), QueryChannel::Bootstrap(tx)); } Err(err) => { tx.send(Err(err.into())).ok(); @@ -531,38 +555,41 @@ impl NetworkBackendBehaviour

{ } else { tx.send(Err(NotBootstrapped.into())).ok(); } - rx - } - - pub fn is_bootstrapped(&self) -> bool { - self.bootstrap_complete - } - - pub fn get_closest_peers(&mut self, key: K) -> GetClosestPeersChannel - where - K: Into> + Into> + Clone, - { - let (tx, rx) = oneshot::channel(); - if self.bootstrap_complete { - if let Some(kad) = self.kad.as_mut() { - let id = kad.get_closest_peers(key); - self.queries - .insert(id.into(), QueryChannel::GetClosestPeers(tx)); - } - } else { - tx.send(Err(NotBootstrapped.into())).ok(); - } - rx } - pub fn provide(&mut self, key: Key) -> StartProvidingChannel { - let (tx, rx) = oneshot::channel(); - if self.bootstrap_complete { + // pub fn get_closest_peers( + // &mut self, + // key: K, + // bootstrap_complete: bool, + // queries: &mut FnvHashMap, + // ) -> GetClosestPeersChannel + // where + // K: Into> + Into> + Clone, + // { + // let (tx, rx) = oneshot::channel(); + // if bootstrap_complete { + // if let Some(kad) = self.kad.as_mut() { + // let id = kad.get_closest_peers(key); + // queries.insert(id.into(), QueryChannel::GetClosestPeers(tx)); + // } + // } else { + // tx.send(Err(NotBootstrapped.into())).ok(); + // } + // rx + // } + + pub fn provide( + &mut self, + key: Key, + bootstrap_complete: bool, + queries: &mut FnvHashMap, + tx: oneshot::Sender>, + ) { + if bootstrap_complete { if let Some(kad) = self.kad.as_mut() { match kad.start_providing(key) { Ok(id) => { - self.queries - .insert(id.into(), QueryChannel::StartProviding(tx)); + queries.insert(id.into(), QueryChannel::StartProviding(tx)); } Err(err) => { tx.send(Err(KadStoreError(err).into())).ok(); @@ -572,7 +599,6 @@ impl NetworkBackendBehaviour

{ } else { tx.send(Err(NotBootstrapped.into())).ok(); } - rx } pub fn unprovide(&mut self, key: &Key) { @@ -581,40 +607,60 @@ impl NetworkBackendBehaviour

{ } } - pub fn providers(&mut self, key: Key) -> GetProvidersChannel { - let (tx, rx) = oneshot::channel(); - if self.bootstrap_complete { + pub fn providers( + &mut self, + key: Key, + bootstrap_complete: bool, + queries: &mut FnvHashMap, + tx: oneshot::Sender>>, + ) { + if bootstrap_complete { if let Some(kad) = self.kad.as_mut() { let id = kad.get_providers(key); - self.queries - .insert(id.into(), QueryChannel::GetProviders(tx)); + queries.insert(id.into(), QueryChannel::GetProviders(HashSet::new(), tx)); } } else { tx.send(Err(NotBootstrapped.into())).ok(); } - rx } - pub fn get_record(&mut self, key: &Key, quorum: Quorum) -> GetRecordChannel { - let (tx, rx) = oneshot::channel(); - if self.bootstrap_complete { + pub fn get_record( + &mut self, + key: Key, + quorum: Quorum, + bootstrap_complete: bool, + queries: &mut FnvHashMap, + tx: oneshot::Sender>>, + ) { + if bootstrap_complete { if let Some(kad) = self.kad.as_mut() { - let id = kad.get_record(key, quorum); - self.queries.insert(id.into(), QueryChannel::GetRecord(tx)); + let quorum = match quorum { + Quorum::One => 1, + Quorum::Majority => K_VALUE.get() / 2 + 1, + Quorum::All => K_VALUE.get(), + Quorum::N(n) => n.get(), + }; + let id = kad.get_record(key); + queries.insert(id.into(), QueryChannel::GetRecord(quorum, vec![], tx)); } } else { tx.send(Err(NotBootstrapped.into())).ok(); } - rx } - pub fn put_record(&mut self, record: Record, quorum: Quorum) -> PutRecordChannel { - let (tx, rx) = oneshot::channel(); - if self.bootstrap_complete { + pub fn put_record( + &mut self, + record: Record, + quorum: Quorum, + bootstrap_complete: bool, + queries: &mut FnvHashMap, + tx: oneshot::Sender>, + ) { + if bootstrap_complete { if let Some(kad) = self.kad.as_mut() { match kad.put_record(record, quorum) { Ok(id) => { - self.queries.insert(id.into(), QueryChannel::PutRecord(tx)); + queries.insert(id.into(), QueryChannel::PutRecord(tx)); } Err(err) => { tx.send(Err(KadStoreError(err).into())).ok(); @@ -624,7 +670,6 @@ impl NetworkBackendBehaviour

{ } else { tx.send(Err(NotBootstrapped.into())).ok(); } - rx } pub fn remove_record(&mut self, key: &Key) { @@ -633,18 +678,21 @@ impl NetworkBackendBehaviour

{ } } - pub fn subscribe(&mut self, topic: &str) -> Result> { + pub fn subscribe( + &mut self, + topic: &str, + subscriptions: &mut FnvHashMap>>, + ) -> Result> { if self.gossipsub.as_ref().is_none() && self.broadcast.as_ref().is_none() { return Err(DisabledProtocol("gossipsub and broadcast").into()); } let (tx, rx) = mpsc::unbounded(); - if let Some(subscribers) = self.subscriptions.get_mut(topic) { + if let Some(subscribers) = subscriptions.get_mut(topic) { subscribers.push(tx); } else { let gossip_topic = IdentTopic::new(topic); let broadcast_topic = Topic::new(gossip_topic.hash().as_str().as_ref()); - self.subscriptions - .insert(gossip_topic.hash().as_str().to_string(), vec![tx]); + subscriptions.insert(gossip_topic.hash().as_str().to_string(), vec![tx]); if let Some(gossipsub) = self.gossipsub.as_mut() { gossipsub .subscribe(&gossip_topic) @@ -670,12 +718,17 @@ impl NetworkBackendBehaviour

{ } } - fn notify_subscribers(&mut self, topic: &str, event: GossipEvent) { - if let Some(subscribers) = self.subscriptions.get_mut(topic) { + fn notify_subscribers( + &mut self, + topic: &str, + event: GossipEvent, + subscriptions: &mut FnvHashMap>>, + ) { + if let Some(subscribers) = subscriptions.get_mut(topic) { subscribers.retain(|subscriber| subscriber.unbounded_send(event.clone()).is_ok()); if subscribers.is_empty() { self.unsubscribe(topic); - self.subscriptions.remove(topic); + subscriptions.remove(topic); } } } @@ -712,11 +765,12 @@ impl NetworkBackendBehaviour

{ &mut self, cid: Cid, providers: impl Iterator, + queries: &mut FnvHashMap, ) -> (GetChannel, QueryId) { let bitswap = self.bitswap.as_mut().expect("bitswap enabled"); let (tx, rx) = oneshot::channel(); let id = bitswap.get(cid, providers); - self.queries.insert(id.into(), QueryChannel::Get(tx)); + queries.insert(id.into(), QueryChannel::Get(tx)); (rx, id.into()) } @@ -725,34 +779,23 @@ impl NetworkBackendBehaviour

{ cid: Cid, providers: Vec, missing: impl Iterator, + queries: &mut FnvHashMap, ) -> (SyncChannel, QueryId) { let bitswap = self.bitswap.as_mut().expect("bitswap enabled"); let (tx, rx) = mpsc::unbounded(); let id = bitswap.sync(cid, providers, missing); - self.queries.insert(id.into(), QueryChannel::Sync(tx)); + queries.insert(id.into(), QueryChannel::Sync(tx)); (rx, id.into()) } - pub fn cancel(&mut self, id: QueryId) { - self.queries.remove(&id); + pub fn cancel(&mut self, id: QueryId, queries: &mut FnvHashMap) { + queries.remove(&id); if let QueryId(InnerQueryId::Bitswap(id)) = id { self.bitswap.as_mut().unwrap().cancel(id); } } - pub fn register_metrics(&self, registry: &Registry) -> Result<()> { - if let Some(bitswap) = self.bitswap.as_ref() { - bitswap.register_metrics(registry)?; - } - self.peers.register_metrics(registry)?; - Ok(()) - } - - pub fn swarm_events(&mut self) -> SwarmEvents { - self.peers.swarm_events() - } - - pub fn streams(&mut self) -> &mut StreamSync { - self.streams.as_mut().expect("streams enabled") + pub fn swarm_events(&mut self, tx: UnboundedSender) { + self.peers.swarm_events(tx) } } diff --git a/src/net/config.rs b/src/net/config.rs index 9cde29f..9f697f2 100644 --- a/src/net/config.rs +++ b/src/net/config.rs @@ -1,17 +1,5 @@ -use crate::generate_keypair; -use libipld::store::{DefaultParams, StoreParams}; -pub use libp2p::dns::{ResolverConfig, ResolverOpts}; -pub use libp2p::gossipsub::GossipsubConfig; -pub use libp2p::identify::IdentifyConfig; -pub use libp2p::kad::record::store::MemoryStoreConfig as KadConfig; -pub use libp2p::mdns::MdnsConfig; -pub use libp2p::ping::PingConfig; -pub use libp2p_bitswap::BitswapConfig; -pub use libp2p_blake_streams::StreamSyncConfig; -pub use libp2p_broadcast::BroadcastConfig; -pub use libp2p_quic::{Keypair, ToLibp2p, TransportConfig}; -use std::path::PathBuf; -use std::time::Duration; +use crate::config::*; +use libp2p::identity::ed25519::Keypair; /// Network configuration. #[derive(Debug)] @@ -19,18 +7,18 @@ pub struct NetworkConfig { /// Enable adding loopback addresses to the address book. Should be /// enabled during testing and disabled in production. pub enable_loopback: bool, - /// Manage addresses in the address book automatically. This removes - /// them when an address is unreachable and removes the peer when there - /// is a dial failure. - pub prune_addresses: bool, + /// Enable binding to the listen port number when dialling peers + /// instead of using a random outgoing port. While this may allow + /// stricter firewall confiuration or shorter peer lists when interacting + /// with other IPFS implementations, it also opens up the possibility of + /// TCP simultaneous open, which leads to spurious dial errors. + pub port_reuse: bool, /// Node name. pub node_name: String, /// Node key. pub node_key: Keypair, /// Pre shared key. pub psk: Option<[u8; 32]>, - /// Quic config. - pub quic: TransportConfig, /// Dns config. If no dns config is provided the system /// defaults will be used. pub dns: Option, @@ -50,43 +38,43 @@ pub struct NetworkConfig { pub broadcast: Option, /// Bitswap config. pub bitswap: Option, - /// Streams config. - pub streams: Option, + /// Keep explicitly dialed and incoming connections open indefinitely + pub keep_alive: bool, } /// `DNS` configuration. #[derive(Debug)] -pub struct DnsConfig { - /// Configures the nameservers to use. - pub config: ResolverConfig, - /// Configuration for the resolver. - pub opts: ResolverOpts, +pub enum DnsConfig { + Custom { + /// Configures the nameservers to use. + config: ResolverConfig, + /// Configuration for the resolver. + opts: ResolverOpts, + }, + SystemWithFallback { + /// Configures the nameservers to use. + config: ResolverConfig, + /// Configuration for the resolver. + opts: ResolverOpts, + }, } impl NetworkConfig { /// Creates a new network configuration. - pub fn new(path: PathBuf, node_key: Keypair) -> Self { + pub fn new(node_key: Keypair) -> Self { let node_name = names::Generator::with_naming(names::Name::Numbered) .next() .unwrap(); - let identify = IdentifyConfig::new("/ipfs-embed/1.0".into(), node_key.to_public()); - let mut quic = TransportConfig::default(); - quic.keep_alive_interval(Some(Duration::from_millis(100))); - quic.max_concurrent_bidi_streams(1024).unwrap(); - quic.max_idle_timeout(Some(Duration::from_secs(10))) - .unwrap(); - quic.stream_receive_window(DefaultParams::MAX_BLOCK_SIZE as _) - .unwrap(); - quic.receive_window(4_000_000).unwrap(); - quic.send_window(4_000_000); - let node_key2 = Keypair::from_bytes(&node_key.to_bytes()).unwrap(); + let identify = IdentifyConfig::new( + "/ipfs-embed/1.0".into(), + libp2p::identity::PublicKey::Ed25519(node_key.public()), + ); Self { enable_loopback: true, - prune_addresses: true, + port_reuse: true, node_name, node_key, psk: None, - quic, dns: None, mdns: Some(MdnsConfig::default()), kad: Some(KadConfig::default()), @@ -95,15 +83,13 @@ impl NetworkConfig { gossipsub: Some(GossipsubConfig::default()), broadcast: Some(BroadcastConfig::default()), bitswap: Some(BitswapConfig::default()), - streams: Some(StreamSyncConfig::new(path, node_key2)), + keep_alive: false, } } } impl Default for NetworkConfig { fn default() -> Self { - let mut config = Self::new(Default::default(), generate_keypair()); - config.streams = None; - config + Self::new(Keypair::generate()) } } diff --git a/src/net/mod.rs b/src/net/mod.rs index a80a79b..b7fd169 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -1,83 +1,171 @@ +mod address_handler; +mod behaviour; +mod config; +mod peer_info; +mod peers; +#[cfg(test)] +mod tests; + +pub use self::{ + behaviour::{GossipEvent, QueryId, SyncEvent}, + config::{DnsConfig, NetworkConfig}, + peer_info::{AddressSource, ConnectionFailure, Direction, PeerInfo, Rtt}, + peers::{register_metrics, Event, SwarmEvents}, +}; + +use self::behaviour::{GetChannel, NetworkBackendBehaviour, QueryChannel, SyncChannel}; use crate::{ executor::{Executor, JoinHandle}, - net::behaviour::{GetChannel, NetworkBackendBehaviour, SyncChannel}, + variable::{Reader, Writer}, +}; +use anyhow::anyhow; +use chrono::{DateTime, Utc}; +use fnv::{FnvHashMap, FnvHashSet}; +use futures::{ + channel::{ + mpsc::{self, Receiver, Sender, TrySendError, UnboundedReceiver, UnboundedSender}, + oneshot, + }, + future::{self, Either}, + stream::{Stream, StreamExt}, + FutureExt, }; -use futures::stream::{Stream, StreamExt}; -use futures::task::AtomicWaker; -use futures::{channel::mpsc, future, pin_mut}; -use libipld::error::BlockNotFound; -use libipld::store::StoreParams; -use libipld::{Cid, Result}; -use libp2p::core::either::EitherTransport; -use libp2p::core::transport::Transport; -use libp2p::core::upgrade::{AuthenticationVersion, SelectUpgrade}; +use libipld::{error::BlockNotFound, store::StoreParams, Cid, Result}; #[cfg(feature = "async_global")] use libp2p::dns::DnsConfig as Dns; #[cfg(all(feature = "tokio", not(feature = "async_global")))] use libp2p::dns::TokioDnsConfig as Dns; -use libp2p::kad::kbucket::Key as BucketKey; -use libp2p::mplex::MplexConfig; -use libp2p::noise::{self, NoiseConfig, X25519Spec}; -use libp2p::pnet::{PnetConfig, PreSharedKey}; -use libp2p::swarm::{AddressScore, Swarm, SwarmBuilder}; #[cfg(feature = "async_global")] -use libp2p::tcp::TcpConfig; +use libp2p::tcp::async_io::Transport as TcpTransport; #[cfg(all(feature = "tokio", not(feature = "async_global")))] -use libp2p::tcp::TokioTcpConfig as TcpConfig; -use libp2p::yamux::YamuxConfig; -use parking_lot::Mutex; -use prometheus::Registry; -use std::collections::HashSet; -use std::future::Future; -use std::pin::Pin; -use std::sync::Arc; -use std::task::{Context, Poll}; -use std::time::Duration; - -mod behaviour; -mod config; -mod p2p_wrapper; -mod peers; - -pub use crate::net::behaviour::{GossipEvent, QueryId, SyncEvent}; -pub use crate::net::config::*; -pub use crate::net::peers::{AddressSource, Event, PeerInfo, SwarmEvents}; -pub use libp2p::core::connection::ListenerId; -pub use libp2p::kad::record::{Key, Record}; -pub use libp2p::kad::{PeerRecord, Quorum}; -pub use libp2p::swarm::AddressRecord; -pub use libp2p::{Multiaddr, PeerId, TransportError}; -pub use libp2p_bitswap::BitswapStore; -pub use libp2p_blake_streams::{ - DocId, Head, LocalStreamWriter, SignedHead, StreamId, StreamReader, +use libp2p::tcp::tokio::Transport as TcpTransport; +use libp2p::{ + core::{ + either::EitherTransport, + transport::{ListenerId, Transport}, + upgrade::{SelectUpgrade, Version}, + }, + dns::DnsErr, + identity::ed25519::PublicKey, + kad::{record::Key, PeerRecord, Quorum, Record}, + mplex::MplexConfig, + noise::{self, NoiseConfig, X25519Spec}, + pnet::{PnetConfig, PreSharedKey}, + swarm::{AddressRecord, AddressScore, Swarm, SwarmBuilder, SwarmEvent}, + tcp::Config as TcpConfig, + yamux::YamuxConfig, + Multiaddr, PeerId, +}; +use libp2p_bitswap::BitswapStore; +use std::{ + collections::HashSet, + future::Future, + pin::Pin, + sync::Arc, + task::{Context, Poll}, + time::Duration, }; -pub use libp2p_quic::{generate_keypair, PublicKey, SecretKey, ToLibp2p}; +use void::unreachable; #[derive(Clone, Debug, Eq, PartialEq)] pub enum ListenerEvent { NewListenAddr(Multiaddr), ExpiredListenAddr(Multiaddr), + ListenFailed(Multiaddr, String), +} + +#[derive(Debug)] +pub enum NetworkCommand { + ListenOn(Multiaddr, UnboundedSender), + AddExternalAddress(Multiaddr), + AddAddress(PeerId, Multiaddr), + AddAddresses(Vec<(PeerId, Multiaddr)>), + RemoveAddress(PeerId, Multiaddr), + PrunePeers(Duration), + Dial(PeerId), + DialAddress(PeerId, Multiaddr), + Ban(PeerId), + Unban(PeerId), + Bootstrap( + Vec<(PeerId, Multiaddr)>, + oneshot::Sender>, + ), + Providers(Key, oneshot::Sender>>), + Provide(Key, oneshot::Sender>), + Unprovide(Key), + GetRecord( + Key, + Quorum, + oneshot::Sender>>, + ), + PutRecord(Record, Quorum, oneshot::Sender>), + RemoveRecord(Key), + Subscribe( + String, + oneshot::Sender>>, + ), + Publish(String, Vec, oneshot::Sender>), + Broadcast(String, Vec, oneshot::Sender>), + Get(Cid, Vec, oneshot::Sender), + Sync(Cid, Vec, Vec, oneshot::Sender), + SwarmEvents(oneshot::Sender), + CancelQuery(QueryId), } #[derive(Clone)] -pub struct NetworkService { - executor: Executor, - swarm: Arc>>>, - waker: Arc, +pub struct NetworkService { + bootstrapped: Reader, + peers: Reader>, + listeners: Reader>, + external: Reader>, + public_key: PublicKey, + peer_id: PeerId, + node_name: String, + cmd: Sender, _swarm_task: Arc>, } -impl NetworkService

{ - pub async fn new>( +type TransportError = libp2p::core::transport::timeout::TransportTimeoutError< + libp2p::core::either::EitherError< + libp2p::core::either::EitherError< + libp2p::core::either::EitherError< + libp2p::core::either::EitherError, + std::io::Error, + >, + libp2p::core::upgrade::UpgradeError, + >, + libp2p::core::upgrade::UpgradeError< + libp2p::core::either::EitherError, + >, + >, +>; + +/// if this fails compilation, also change peers::is_sim_open() +fn assert_transport_error_type, U>(_: &T) {} + +impl NetworkService { + pub async fn new( mut config: NetworkConfig, store: S, executor: Executor, ) -> Result { - let peer_id = config.node_key.to_peer_id(); - let behaviour = NetworkBackendBehaviour::

::new(&mut config, store).await?; + let public_key = config.node_key.public(); + let peer_id = + PeerId::from_public_key(&libp2p::core::PublicKey::Ed25519(public_key.clone())); + let node_name = config.node_name.clone(); + + let peers = Writer::new(FnvHashMap::default()); + let peers2 = peers.reader(); + let listeners = Writer::new(FnvHashSet::default()); + let listeners2 = listeners.reader(); + let external = Writer::new(vec![]); + let external2 = external.reader(); + let behaviour = + NetworkBackendBehaviour::new(&mut config, store, listeners, peers, external)?; let tcp = { - let transport = TcpConfig::new().nodelay(true).port_reuse(true); + let transport = + TcpTransport::new(TcpConfig::new().nodelay(true).port_reuse(config.port_reuse)); let transport = if let Some(psk) = config.psk { let psk = PreSharedKey::new(psk); EitherTransport::Left( @@ -87,22 +175,20 @@ impl NetworkService

{ EitherTransport::Right(transport) }; let dh_key = noise::Keypair::::new() - .into_authentic(&config.node_key.to_keypair()) + .into_authentic(&libp2p::core::identity::Keypair::Ed25519( + config.node_key.clone(), + )) .unwrap(); - let transport = transport - .upgrade() - .authenticate_with_version( - NoiseConfig::xx(dh_key).into_authenticated(), - AuthenticationVersion::V1SimultaneousOpen, - ) + transport + .upgrade(Version::V1) + .authenticate(NoiseConfig::xx(dh_key).into_authenticated()) .multiplex(SelectUpgrade::new( YamuxConfig::default(), MplexConfig::new(), )) .timeout(Duration::from_secs(5)) - .boxed(); - p2p_wrapper::P2pWrapper(transport) }; + assert_transport_error_type::<_, TransportError>(&tcp); /*let quic = { QuicConfig { keypair: config.node_key, @@ -117,28 +203,56 @@ impl NetworkService

{ EitherOutput::First(first) => first, EitherOutput::Second(second) => second, });*/ - let quic_or_tcp = tcp; + let quic_or_tcp = tcp.boxed(); #[cfg(feature = "async_global")] let transport = if let Some(config) = config.dns { - Dns::custom(quic_or_tcp, config.config, config.opts) - .await? - .boxed() + match config { + DnsConfig::Custom { config, opts } => { + Dns::custom(quic_or_tcp, config, opts).await? + } + DnsConfig::SystemWithFallback { config, opts } => { + match trust_dns_resolver::system_conf::read_system_conf() { + Ok((config, opts)) => Dns::custom(quic_or_tcp, config, opts).await?, + Err(e) => { + tracing::warn!("falling back to custom DNS config, system default yielded error `${:#}`", e); + Dns::custom(quic_or_tcp, config, opts).await? + } + } + } + } } else { - Dns::system(quic_or_tcp).await?.boxed() + Dns::system(quic_or_tcp).await? }; #[cfg(all(feature = "tokio", not(feature = "async_global")))] let transport = if let Some(config) = config.dns { - Dns::custom(quic_or_tcp, config.config, config.opts)?.boxed() + match config { + DnsConfig::Custom { config, opts } => Dns::custom(quic_or_tcp, config, opts)?, + DnsConfig::SystemWithFallback { config, opts } => { + match trust_dns_resolver::system_conf::read_system_conf() { + Ok((config, opts)) => Dns::custom(quic_or_tcp, config, opts)?, + Err(e) => { + tracing::warn!("falling back to custom DNS config, system default yielded error `${:#}`", e); + Dns::custom(quic_or_tcp, config, opts)? + } + } + } + } } else { - Dns::system(quic_or_tcp)?.boxed() + Dns::system(quic_or_tcp)? }; + assert_transport_error_type::<_, DnsErr>(&transport); let exec = executor.clone(); - let swarm = SwarmBuilder::new(transport, behaviour, peer_id) - .executor(Box::new(move |fut| { + let swarm = SwarmBuilder::with_executor( + transport.boxed(), + behaviour, + peer_id, + Box::new(move |fut| { exec.spawn(fut).detach(); - })) - .build(); + }), + ) + .max_negotiating_inbound_streams(10000) + .build(); /* // Required for swarm book keeping. swarm @@ -146,335 +260,562 @@ impl NetworkService

{ .unwrap(); */ - let swarm = Arc::new(Mutex::new(swarm)); - let swarm2 = swarm.clone(); - let waker = Arc::new(AtomicWaker::new()); - let waker2 = waker.clone(); - let swarm_task = executor.spawn(future::poll_fn(move |cx| { - waker.register(cx.waker()); - let mut guard = swarm.lock(); - loop { - let swarm = &mut *guard; - pin_mut!(swarm); - if !swarm.poll_next(cx).is_ready() { - break; - } - } - Poll::Pending - })); + let bootstrapped = Writer::new(false); + let bootstrapped2 = bootstrapped.reader(); + let (cmd_tx, cmd_rx) = mpsc::channel(100); + let swarm_task = executor.spawn(poll_swarm( + cmd_rx, + cmd_tx.clone(), + swarm, + executor.clone(), + bootstrapped, + )); Ok(Self { - executor, - swarm: swarm2, - waker: waker2, + bootstrapped: bootstrapped2, + peers: peers2, + listeners: listeners2, + external: external2, + public_key, + peer_id, + node_name, + cmd: cmd_tx, _swarm_task: Arc::new(swarm_task), }) } pub fn local_public_key(&self) -> PublicKey { - let swarm = self.swarm.lock(); - *swarm.behaviour().local_public_key() + self.public_key.clone() } pub fn local_peer_id(&self) -> PeerId { - let swarm = self.swarm.lock(); - *swarm.local_peer_id() + self.peer_id } pub fn local_node_name(&self) -> String { - let swarm = self.swarm.lock(); - swarm.behaviour().local_node_name().to_string() - } - - pub fn listen_on(&self, addr: Multiaddr) -> Result> { - let mut swarm = self.swarm.lock(); - let stream = swarm.behaviour_mut().swarm_events(); - let listener = swarm.listen_on(addr)?; - self.waker.wake(); - Ok(stream - .take_while(move |event| match event { - Event::ListenerClosed(id) if *id == listener => future::ready(false), - _ => future::ready(true), - }) - .filter_map(move |event| match event { - Event::NewListenAddr(id, addr) if id == listener => { - future::ready(Some(ListenerEvent::NewListenAddr(addr))) - } - Event::ExpiredListenAddr(id, addr) if id == listener => { - future::ready(Some(ListenerEvent::ExpiredListenAddr(addr))) - } - _ => future::ready(None), - })) - } - - pub fn listeners(&self) -> Vec { - let swarm = self.swarm.lock(); - swarm.listeners().cloned().collect() + self.node_name.clone() } - pub fn add_external_address(&self, mut addr: Multiaddr) { - crate::net::peers::normalize_addr(&mut addr, &self.local_peer_id()); - let mut swarm = self.swarm.lock(); - swarm.add_external_address(addr, AddressScore::Infinite); - self.waker.wake(); + fn cmd(&mut self, msg: NetworkCommand) -> Option<(NetworkCommand, &'static str)> { + Self::handle_send_result(self.cmd.try_send(msg)) } - pub fn external_addresses(&self) -> Vec { - let swarm = self.swarm.lock(); - swarm.external_addresses().cloned().collect() + fn cmd_shared(&self, msg: NetworkCommand) -> Option<(NetworkCommand, &'static str)> { + Self::handle_send_result(self.cmd.clone().try_send(msg)) } - pub fn add_address(&self, peer: &PeerId, addr: Multiaddr) { - let mut swarm = self.swarm.lock(); - swarm - .behaviour_mut() - .add_address(peer, addr, AddressSource::User); - self.waker.wake(); + fn handle_send_result( + res: Result<(), TrySendError>, + ) -> Option<(NetworkCommand, &'static str)> { + match res { + Ok(_) => None, + Err(err) => { + let reason = if err.is_disconnected() { + "receiver went away" + } else { + "channel is full" + }; + let val = err.into_inner(); + tracing::warn!("failed IPFS swarm command {:?}: {}", val, reason); + Some((val, reason)) + } + } } - pub fn remove_address(&self, peer: &PeerId, addr: &Multiaddr) { - let mut swarm = self.swarm.lock(); - swarm.behaviour_mut().remove_address(peer, addr); - self.waker.wake(); + pub fn listen_on(&mut self, addr: Multiaddr) -> impl Stream { + let (tx, rx) = mpsc::unbounded(); + if let Some((NetworkCommand::ListenOn(addr, tx), reason)) = + self.cmd(NetworkCommand::ListenOn(addr, tx)) + { + tx.unbounded_send(ListenerEvent::ListenFailed( + addr, + format!("cannot send to Swarm: {}", reason), + )) + .ok(); + } + rx } - pub fn dial(&self, peer: &PeerId) { - let mut swarm = self.swarm.lock(); - swarm.behaviour_mut().dial(peer); - self.waker.wake(); + pub fn listeners(&self) -> Vec { + self.listeners.project(|l| l.iter().cloned().collect()) } - pub fn ban(&self, peer: PeerId) { - let mut swarm = self.swarm.lock(); - swarm.ban_peer_id(peer); - self.waker.wake(); + pub fn add_external_address(&mut self, mut addr: Multiaddr) { + peers::normalize_addr(&mut addr, &self.local_peer_id()); + self.cmd(NetworkCommand::AddExternalAddress(addr)); } - pub fn unban(&self, peer: PeerId) { - let mut swarm = self.swarm.lock(); - swarm.unban_peer_id(peer); - self.waker.wake(); + pub fn external_addresses(&self) -> Vec { + self.external.get_cloned() } - pub fn peers(&self) -> Vec { - let swarm = self.swarm.lock(); - swarm.behaviour().peers().copied().collect() + pub fn add_address(&mut self, peer: PeerId, addr: Multiaddr) { + self.cmd(NetworkCommand::AddAddress(peer, addr)); } - pub fn connections(&self) -> Vec<(PeerId, Multiaddr)> { - let swarm = self.swarm.lock(); - swarm - .behaviour() - .connections() - .map(|(peer_id, addr)| (*peer_id, addr.clone())) - .collect() + pub fn add_addresses(&mut self, addresses: Vec<(PeerId, Multiaddr)>) { + self.cmd(NetworkCommand::AddAddresses(addresses)); } - pub fn is_connected(&self, peer: &PeerId) -> bool { - let swarm = self.swarm.lock(); - swarm.behaviour().is_connected(peer) + pub fn remove_address(&mut self, peer: PeerId, addr: Multiaddr) { + self.cmd(NetworkCommand::RemoveAddress(peer, addr)); } - pub fn peer_info(&self, peer: &PeerId) -> Option { - let swarm = self.swarm.lock(); - swarm.behaviour().info(peer).cloned() + pub fn prune_peers(&mut self, min_age: Duration) { + self.cmd(NetworkCommand::PrunePeers(min_age)); } - pub async fn bootstrap(&self, peers: &[(PeerId, Multiaddr)]) -> Result<()> { - for (peer, addr) in peers { - self.add_address(peer, addr.clone()); - self.dial(peer); - } - let rx = { - let mut swarm = self.swarm.lock(); - swarm.behaviour_mut().bootstrap() - }; - tracing::trace!("started bootstrap"); - rx.await??; - tracing::trace!("boostrap complete"); - Ok(()) + pub fn dial(&mut self, peer: PeerId) { + self.cmd(NetworkCommand::Dial(peer)); } - pub fn is_bootstrapped(&self) -> bool { - let swarm = self.swarm.lock(); - swarm.behaviour().is_bootstrapped() + pub fn dial_address(&mut self, peer: PeerId, addr: Multiaddr) { + self.cmd(NetworkCommand::DialAddress(peer, addr)); } - pub async fn get_closest_peers(&self, key: K) -> Result> - where - K: Into> + Into> + Clone, - { - let rx = { - let mut swarm = self.swarm.lock(); - swarm.behaviour_mut().get_closest_peers(key) - }; - Ok(rx.await??) + pub fn ban(&mut self, peer: PeerId) { + self.cmd(NetworkCommand::Ban(peer)); } - pub async fn providers(&self, key: Key) -> Result> { - let rx = { - let mut swarm = self.swarm.lock(); - swarm.behaviour_mut().providers(key) - }; - Ok(rx.await??) + pub fn unban(&mut self, peer: PeerId) { + self.cmd(NetworkCommand::Unban(peer)); } - pub async fn provide(&self, key: Key) -> Result<()> { - let rx = { - let mut swarm = self.swarm.lock(); - swarm.behaviour_mut().provide(key) - }; - Ok(rx.await??) - } - - pub fn unprovide(&self, key: &Key) { - let mut swarm = self.swarm.lock(); - swarm.behaviour_mut().unprovide(key); - self.waker.wake(); + pub fn peers(&self) -> Vec { + self.peers.project(|peers| peers.keys().copied().collect()) + } + + pub fn connections(&self) -> Vec<(PeerId, Multiaddr, DateTime, Direction)> { + self.peers.project(|peers| { + peers + .iter() + .flat_map(|(peer, info)| { + info.connections + .iter() + .map(move |(a, t)| (*peer, a.clone(), t.0, t.1)) + }) + .collect() + }) } - pub async fn get_record(&self, key: &Key, quorum: Quorum) -> Result> { - let rx = { - let mut swarm = self.swarm.lock(); - swarm.behaviour_mut().get_record(key, quorum) - }; - Ok(rx.await??) + pub fn is_connected(&self, peer: &PeerId) -> bool { + *peer == self.local_peer_id() + || self.peers.project(|peers| { + peers + .get(peer) + .map(|info| !info.connections.is_empty()) + .unwrap_or(false) + }) } - pub async fn put_record(&self, record: Record, quorum: Quorum) -> Result<()> { - let rx = { - let mut swarm = self.swarm.lock(); - swarm.behaviour_mut().put_record(record, quorum) - }; - rx.await??; - Ok(()) + pub fn peer_info(&self, peer: &PeerId) -> Option { + self.peers.project(|peers| peers.get(peer).cloned()) } - pub fn remove_record(&self, key: &Key) { - let mut swarm = self.swarm.lock(); - swarm.behaviour_mut().remove_record(key); - self.waker.wake(); + pub fn bootstrap( + &mut self, + peers: Vec<(PeerId, Multiaddr)>, + ) -> impl Future> { + let (tx, rx) = oneshot::channel(); + if let Some((_, err)) = self.cmd(NetworkCommand::Bootstrap(peers, tx)) { + return future::ready(Err(anyhow!("{}", err))).left_future(); + } + tracing::debug!("started bootstrap"); + async { + rx.await??; + tracing::debug!("boostrap complete"); + Ok(()) + } + .right_future() } - pub fn subscribe(&self, topic: &str) -> Result> { - let mut swarm = self.swarm.lock(); - let stream = swarm.behaviour_mut().subscribe(topic)?; - self.waker.wake(); - Ok(stream) + pub fn is_bootstrapped(&self) -> bool { + self.bootstrapped.get() + } + + // This weird function signature seems impossible to support. WTF. + // pub async fn get_closest_peers(&self, key: K) -> Result> + // where + // K: Into> + Into> + Clone, + // { + // let rx = { + // let mut swarm = self.swarm.lock(); + // swarm.behaviour_mut().get_closest_peers(key) + // }; + // Ok(rx.await??) + // } + + pub fn providers(&mut self, key: Key) -> impl Future>> { + let (tx, rx) = oneshot::channel(); + if let Some((_, err)) = self.cmd(NetworkCommand::Providers(key, tx)) { + return future::ready(Err(anyhow!("{}", err))).left_future(); + } + async { rx.await? }.right_future() } - pub fn publish(&self, topic: &str, msg: Vec) -> Result<()> { - let mut swarm = self.swarm.lock(); - swarm.behaviour_mut().publish(topic, msg)?; - self.waker.wake(); - Ok(()) + pub fn provide(&mut self, key: Key) -> impl Future> { + let (tx, rx) = oneshot::channel(); + if let Some((_, err)) = self.cmd(NetworkCommand::Provide(key, tx)) { + return future::ready(Err(anyhow!("{}", err))).left_future(); + } + async { rx.await? }.right_future() } - pub fn broadcast(&self, topic: &str, msg: Vec) -> Result<()> { - let mut swarm = self.swarm.lock(); - swarm.behaviour_mut().broadcast(topic, msg)?; - self.waker.wake(); + pub fn unprovide(&mut self, key: Key) -> Result<()> { + if let Some((_, err)) = self.cmd(NetworkCommand::Unprovide(key)) { + return Err(anyhow!("{}", err)); + } Ok(()) } - pub fn get(&self, cid: Cid, providers: impl Iterator) -> GetQuery

{ - let mut swarm = self.swarm.lock(); - let (rx, id) = swarm.behaviour_mut().get(cid, providers); - self.waker.wake(); - GetQuery { - swarm: Some(self.swarm.clone()), - id, - rx, + pub fn get_record( + &mut self, + key: Key, + quorum: Quorum, + ) -> impl Future>> { + let (tx, rx) = oneshot::channel(); + if let Some((_, err)) = self.cmd(NetworkCommand::GetRecord(key, quorum, tx)) { + return future::ready(Err(anyhow!("{}", err))).left_future(); } + async { rx.await? }.right_future() } - pub fn sync(&self, cid: Cid, providers: Vec, missing: Vec) -> SyncQuery

{ - if missing.is_empty() { - return SyncQuery::ready(Ok(())); - } - if providers.is_empty() { - return SyncQuery::ready(Err(BlockNotFound(missing[0]).into())); - } - let mut swarm = self.swarm.lock(); - let (rx, id) = swarm - .behaviour_mut() - .sync(cid, providers, missing.into_iter()); - self.waker.wake(); - SyncQuery { - swarm: Some(self.swarm.clone()), - id: Some(id), - rx, + pub fn put_record( + &mut self, + record: Record, + quorum: Quorum, + ) -> impl Future> { + let (tx, rx) = oneshot::channel(); + if let Some((_, err)) = self.cmd(NetworkCommand::PutRecord(record, quorum, tx)) { + return future::ready(Err(anyhow!("{}", err))).left_future(); } + async { rx.await? }.right_future() } - pub fn register_metrics(&self, registry: &Registry) -> Result<()> { - let swarm = self.swarm.lock(); - swarm.behaviour().register_metrics(registry) - } - - pub fn swarm_events(&self) -> SwarmEvents { - let mut swarm = self.swarm.lock(); - swarm.behaviour_mut().swarm_events() - } - - pub fn docs(&self) -> Result> { - let mut swarm = self.swarm.lock(); - swarm.behaviour_mut().streams().docs() - } - - pub fn streams(&self) -> Result> { - let mut swarm = self.swarm.lock(); - swarm.behaviour_mut().streams().streams() + pub fn remove_record(&mut self, key: Key) -> Result<()> { + if let Some((_, err)) = self.cmd(NetworkCommand::RemoveRecord(key)) { + return Err(anyhow!("{}", err)); + } + Ok(()) } - pub fn substreams(&self, doc: DocId) -> Result> { - let mut swarm = self.swarm.lock(); - swarm.behaviour_mut().streams().substreams(doc) + pub fn subscribe( + &mut self, + topic: String, + ) -> impl Future>> { + let (tx, rx) = oneshot::channel(); + if let Some((_, err)) = self.cmd(NetworkCommand::Subscribe(topic, tx)) { + return future::ready(Err(anyhow!("{}", err))).left_future(); + } + async { rx.await? }.right_future() } - pub fn stream_add_peers(&self, doc: DocId, peers: impl Iterator) { - let mut swarm = self.swarm.lock(); - swarm.behaviour_mut().streams().add_peers(doc, peers) + pub fn publish(&mut self, topic: String, msg: Vec) -> impl Future> { + let (tx, rx) = oneshot::channel(); + if let Some((_, err)) = self.cmd(NetworkCommand::Publish(topic, msg, tx)) { + return future::ready(Err(anyhow!("{}", err))).left_future(); + } + async { rx.await? }.right_future() } - pub fn stream_head(&self, id: &StreamId) -> Result> { - let mut swarm = self.swarm.lock(); - swarm.behaviour_mut().streams().head(id) + pub fn broadcast(&mut self, topic: String, msg: Vec) -> impl Future> { + let (tx, rx) = oneshot::channel(); + if let Some((_, err)) = self.cmd(NetworkCommand::Broadcast(topic, msg, tx)) { + return future::ready(Err(anyhow!("{}", err))).left_future(); + } + async { rx.await? }.right_future() } - pub fn stream_slice(&self, id: &StreamId, start: u64, len: u64) -> Result { - let mut swarm = self.swarm.lock(); - swarm.behaviour_mut().streams().slice(id, start, len) + // This cannot take `&mut self` due to trait constraints, so it needs to use the less efficient cmd_shared. + pub fn get(&self, cid: Cid, providers: Vec) -> impl Future> { + let (tx, rx) = oneshot::channel(); + if let Some((_, err)) = self.cmd_shared(NetworkCommand::Get(cid, providers, tx)) { + return future::ready(Err(anyhow!("{}", err))).left_future(); + } + async { Ok(rx.await?) }.right_future() } - pub fn stream_remove(&self, id: &StreamId) -> Result<()> { - let mut swarm = self.swarm.lock(); - swarm.behaviour_mut().streams().remove(id) + // This cannot take `&mut self` due to trait constraints, so it needs to use the less efficient cmd_shared. + pub fn sync( + &self, + cid: Cid, + providers: Vec, + missing: Vec, + ) -> impl Future> { + if missing.is_empty() { + return future::ready(Ok(SyncQuery::ready(Ok(())))).left_future(); + } + if providers.is_empty() { + return future::ready(Ok(SyncQuery::ready(Err(BlockNotFound(missing[0]).into())))) + .left_future(); + } + let (tx, rx) = oneshot::channel(); + if let Some((_, err)) = self.cmd_shared(NetworkCommand::Sync(cid, providers, missing, tx)) { + return future::ready(Err(anyhow!("{}", err))).left_future(); + } + async { Ok(rx.await?) }.right_future() } - pub fn stream_append(&self, id: DocId) -> Result { - let mut swarm = self.swarm.lock(); - swarm.behaviour_mut().streams().append(id) + pub fn swarm_events(&mut self) -> impl Future> { + let (tx, rx) = oneshot::channel(); + if let Some((_, err)) = self.cmd(NetworkCommand::SwarmEvents(tx)) { + return future::ready(Err(anyhow!("{}", err))).left_future(); + } + async { Ok(rx.await?) }.right_future() } +} - pub fn stream_subscribe(&self, id: &StreamId) -> Result<()> { - let mut swarm = self.swarm.lock(); - swarm.behaviour_mut().streams().subscribe(id) +async fn poll_swarm( + mut cmd_rx: Receiver, + cmd_tx: Sender, + mut swarm: Swarm>, + executor: Executor, + bootstrapped: Writer, +) { + let mut subscriptions = + FnvHashMap::>>::default(); + let mut queries = FnvHashMap::::default(); + loop { + match future::select( + future::poll_fn(|cx| { + tracing::trace!("polling swarm ({:?})", std::thread::current().id()); + swarm.poll_next_unpin(cx) + }), + cmd_rx.next(), + ) + .await + { + Either::Left((None, _)) => { + tracing::debug!("poll_swarm: swarm stream ended, terminating"); + return; + } + Either::Left((Some(cmd), _)) => match cmd { + SwarmEvent::ConnectionClosed { + peer_id, + endpoint, + num_established, + cause, + } => swarm.behaviour_mut().connection_closed( + peer_id, + endpoint, + num_established, + cause, + ), + SwarmEvent::Behaviour(event) => { + let swarm = swarm.behaviour_mut(); + match event { + behaviour::NetworkBackendBehaviourEvent::Peers(e) => unreachable(e), + behaviour::NetworkBackendBehaviourEvent::Kad(e) => { + let mut bootstrap_complete = *bootstrapped.read(); + let bootstrap_old = bootstrap_complete; + // DO NOT HOLD bootstrapped LOCK ACROSS ARBITRARY CODE + swarm.inject_kad_event(e, &mut bootstrap_complete, &mut queries); + if bootstrap_complete != bootstrap_old { + *bootstrapped.write() = bootstrap_complete; + } + } + behaviour::NetworkBackendBehaviourEvent::Mdns(e) => { + swarm.inject_mdns_event(e); + } + behaviour::NetworkBackendBehaviourEvent::Ping(e) => { + swarm.inject_ping_event(e); + } + behaviour::NetworkBackendBehaviourEvent::Identify(e) => { + swarm.inject_id_event(e); + } + behaviour::NetworkBackendBehaviourEvent::Bitswap(e) => { + swarm.inject_bitswap_event(e, &mut queries); + } + behaviour::NetworkBackendBehaviourEvent::Gossipsub(e) => { + swarm.inject_gossip_event(e, &mut subscriptions); + } + behaviour::NetworkBackendBehaviourEvent::Broadcast(e) => { + swarm.inject_broadcast_event(e, &mut subscriptions); + } + } + } + _ => {} + }, + Either::Right((None, _)) => { + tracing::debug!("poll_swarm: command sender dropped, terminating"); + return; + } + Either::Right((Some(cmd), _)) => match cmd { + NetworkCommand::ListenOn(addr, response) => { + let (tx, rx) = mpsc::unbounded(); + swarm.behaviour_mut().swarm_events(tx); + match swarm.listen_on(addr.clone()) { + Ok(listener) => executor + .spawn(forward_listener_events(listener, response, rx)) + .detach(), + Err(error) => { + response + .unbounded_send(ListenerEvent::ListenFailed( + addr, + error.to_string(), + )) + .ok(); + } + }; + } + NetworkCommand::AddExternalAddress(addr) => { + swarm.add_external_address(addr, AddressScore::Infinite); + } + NetworkCommand::AddAddress(peer, addr) => { + swarm + .behaviour_mut() + .add_address(&peer, addr, AddressSource::User); + } + NetworkCommand::AddAddresses(addresses) => { + for (peer, addr) in addresses { + swarm + .behaviour_mut() + .add_address(&peer, addr, AddressSource::User); + } + } + NetworkCommand::RemoveAddress(peer, addr) => { + swarm.behaviour_mut().remove_address(&peer, &addr); + } + NetworkCommand::PrunePeers(min_age) => { + swarm.behaviour_mut().prune_peers(min_age); + } + NetworkCommand::Dial(peer) => { + swarm.behaviour_mut().dial(&peer); + } + NetworkCommand::DialAddress(peer, addr) => { + swarm.behaviour_mut().dial_address(&peer, addr); + } + NetworkCommand::Ban(peer) => { + swarm.ban_peer_id(peer); + } + NetworkCommand::Unban(peer) => { + swarm.unban_peer_id(peer); + } + NetworkCommand::Bootstrap(initial, tx) => { + let swarm = swarm.behaviour_mut(); + for (peer, addr) in initial { + swarm.add_address(&peer, addr.clone(), AddressSource::User); + swarm.dial(&peer); + } + swarm.bootstrap(&mut queries, tx); + } + NetworkCommand::Providers(key, tx) => { + let bootstrap_complete = *bootstrapped.read(); + swarm + .behaviour_mut() + .providers(key, bootstrap_complete, &mut queries, tx); + } + NetworkCommand::Provide(key, tx) => { + let bootstrap_complete = *bootstrapped.read(); + swarm + .behaviour_mut() + .provide(key, bootstrap_complete, &mut queries, tx); + } + NetworkCommand::Unprovide(key) => { + swarm.behaviour_mut().unprovide(&key); + } + NetworkCommand::GetRecord(key, quorum, tx) => { + let bootstrap_complete = *bootstrapped.read(); + swarm.behaviour_mut().get_record( + key, + quorum, + bootstrap_complete, + &mut queries, + tx, + ); + } + NetworkCommand::PutRecord(record, quorum, tx) => { + let bootstrap_complete = *bootstrapped.read(); + swarm.behaviour_mut().put_record( + record, + quorum, + bootstrap_complete, + &mut queries, + tx, + ); + } + NetworkCommand::RemoveRecord(key) => { + swarm.behaviour_mut().remove_record(&key); + } + NetworkCommand::Subscribe(topic, tx) => { + tx.send(swarm.behaviour_mut().subscribe(&topic, &mut subscriptions)) + .ok(); + } + NetworkCommand::Publish(topic, msg, tx) => { + tx.send(swarm.behaviour_mut().publish(&topic, msg)).ok(); + } + NetworkCommand::Broadcast(topic, msg, tx) => { + tx.send(swarm.behaviour_mut().broadcast(&topic, msg)).ok(); + } + NetworkCommand::Get(cid, providers, tx) => { + let (rx, id) = + swarm + .behaviour_mut() + .get(cid, providers.into_iter(), &mut queries); + tx.send(GetQuery { + swarm: cmd_tx.clone(), + id, + rx, + }) + .ok(); + } + NetworkCommand::Sync(cid, providers, missing, tx) => { + let (rx, id) = swarm.behaviour_mut().sync( + cid, + providers, + missing.into_iter(), + &mut queries, + ); + tx.send(SyncQuery { + swarm: Some(cmd_tx.clone()), + id: Some(id), + rx, + }) + .ok(); + } + NetworkCommand::SwarmEvents(result) => { + let (tx, rx) = mpsc::unbounded(); + swarm.behaviour_mut().swarm_events(tx); + result.send(SwarmEvents::new(rx)).ok(); + } + NetworkCommand::CancelQuery(id) => { + swarm.behaviour_mut().cancel(id, &mut queries); + } + }, + } } +} - pub fn stream_update_head(&self, head: SignedHead) { - let mut swarm = self.swarm.lock(); - swarm.behaviour_mut().streams().update_head(head) - } +fn forward_listener_events( + listener: ListenerId, + response: UnboundedSender, + rx: UnboundedReceiver, +) -> impl Future { + rx.take_while(move |event| match event { + Event::ListenerClosed(id) if *id == listener => future::ready(false), + Event::NewListenAddr(id, addr) if *id == listener => future::ready( + response + .unbounded_send(ListenerEvent::NewListenAddr(addr.clone())) + .is_ok(), + ), + Event::ExpiredListenAddr(id, addr) if *id == listener => future::ready( + response + .unbounded_send(ListenerEvent::ExpiredListenAddr(addr.clone())) + .is_ok(), + ), + _ => future::ready(true), + }) + .for_each(|_| future::ready(())) } -pub struct GetQuery { - swarm: Option>>>>, +#[derive(Debug)] +pub struct GetQuery { + swarm: Sender, id: QueryId, rx: GetChannel, } -impl Future for GetQuery

{ +impl Future for GetQuery { type Output = Result<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { @@ -486,22 +827,25 @@ impl Future for GetQuery

{ } } -impl Drop for GetQuery

{ +impl Drop for GetQuery { fn drop(&mut self) { - let swarm = self.swarm.take().unwrap(); - let mut swarm = swarm.lock(); - swarm.behaviour_mut().cancel(self.id); + if let Err(err) = self.swarm.try_send(NetworkCommand::CancelQuery(self.id)) { + if !err.is_disconnected() { + tracing::warn!("cannot cancel dropped GetQuery: {}", err.into_send_error()); + } + } } } /// A `bitswap` sync query. -pub struct SyncQuery { - swarm: Option>>>>, +#[derive(Debug)] +pub struct SyncQuery { + swarm: Option>, id: Option, rx: SyncChannel, } -impl SyncQuery

{ +impl SyncQuery { fn ready(res: Result<()>) -> Self { let (tx, rx) = mpsc::unbounded(); tx.unbounded_send(SyncEvent::Complete(res)).unwrap(); @@ -513,12 +857,14 @@ impl SyncQuery

{ } } -impl Future for SyncQuery

{ +impl Future for SyncQuery { type Output = Result<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { loop { - match Pin::new(&mut self.rx).poll_next(cx) { + let poll = Pin::new(&mut self.rx).poll_next(cx); + tracing::trace!("sync progress: {:?}", poll); + match poll { Poll::Ready(Some(SyncEvent::Complete(result))) => return Poll::Ready(result), Poll::Ready(_) => continue, Poll::Pending => return Poll::Pending, @@ -527,20 +873,24 @@ impl Future for SyncQuery

{ } } -impl Stream for SyncQuery

{ +impl Stream for SyncQuery { type Item = SyncEvent; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - Pin::new(&mut self.rx).poll_next(cx) + let poll = Pin::new(&mut self.rx).poll_next(cx); + tracing::trace!("sync progress: {:?}", poll); + poll } } -impl Drop for SyncQuery

{ +impl Drop for SyncQuery { fn drop(&mut self) { - if let Some(id) = self.id.take() { - let swarm = self.swarm.take().unwrap(); - let mut swarm = swarm.lock(); - swarm.behaviour_mut().cancel(id); + if let (Some(id), Some(mut swarm)) = (self.id.take(), self.swarm.take()) { + if let Err(err) = swarm.try_send(NetworkCommand::CancelQuery(id)) { + if !err.is_disconnected() { + tracing::warn!("cannot cancel dropped SyncQuery: {}", err.into_send_error()); + } + } } } } diff --git a/src/net/p2p_wrapper.rs b/src/net/p2p_wrapper.rs deleted file mode 100644 index 0fee6c2..0000000 --- a/src/net/p2p_wrapper.rs +++ /dev/null @@ -1,63 +0,0 @@ -use futures::stream::{BoxStream, StreamExt, TryStreamExt}; -use libp2p::core::multiaddr::{Multiaddr, Protocol}; -use libp2p::core::muxing::StreamMuxerBox; -use libp2p::core::transport::{ListenerEvent, Transport, TransportError}; -use libp2p::PeerId; - -#[derive(Clone)] -pub struct P2pWrapper(pub T); - -impl> Transport for P2pWrapper -where - T::Listener: Send + 'static, - T::ListenerUpgrade: Send + 'static, - T::Error: Send + 'static, -{ - type Output = T::Output; - type Error = T::Error; - #[allow(clippy::type_complexity)] - type Listener = - BoxStream<'static, Result, Self::Error>>; - type ListenerUpgrade = futures::future::Ready>; - type Dial = T::Dial; - - fn listen_on(self, addr: Multiaddr) -> Result> { - Ok(self - .0 - .listen_on(addr)? - .and_then(|event| async move { - Ok(match event { - ListenerEvent::Upgrade { - local_addr, - mut remote_addr, - upgrade, - } => { - let upgrade = match upgrade.await { - Ok((peer, muxer)) => { - remote_addr.push(Protocol::P2p(peer.into())); - futures::future::ok((peer, muxer)) - } - Err(err) => futures::future::err(err), - }; - ListenerEvent::Upgrade { - local_addr, - remote_addr, - upgrade, - } - } - ListenerEvent::NewAddress(addr) => ListenerEvent::NewAddress(addr), - ListenerEvent::AddressExpired(addr) => ListenerEvent::AddressExpired(addr), - ListenerEvent::Error(err) => ListenerEvent::Error(err), - }) - }) - .boxed()) - } - - fn dial(self, addr: Multiaddr) -> Result> { - self.0.dial(addr) - } - - fn address_translation(&self, listen: &Multiaddr, observed: &Multiaddr) -> Option { - self.0.address_translation(listen, observed) - } -} diff --git a/src/net/peer_info.rs b/src/net/peer_info.rs new file mode 100644 index 0000000..5d54e1f --- /dev/null +++ b/src/net/peer_info.rs @@ -0,0 +1,374 @@ +use chrono::{DateTime, Utc}; +use fnv::FnvHashMap; +use libp2p::{ + core::ConnectedPoint, multiaddr::Protocol, swarm::DialError, Multiaddr, TransportError, +}; +use std::{ + borrow::Cow, cmp::Ordering, collections::VecDeque, error::Error, fmt::Write, io, time::Duration, +}; + +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct PeerInfo { + pub(crate) protocol_version: Option, + pub(crate) agent_version: Option, + pub(crate) protocols: Vec, + pub(crate) listeners: Vec, + pub(crate) addresses: FnvHashMap)>, + pub(crate) connections: FnvHashMap, Direction)>, + failures: VecDeque, + rtt: Option, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Direction { + Inbound, + Outbound, +} + +impl From<&ConnectedPoint> for Direction { + fn from(cp: &ConnectedPoint) -> Self { + match cp { + ConnectedPoint::Dialer { .. } => Direction::Outbound, + ConnectedPoint::Listener { .. } => Direction::Inbound, + } + } +} + +impl PeerInfo { + pub fn protocol_version(&self) -> Option<&str> { + self.protocol_version.as_deref() + } + + pub fn agent_version(&self) -> Option<&str> { + self.agent_version.as_deref() + } + + pub fn protocols(&self) -> impl Iterator + '_ { + self.protocols.iter().map(|s| &**s) + } + + pub fn listen_addresses(&self) -> impl Iterator { + self.listeners.iter() + } + + pub fn addresses( + &self, + ) -> impl Iterator)> + '_ { + self.addresses + .iter() + .map(|(addr, (source, dt))| (addr, *source, *dt)) + } + + pub fn connections(&self) -> impl Iterator, Direction)> { + self.connections.iter().map(|(a, (dt, dir))| (a, *dt, *dir)) + } + + pub fn rtt(&self) -> Option { + self.rtt.map(|x| x.current) + } + + pub fn full_rtt(&self) -> Option { + self.rtt + } + + pub(crate) fn set_rtt(&mut self, rtt: Option) { + if let Some(duration) = rtt { + if let Some(ref mut rtt) = self.rtt { + rtt.register(duration); + } else { + self.rtt = Some(Rtt::new(duration)); + } + } else if let Some(ref mut rtt) = self.rtt { + rtt.register_failure(); + } + } + + pub fn recent_failures(&self) -> impl Iterator { + self.failures.iter() + } + + pub(crate) fn push_failure( + &mut self, + addr: &Multiaddr, + f: ConnectionFailure, + probe_result: bool, + ) { + if self.failures.len() > 9 { + self.failures.pop_back(); + } + if probe_result + && self + .addresses + .get(addr) + .iter() + .any(|(s, _dt)| s.is_to_probe()) + { + self.addresses.remove(addr); + } + self.failures.push_front(f); + } + + pub fn confirmed_addresses(&self) -> impl Iterator { + self.addresses + .iter() + .filter(|x| x.1 .0.is_confirmed()) + .map(|x| x.0) + } + + pub fn addresses_to_probe(&self) -> impl Iterator { + self.addresses + .iter() + .filter(|x| x.1 .0.is_to_probe()) + .map(|x| x.0) + } + + pub fn addresses_to_translate(&self) -> impl Iterator { + self.addresses + .iter() + .filter(|x| x.1 .0.is_to_translate()) + .map(|x| x.0) + } + + pub(crate) fn ingest_address(&mut self, addr: Multiaddr, source: AddressSource) -> bool { + if let Some((src, dt)) = self.addresses.get_mut(&addr) { + *dt = Utc::now(); + match source.cmp(src) { + Ordering::Less => false, + Ordering::Equal => false, + Ordering::Greater => { + *src = source; + source.is_to_probe() + } + } + } else { + debug_assert!(matches!(addr.iter().last(), Some(Protocol::P2p(_)))); + self.addresses.insert(addr, (source, Utc::now())); + source.is_to_probe() + } + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct Rtt { + current: Duration, + decay_3: Duration, + decay_10: Duration, + failures: u32, + failure_rate: u32, +} + +impl Rtt { + pub fn new(current: Duration) -> Self { + Self { + current, + decay_3: current, + decay_10: current, + failures: 0, + failure_rate: 0, + } + } + + pub fn register(&mut self, current: Duration) { + self.current = current; + self.decay_3 = self.decay_3 * 7 / 10 + current * 3 / 10; + self.decay_10 = self.decay_10 * 9 / 10 + current / 10; + self.failures = 0; + self.failure_rate = self.failure_rate * 99 / 100; + } + + pub fn register_failure(&mut self) { + self.failures += 1; + // failures decay at 1% rate, failure_rate is 1_000_000 for only failures + self.failure_rate = self.failure_rate * 99 / 100 + 10_000; + } + + /// Get the rtt's last recent value. + pub fn current(&self) -> Duration { + self.current + } + + /// Get the rtt's exponentially weighted moving average + /// + /// Decay parameter is 30%. + pub fn decay_3(&self) -> Duration { + self.decay_3 + } + + /// Get the rtt's exponentially weighted moving average + /// + /// Decay parameter is 10%. + pub fn decay_10(&self) -> Duration { + self.decay_10 + } + + /// Get the rtt's failure counter. + pub fn failures(&self) -> u32 { + self.failures + } + + /// Get the rtt's exponentially weighted moving average of the failure rate + /// + /// Decay parameter is 1% and the returned value is rate * 1e6. + pub fn failure_rate(&self) -> u32 { + self.failure_rate + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub enum AddressSource { + Incoming, + Listen, + Kad, + Mdns, + Candidate, + User, + Dial, +} + +impl AddressSource { + pub fn is_confirmed(&self) -> bool { + matches!( + self, + AddressSource::Dial | AddressSource::User | AddressSource::Candidate + ) + } + pub fn is_to_probe(&self) -> bool { + matches!( + self, + AddressSource::Listen + | AddressSource::Kad + | AddressSource::Mdns + | AddressSource::Candidate + ) + } + pub fn is_to_translate(&self) -> bool { + matches!(self, AddressSource::Incoming) + } +} + +#[test] +fn address_source_order() { + use AddressSource::*; + assert!(Dial > User); + assert!(User > Mdns); + assert!(Mdns > Kad); + assert!(Kad > Listen); + assert!(Listen > Incoming); +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ConnectionFailure { + kind: ConnectionFailureKind, + addr: Multiaddr, + time: DateTime, + display: String, + debug: String, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ConnectionFailureKind { + DialError, + PeerDisconnected, + WeDisconnected, +} + +fn without_peer(a: &Multiaddr) -> Cow<'_, Multiaddr> { + if matches!(a.iter().last(), Some(Protocol::P2p(_))) { + let mut a = a.clone(); + a.pop(); + Cow::Owned(a) + } else { + Cow::Borrowed(a) + } +} + +impl ConnectionFailure { + pub(crate) fn dial(addr: Multiaddr, error: &DialError) -> Self { + let display = match error { + DialError::ConnectionIo(e) => format!("I/O error: {}", e), + DialError::Transport(e) => { + if e.len() == 1 { + // this should always be the case since we only get here for validation dials + format!("transport error: {}", D(&e[0].1)) + } else { + let mut s = "transport errors:".to_owned(); + for (addr, err) in e { + s.push('\n'); + let _ = write!(&mut s, "{} {}", without_peer(addr), D(err)); + } + s + } + } + x => x.to_string(), + }; + Self { + kind: ConnectionFailureKind::DialError, + addr: without_peer(&addr).into_owned(), + time: Utc::now(), + display, + debug: format!("{:?}", error), + } + } + + pub(crate) fn transport(addr: Multiaddr, error: &TransportError) -> Self { + Self { + kind: ConnectionFailureKind::DialError, + addr: without_peer(&addr).into_owned(), + time: Utc::now(), + display: format!("transport error: {}", D(error)), + debug: format!("{:?}", error), + } + } + + pub(crate) fn us(addr: Multiaddr, display: String, debug: String) -> Self { + Self { + kind: ConnectionFailureKind::WeDisconnected, + addr, + time: Utc::now(), + display, + debug, + } + } + + pub(crate) fn them(addr: Multiaddr, display: String, debug: String) -> Self { + Self { + kind: ConnectionFailureKind::PeerDisconnected, + addr, + time: Utc::now(), + display, + debug, + } + } + + pub fn kind(&self) -> ConnectionFailureKind { + self.kind + } + + pub fn addr(&self) -> &Multiaddr { + &self.addr + } + + pub fn time(&self) -> DateTime { + self.time + } + + pub fn display(&self) -> &str { + self.display.as_str() + } + + pub fn debug(&self) -> &str { + self.debug.as_str() + } +} + +struct D<'a>(&'a TransportError); + +impl<'a> std::fmt::Display for D<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0)?; + if let Some(cause) = self.0.source() { + write!(f, "{}", cause)?; + } + Ok(()) + } +} diff --git a/src/net/peers.rs b/src/net/peers.rs index 003204c..8e3ac22 100644 --- a/src/net/peers.rs +++ b/src/net/peers.rs @@ -1,78 +1,93 @@ +use super::{ + address_handler::IntoAddressHandler, + behaviour::MyHandlerError, + peer_info::{AddressSource, Direction, PeerInfo}, +}; +use crate::{net::peer_info::ConnectionFailure, variable::Writer}; use anyhow::Result; -use fnv::FnvHashMap; -use futures::channel::mpsc; -use futures::stream::Stream; +use chrono::{DateTime, Utc}; +use fnv::{FnvHashMap, FnvHashSet}; +use futures::{ + channel::mpsc::{self, UnboundedSender}, + future::BoxFuture, + stream::{FuturesUnordered, Stream}, + FutureExt, StreamExt, +}; +use futures_timer::Delay; use lazy_static::lazy_static; -use libp2p::core::connection::{ConnectedPoint, ConnectionId, ListenerId}; -use libp2p::identify::IdentifyInfo; -use libp2p::multiaddr::Protocol; -use libp2p::swarm::protocols_handler::DummyProtocolsHandler; -use libp2p::swarm::{DialPeerCondition, NetworkBehaviour, NetworkBehaviourAction, PollParameters}; -use libp2p::{Multiaddr, PeerId}; -use libp2p_blake_streams::Head; -use libp2p_quic::PublicKey; +use libp2p::{ + core::{ + connection::ConnectedPoint, + either::EitherError, + transport::{timeout::TransportTimeoutError, ListenerId}, + UpgradeError, + }, + dns::DnsErr, + identify, + multiaddr::Protocol, + noise::NoiseError, + swarm::{ + derive_prelude::FromSwarm, + dial_opts::{DialOpts, PeerCondition}, + AddressRecord, ConnectionError, DialError, NetworkBehaviour, NetworkBehaviourAction, + PollParameters, + }, + Multiaddr, PeerId, TransportError, +}; use prometheus::{IntCounter, IntGauge, Registry}; -use std::borrow::Cow; -use std::collections::VecDeque; -use std::pin::Pin; -use std::task::{Context, Poll}; -use std::time::Duration; +use std::{ + borrow::Cow, + collections::VecDeque, + convert::TryInto, + io::ErrorKind, + net::IpAddr, + pin::Pin, + task::{Context, Poll}, + time::Duration, +}; #[derive(Clone, Debug, Eq, PartialEq)] pub enum Event { + /// a new listener has been created NewListener(ListenerId), + /// the given listener started listening on this address NewListenAddr(ListenerId, Multiaddr), + /// the given listener stopped listening on this address ExpiredListenAddr(ListenerId, Multiaddr), + /// the given listener experienced an error + ListenerError(ListenerId, String), + /// the given listener was closed ListenerClosed(ListenerId), + /// we received an observed address for ourselves from a peer NewExternalAddr(Multiaddr), + /// an address observed earlier for ourselves has been retired since it was + /// not refreshed ExpiredExternalAddr(Multiaddr), + /// an address was added for the given peer, following a successful dailling + /// attempt Discovered(PeerId), + /// a dialling attempt for the given peer has failed + DialFailure(PeerId, Multiaddr, String), + /// a peer could not be reached by any known address Unreachable(PeerId), + /// a new connection has been opened to the given peer + ConnectionEstablished(PeerId, ConnectedPoint), + /// a connection to the given peer has been closed + // FIXME add termination reason + ConnectionClosed(PeerId, ConnectedPoint), + /// the given peer signaled that its address has changed + AddressChanged(PeerId, ConnectedPoint, ConnectedPoint), + /// we are now connected to the given peer Connected(PeerId), + /// the last connection to the given peer has been closed Disconnected(PeerId), + /// the given peer subscribed to the given gossipsub or broadcast topic Subscribed(PeerId, String), + /// the given peer unsubscribed from the given gossipsub or broadcast topic Unsubscribed(PeerId, String), - NewHead(Head), Bootstrapped, -} - -#[derive(Clone, Debug, Default, Eq, PartialEq)] -pub struct PeerInfo { - protocol_version: Option, - agent_version: Option, - protocols: Vec, - addresses: FnvHashMap, - rtt: Option, -} - -impl PeerInfo { - pub fn protocol_version(&self) -> Option<&str> { - self.protocol_version.as_deref() - } - - pub fn agent_version(&self) -> Option<&str> { - self.agent_version.as_deref() - } - - pub fn protocols(&self) -> impl Iterator + '_ { - self.protocols.iter().map(|s| &**s) - } - - pub fn addresses(&self) -> impl Iterator + '_ { - self.addresses.iter().map(|(addr, source)| (addr, *source)) - } - - pub fn rtt(&self) -> Option { - self.rtt - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum AddressSource { - Mdns, - Kad, - Peer, - User, + /// the peer-info for the given peer has been updated with new information + NewInfo(PeerId), } lazy_static! { @@ -102,6 +117,8 @@ lazy_static! { IntCounter::new("peers_dial_failure", "Number of dial failures.").unwrap(); } +const SIM_OPEN_RETRIES: u8 = 10; + #[inline] pub(crate) fn normalize_addr(addr: &mut Multiaddr, peer: &PeerId) { if let Some(Protocol::P2p(_)) = addr.iter().last() { @@ -121,8 +138,40 @@ fn normalize_addr_ref<'a>(addr: &'a Multiaddr, peer: &PeerId) -> Cow<'a, Multiad } } +fn without_peer_id(addr: &Multiaddr) -> Multiaddr { + let mut addr = addr.clone(); + if let Some(Protocol::P2p(_)) = addr.iter().last() { + addr.pop(); + } + addr +} + +fn normalize_connected_point( + cp: &ConnectedPoint, + local: &PeerId, + remote: &PeerId, +) -> ConnectedPoint { + match cp { + ConnectedPoint::Dialer { + address, + role_override, + } => ConnectedPoint::Dialer { + address: normalize_addr_ref(address, remote).into_owned(), + role_override: *role_override, + }, + ConnectedPoint::Listener { + local_addr, + send_back_addr, + } => ConnectedPoint::Listener { + local_addr: normalize_addr_ref(local_addr, local).into_owned(), + send_back_addr: normalize_addr_ref(send_back_addr, remote).into_owned(), + }, + } +} + trait MultiaddrExt { fn is_loopback(&self) -> bool; + fn peer_id(&self) -> Option; } impl MultiaddrExt for Multiaddr { @@ -134,50 +183,56 @@ impl MultiaddrExt for Multiaddr { } true } + fn peer_id(&self) -> Option { + match self.iter().last() { + Some(Protocol::P2p(p)) => p.try_into().ok(), + _ => None, + } + } } #[derive(Debug)] pub struct AddressBook { + port_reuse: bool, enable_loopback: bool, - prune_addresses: bool, - local_node_name: String, + keep_alive: bool, local_peer_id: PeerId, - local_public_key: PublicKey, - peers: FnvHashMap, - connections: FnvHashMap, + listeners: Writer>, + peers: Writer>, + external: Writer>, + refresh_external: bool, event_stream: Vec>, - actions: VecDeque>, + pub(crate) actions: VecDeque>, + deferred: FuturesUnordered< + BoxFuture<'static, NetworkBehaviourAction>, + >, } impl AddressBook { pub fn new( local_peer_id: PeerId, - local_node_name: String, - local_public_key: PublicKey, + port_reuse: bool, enable_loopback: bool, - prune_addresses: bool, + keep_alive: bool, + listeners: Writer>, + peers: Writer>, + external: Writer>, ) -> Self { Self { + port_reuse, enable_loopback, - prune_addresses, - local_node_name, + keep_alive, local_peer_id, - local_public_key, - peers: Default::default(), - connections: Default::default(), + listeners, + peers, + external, + refresh_external: true, event_stream: Default::default(), actions: Default::default(), + deferred: Default::default(), } } - pub fn local_public_key(&self) -> &PublicKey { - &self.local_public_key - } - - pub fn local_node_name(&self) -> &str { - &self.local_node_name - } - pub fn local_peer_id(&self) -> &PeerId { &self.local_peer_id } @@ -187,10 +242,36 @@ impl AddressBook { tracing::error!("attempting to dial self"); return; } - tracing::trace!("dialing {}", peer); - self.actions.push_back(NetworkBehaviourAction::DialPeer { - peer_id: *peer, - condition: DialPeerCondition::Disconnected, + tracing::debug!("request dialing {}", peer); + let handler = self.new_handler(); + self.actions.push_back(NetworkBehaviourAction::Dial { + opts: DialOpts::peer_id(*peer).build(), + handler, + }); + } + + pub fn dial_address(&mut self, peer: &PeerId, addr: Multiaddr) { + if peer == self.local_peer_id() { + tracing::error!("attempting to dial self"); + return; + } + let target = normalize_addr_ref(&addr, peer); + let mut peers = self.peers.write(); + let info = peers.entry(*peer).or_default(); + if info.connections.contains_key(target.as_ref()) { + tracing::debug!(peer = %peer, addr = %&addr, + "skipping dial since already connected"); + return; + } + drop(peers); + tracing::debug!(peer = %peer, addr = %&addr, "request dialing"); + let handler = IntoAddressHandler( + Some((target.into_owned(), SIM_OPEN_RETRIES + 1)), + self.keep_alive, + ); + self.actions.push_back(NetworkBehaviourAction::Dial { + opts: DialOpts::peer_id(*peer).addresses(vec![addr]).build(), + handler, }); } @@ -201,61 +282,284 @@ impl AddressBook { if !self.enable_loopback && address.is_loopback() { return; } - let discovered = !self.peers.contains_key(peer); - let info = self.peers.entry(*peer).or_default(); - normalize_addr(&mut address, peer); - #[allow(clippy::map_entry)] - if !info.addresses.contains_key(&address) { - tracing::trace!("adding address {} from {:?}", address, source); - info.addresses.insert(address, source); - } - if discovered { - self.notify(Event::Discovered(*peer)); + let discovered = self + .peers + .read() + .get(peer) + .filter(|info| info.confirmed_addresses().next().is_some()) + .is_none(); + let addr_full = match normalize_addr_ref(&address, peer) { + Cow::Borrowed(a) => { + let ret = a.clone(); + address.pop(); + ret + } + Cow::Owned(a) => a, + }; + let is_listener = self.listeners.read().contains(&address); + if !is_listener { + // addr_full is with peerId, address is guaranteed without + tracing::debug!(peer = %peer, "adding address {} from {:?}", address, source); + let mut peers = self.peers.write(); + let info = peers.entry(*peer).or_default(); + let result = info.ingest_address(addr_full.clone(), source) + && !info.connections.contains_key(&addr_full); + drop(peers); + if result { + self.actions.push_back(NetworkBehaviourAction::Dial { + opts: DialOpts::peer_id(*peer) + .condition(PeerCondition::Always) + .addresses(vec![address]) + .build(), + handler: IntoAddressHandler(Some((addr_full, SIM_OPEN_RETRIES + 1)), false), + }); + } + if discovered && source.is_confirmed() { + self.notify(Event::Discovered(*peer)); + } + self.notify(Event::NewInfo(*peer)); + } else { + tracing::debug!(peer = %peer, addr = %&address, + "ignoring peer address from unreachable scope"); } } pub fn remove_address(&mut self, peer: &PeerId, address: &Multiaddr) { - let address = normalize_addr_ref(address, peer); - if let Some(info) = self.peers.get_mut(peer) { + if let Some(info) = self.peers.write().get_mut(peer) { + let address = normalize_addr_ref(address, peer); tracing::trace!("removing address {}", address); info.addresses.remove(&address); } } - pub fn peers(&self) -> impl Iterator + '_ { - self.peers.keys() + pub fn prune_peers(&mut self, min_age: Duration) { + let _span = tracing::trace_span!("prune_peers").entered(); + let now = Utc::now(); + let mut remove = Vec::new(); + 'l: for (peer, info) in self.peers.read().iter() { + if info.connections().next().is_some() { + tracing::trace!(peer = %peer, "keeping connected"); + continue; + } + if let Some(f) = info.recent_failures().next() { + // do not remove if most recent failure is younger than min_age + if diff_time(f.time(), now) < min_age { + tracing::trace!(peer = %peer, "keeping recently failed"); + continue; + } + } + for (a, s, dt) in info.addresses() { + if s.is_confirmed() { + // keep those that have confirmed addresses + tracing::trace!(peer = %peer, addr = %a, "keeping confirmed"); + continue 'l; + } + if s.is_to_probe() && diff_time(dt, now) < min_age.max(Duration::from_secs(10)) { + // keep those which we are presumably trying to probe + tracing::trace!(peer = %peer, addr = %a, "keeping probed"); + continue 'l; + } + } + tracing::trace!(peer = %peer, "pruning"); + remove.push(*peer); + } + for peer in remove { + self.peers.write().remove(&peer); + self.notify(Event::NewInfo(peer)); + } } - pub fn connections(&self) -> impl Iterator + '_ { - self.connections.iter().map(|(peer, addr)| (peer, addr)) + pub fn connection_closed( + &mut self, + peer: PeerId, + conn: ConnectedPoint, + num_established: u32, + error: Option>, + ) { + use libp2p::core::either::EitherError::*; + use ConnectionError::Handler as ConnHandler; + + let conn = normalize_connected_point(&conn, &self.local_peer_id, &peer); + let addr = conn.get_remote_address(); + + let debug = format!("{:?}", error); + let (reason, peer_closed) = match error { + Some(ConnHandler(A(A(A(A(A(A(A(e))))))))) => void::unreachable(e), + Some(ConnHandler(A(A(A(A(A(A(B(e))))))))) => { + (format!("Kademlia I/O error: {}", e), false) + } + Some(ConnHandler(A(A(A(A(A(B(e)))))))) => void::unreachable(e), + Some(ConnHandler(A(A(A(A(B(e))))))) => (format!("Ping failure: {}", e), false), + Some(ConnHandler(A(A(A(B(e)))))) => (format!("Identify I/O error: {}", e), false), + Some(ConnHandler(A(A(B(e))))) => (format!("Bitswap error: {}", e), false), + Some(ConnHandler(A(B(e)))) => (format!("Gossipsub error: {}", e), false), + Some(ConnHandler(B(e))) => (format!("Broadcast error: {}", e), false), + Some(ConnectionError::IO(e)) => (format!("connection I/O error: {}", e), true), + Some(ConnectionError::KeepAliveTimeout) => { + ("we closed due to missing keepalive".to_owned(), false) + } + None => ("we closed".to_owned(), false), + }; + + tracing::debug!( + addr = display(&addr), + outbound = conn.is_dialer(), + conn_left = %num_established, + "connection closed ({})", + reason + ); + + let mut peers = self.peers.write(); + let entry = peers.entry(peer).or_default(); + entry.connections.remove(addr); + let addr_no_peer = without_peer_id(addr); + let failure = if peer_closed { + ConnectionFailure::them(addr_no_peer, reason, debug) + } else { + ConnectionFailure::us(addr_no_peer, reason, debug) + }; + entry.push_failure(addr, failure, false); + drop(peers); + + self.notify(Event::ConnectionClosed(peer, conn)); + if num_established == 0 { + self.notify(Event::Disconnected(peer)); + } + self.notify(Event::NewInfo(peer)); } - pub fn is_connected(&self, peer: &PeerId) -> bool { - self.connections.contains_key(peer) || peer == self.local_peer_id() + #[cfg(test)] + pub fn peers(&self) -> Vec { + self.peers.read().keys().copied().collect() } - pub fn info(&self, peer_id: &PeerId) -> Option<&PeerInfo> { - self.peers.get(peer_id) + #[cfg(test)] + pub fn info(&self, peer_id: &PeerId) -> Option { + self.peers.read().get(peer_id).cloned() } pub fn set_rtt(&mut self, peer_id: &PeerId, rtt: Option) { - if let Some(info) = self.peers.get_mut(peer_id) { - info.rtt = rtt; + let mut peers = self.peers.write(); + if let Some(info) = peers.get_mut(peer_id) { + info.set_rtt(rtt); + drop(peers); + self.notify(Event::NewInfo(*peer_id)); } } - pub fn set_info(&mut self, peer_id: &PeerId, identify: IdentifyInfo) { - if let Some(info) = self.peers.get_mut(peer_id) { + pub fn set_info(&mut self, peer_id: &PeerId, identify: identify::Info) { + let _span = tracing::trace_span!("set_info", peer = %peer_id).entered(); + let mut peers = self.peers.write(); + if let Some(info) = peers.get_mut(peer_id) { info.protocol_version = Some(identify.protocol_version); info.agent_version = Some(identify.agent_version); info.protocols = identify.protocols; + info.listeners = identify.listen_addrs; + + let listen_port = info + .listeners + .iter() + .filter(|a| { + // discount the addresses to which we are currently connected: + // if they are directly reachable then the address will be found in any case, + // and if they are NATed addresses then they likely got there by our own + // observation sent via Identify + !info + .connections + .contains_key(normalize_addr_ref(a, peer_id).as_ref()) + }) + .filter_map(ip_port) + .collect::>(); + tracing::trace!(lp = ?&listen_port); + + // collect all advertised listen ports (which includes actual listeners as well + // as observed addresses, which may be NATed) so that we can at + // least try to guess a reasonable port where the NAT may have a + // hole configured + let common_port = listen_port + .iter() + .map(|(_a, p)| *p) + .collect::>(); + tracing::trace!(cp = ?&common_port); + + // in the absence of port_reuse or the presence of NAT the remote port on an + // incoming connection won’t be reachable for us, so attempt a + // translation that is then validated by dailling the resulting + // address + let mut translated = FnvHashSet::default(); + for addr in info.addresses_to_translate() { + if let Some((ip, _p)) = ip_port(addr) { + let mut added = false; + for (_a, lp) in listen_port.iter().filter(|(a, _p)| *a == ip) { + tracing::trace!("adding lp {} -> {}", addr, lp); + translated.insert(addr.replace(1, |_| Some(Protocol::Tcp(*lp))).unwrap()); + added = true; + } + if !added { + for cp in &common_port { + tracing::trace!("adding cp {} -> {}", addr, cp); + translated + .insert(addr.replace(1, |_| Some(Protocol::Tcp(*cp))).unwrap()); + added = true; + } + } + if !added { + // no idea for a translation, so add it for validation + tracing::trace!("adding raw {}", addr); + translated.insert(addr.clone()); + } + } else { + tracing::trace!("ignoring {}", addr); + } + } + + info.addresses.retain(|_a, (s, _dt)| !s.is_to_translate()); + + let loopback = self.enable_loopback; + translated.extend( + info.listeners + .iter() + .filter(|a| loopback || !a.is_loopback()) + .map(|a| normalize_addr_ref(a, peer_id).into_owned()), + ); + + for addr in translated { + let mut tcp = addr.clone(); + tcp.pop(); + if self.listeners.read().contains(&tcp) { + // diallling our own listener somehow breaks the Swarm + tracing::trace!("not adding self-addr {}", tcp); + continue; + } + tracing::debug!(peer = %peer_id, addr = %&tcp, + "adding address derived from Identify"); + if info.ingest_address(addr.clone(), AddressSource::Listen) { + // no point trying to dial if we’re already connected and port_reuse==true since + // a second connection is fundamentally impossible in this + // case + if self.port_reuse && info.connections.contains_key(&addr) { + // this will offer the address as soon as the Swarm asks for one for this + // peer, leading to a dial attempt that will answer + // the question + info.ingest_address(addr, AddressSource::Candidate); + } else { + self.actions.push_back(NetworkBehaviourAction::Dial { + opts: DialOpts::peer_id(*peer_id) + .condition(PeerCondition::Always) + .addresses(vec![tcp]) + .build(), + handler: IntoAddressHandler(Some((addr, 4)), false), + }) + } + } + } + drop(peers); + self.notify(Event::NewInfo(*peer_id)); } } - pub fn swarm_events(&mut self) -> SwarmEvents { - let (tx, rx) = mpsc::unbounded(); + pub fn swarm_events(&mut self, tx: UnboundedSender) { self.event_stream.push(tx); - SwarmEvents(rx) } pub fn notify(&mut self, event: Event) { @@ -264,22 +568,165 @@ impl AddressBook { .retain(|tx| tx.unbounded_send(event.clone()).is_ok()); } - pub fn register_metrics(&self, registry: &Registry) -> Result<()> { - registry.register(Box::new(LISTENERS.clone()))?; - registry.register(Box::new(LISTEN_ADDRS.clone()))?; - registry.register(Box::new(EXTERNAL_ADDRS.clone()))?; - registry.register(Box::new(DISCOVERED.clone()))?; - registry.register(Box::new(CONNECTED.clone()))?; - registry.register(Box::new(CONNECTIONS.clone()))?; - registry.register(Box::new(LISTENER_ERROR.clone()))?; - registry.register(Box::new(ADDRESS_REACH_FAILURE.clone()))?; - registry.register(Box::new(DIAL_FAILURE.clone()))?; - Ok(()) + pub(crate) fn dial_failure( + &mut self, + handler: IntoAddressHandler, + peer_id: Option, + error: &DialError, + ) { + let peer_id = if let Some(peer_id) = handler.peer_id().or(peer_id) { + peer_id + } else { + tracing::debug!("dial failure without peer ID: {}", error); + return; + }; + let mut peer = self.peers.write(); + if let Some(info) = peer.get_mut(&peer_id) { + if let IntoAddressHandler(Some((addr, retries)), keep_alive) = handler { + // this was our own validation dial + let transport = matches!(error, DialError::Transport(_)); + let wrong_peer = matches!(error, DialError::WrongPeerId { .. }); + let probe_result = + transport || wrong_peer || matches!(error, DialError::ConnectionIo(_)); + let failure = ConnectionFailure::dial(without_peer_id(&addr), error); + let is_sim_open = if let DialError::Transport(v) = error { + v.iter().any(|(_, e)| is_sim_open(e)) + } else { + false + }; + + let error = error.to_string(); + tracing::debug!(addr = %&addr, error = %&error, active = probe_result, + "validation dial failure"); + info.push_failure(&addr, failure, probe_result); + if wrong_peer { + // we know who we dialled and we know someone else answered => kill the address + // regardless of whether it was confirmed + info.addresses.remove(&addr); + } + if is_sim_open && retries > 0 { + // TCP simultaneous open leads to both sides being initiator in the Noise + // handshake, which yields this particular error + if retries == SIM_OPEN_RETRIES + 1 { + tracing::debug!("scheduling redial after presumed TCP simultaneous open"); + } + let action = NetworkBehaviourAction::Dial { + opts: DialOpts::peer_id(peer_id) + .addresses(vec![addr.clone()]) + .build(), + handler: IntoAddressHandler(Some((addr.clone(), retries - 1)), keep_alive), + }; + self.deferred + .push(Delay::new(self.redial_delay()).map(move |_| action).boxed()); + } + drop(peer); + + self.notify(Event::DialFailure(peer_id, addr, error)); + self.notify(Event::NewInfo(peer_id)); + } else if let DialError::Transport(v) = error { + let mut events = Vec::with_capacity(v.len()); + let mut deferred = Vec::new(); + for (addr, error) in v { + let is_sim_open = is_sim_open(error); + let failure = ConnectionFailure::transport(without_peer_id(addr), error); + let error = format!("{:?}", error); + tracing::debug!(addr = %&addr, error = %&error, "non-validation dial failure"); + info.push_failure(normalize_addr_ref(addr, &peer_id).as_ref(), failure, true); + // TCP simultaneous open leads to both sides being initiator in the Noise + // handshake, which yields this particular error + if is_sim_open { + tracing::debug!("scheduling redial after presumed TCP simultaneous open"); + deferred.push(NetworkBehaviourAction::Dial { + opts: DialOpts::peer_id(peer_id) + .addresses(vec![addr.clone()]) + .build(), + handler: IntoAddressHandler( + Some((addr.clone(), SIM_OPEN_RETRIES)), + self.keep_alive, + ), + }) + } + events.push(Event::DialFailure(peer_id, addr.clone(), error)); + } + drop(peer); + for event in events { + self.notify(event); + } + if deferred.is_empty() { + self.notify(Event::Unreachable(peer_id)); + } + for action in deferred { + self.deferred + .push(Delay::new(self.redial_delay()).map(move |_| action).boxed()); + } + self.notify(Event::NewInfo(peer_id)); + } else if let DialError::DialPeerConditionFalse(d) = error { + tracing::trace!(peer = %peer_id, cond = ?d, "dial condition not satisfied"); + } else { + drop(peer); + tracing::debug!(peer = %peer_id, error = %error, "dial failure"); + if !matches!(error, DialError::Banned | DialError::LocalPeerId) { + self.notify(Event::Unreachable(peer_id)); + } + } + } else { + tracing::debug!(peer = %peer_id, error = %error, "dial failure for unknown peer"); + } + } + + fn redial_delay(&self) -> Duration { + Duration::from_secs(1) * rand::random::() / u32::MAX + + if self.port_reuse { + Duration::from_secs(1) + } else { + Duration::ZERO + } } } +pub fn register_metrics(registry: &Registry) -> Result<()> { + registry.register(Box::new(LISTENERS.clone()))?; + registry.register(Box::new(LISTEN_ADDRS.clone()))?; + registry.register(Box::new(EXTERNAL_ADDRS.clone()))?; + registry.register(Box::new(DISCOVERED.clone()))?; + registry.register(Box::new(CONNECTED.clone()))?; + registry.register(Box::new(CONNECTIONS.clone()))?; + registry.register(Box::new(LISTENER_ERROR.clone()))?; + registry.register(Box::new(ADDRESS_REACH_FAILURE.clone()))?; + registry.register(Box::new(DIAL_FAILURE.clone()))?; + Ok(()) +} + +fn ip_port(m: &Multiaddr) -> Option<(IpAddr, u16)> { + let mut iter = m.iter(); + let addr = match iter.next()? { + Protocol::Ip4(ip) => IpAddr::V4(ip), + Protocol::Ip6(ip) => IpAddr::V6(ip), + _ => return None, + }; + let port = match iter.next()? { + Protocol::Tcp(p) => p, + _ => return None, + }; + Some((addr, port)) +} + +fn diff_time(former: DateTime, latter: DateTime) -> Duration { + latter + .signed_duration_since(former) + .to_std() + .unwrap_or(Duration::ZERO) +} + +#[derive(Debug)] pub struct SwarmEvents(mpsc::UnboundedReceiver); +impl SwarmEvents { + pub fn new(channel: mpsc::UnboundedReceiver) -> Self { + Self(channel) + } +} + impl Stream for SwarmEvents { type Item = Event; @@ -289,228 +736,178 @@ impl Stream for SwarmEvents { } impl NetworkBehaviour for AddressBook { - type ProtocolsHandler = DummyProtocolsHandler; + type ConnectionHandler = IntoAddressHandler; type OutEvent = void::Void; - fn new_handler(&mut self) -> Self::ProtocolsHandler { - Default::default() + fn new_handler(&mut self) -> Self::ConnectionHandler { + IntoAddressHandler(None, self.keep_alive) } fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec { - if let Some(info) = self.peers.get(peer_id) { - info.addresses().map(|(addr, _)| addr.clone()).collect() + if let Some(info) = self.peers.read().get(peer_id) { + info.confirmed_addresses().cloned().collect() } else { vec![] } } - fn inject_event(&mut self, _peer_id: PeerId, _connection: ConnectionId, _event: void::Void) {} - fn poll( &mut self, - _cx: &mut Context, - _params: &mut impl PollParameters, - ) -> Poll> { + cx: &mut Context, + params: &mut impl PollParameters, + ) -> Poll> { + if self.refresh_external { + self.refresh_external = false; + *self.external.write() = params.external_addresses().collect(); + } if let Some(action) = self.actions.pop_front() { Poll::Ready(action) + } else if !self.deferred.is_empty() { + self.deferred.poll_next_unpin(cx).map(|p| p.unwrap()) } else { Poll::Pending } } - fn inject_connected(&mut self, peer_id: &PeerId) { - tracing::trace!("connected to {}", peer_id); - CONNECTED.inc(); - self.notify(Event::Connected(*peer_id)); - } - - fn inject_disconnected(&mut self, peer_id: &PeerId) { - tracing::trace!("disconnected from {}", peer_id); - CONNECTED.dec(); - self.notify(Event::Disconnected(*peer_id)); - } - - fn inject_connection_established( - &mut self, - peer_id: &PeerId, - _: &ConnectionId, - conn: &ConnectedPoint, - ) { - let mut address = conn.get_remote_address().clone(); - normalize_addr(&mut address, peer_id); - self.add_address(peer_id, address.clone(), AddressSource::Peer); - self.connections.insert(*peer_id, address); - } - - fn inject_address_change( - &mut self, - peer_id: &PeerId, - _: &ConnectionId, - _old: &ConnectedPoint, - new: &ConnectedPoint, - ) { - let mut new = new.get_remote_address().clone(); - normalize_addr(&mut new, peer_id); - self.add_address(peer_id, new.clone(), AddressSource::Peer); - self.connections.insert(*peer_id, new); - } - - fn inject_connection_closed( - &mut self, - peer_id: &PeerId, - _: &ConnectionId, - _conn: &ConnectedPoint, - ) { - self.connections.remove(peer_id); - } - - fn inject_addr_reach_failure( - &mut self, - peer_id: Option<&PeerId>, - addr: &Multiaddr, - error: &dyn std::error::Error, - ) { - if let Some(peer_id) = peer_id { - if self.is_connected(peer_id) { - return; + fn on_swarm_event(&mut self, event: FromSwarm) { + match event { + FromSwarm::ConnectionEstablished(c) => { + let conn = normalize_connected_point(c.endpoint, &self.local_peer_id, &c.peer_id); + let address = conn.get_remote_address(); + tracing::debug!( + addr = %address, + out = conn.is_dialer(), + "connection established" + ); + let src = if conn.is_dialer() { + AddressSource::Dial + } else { + AddressSource::Incoming + }; + self.add_address(&c.peer_id, address.clone(), src); + self.peers + .write() + .entry(c.peer_id) + .or_default() + .connections + .insert(address.clone(), (Utc::now(), Direction::from(&conn))); + if c.other_established == 0 { + self.notify(Event::Connected(c.peer_id)); + } + self.notify(Event::ConnectionEstablished(c.peer_id, conn)); } - tracing::trace!("address reach failure {}", error); - ADDRESS_REACH_FAILURE.inc(); - if self.prune_addresses { - self.remove_address(peer_id, addr); + FromSwarm::ConnectionClosed(_) => { + // handled via external SwarmEvent since that is the only way to get the reason } - } - } - - fn inject_dial_failure(&mut self, peer_id: &PeerId) { - if self.prune_addresses { - // If an address was added after the peer was dialed retry dialing the - // peer. - if let Some(peer) = self.peers.get(peer_id) { - if !peer.addresses.is_empty() { - tracing::trace!("redialing with new addresses"); - self.dial(peer_id); - return; + FromSwarm::AddressChange(a) => { + let old = normalize_connected_point(a.old, &self.local_peer_id, &a.peer_id); + let new = normalize_connected_point(a.new, &self.local_peer_id, &a.peer_id); + let old_addr = old.get_remote_address(); + let new_addr = new.get_remote_address(); + tracing::debug!( + old = %old.get_remote_address(), + new = %new_addr, + out = new.is_dialer(), + "address changed" + ); + let src = if new.is_dialer() { + AddressSource::Dial + } else { + AddressSource::Incoming + }; + self.add_address(&a.peer_id, new_addr.clone(), src); + let mut peers = self.peers.write(); + let entry = peers.entry(a.peer_id).or_default(); + entry.connections.remove(old_addr); + entry + .connections + .insert(new_addr.clone(), (Utc::now(), Direction::from(&new))); + drop(peers); + + self.notify(Event::AddressChanged(a.peer_id, old, new)); + } + FromSwarm::DialFailure(d) => self.dial_failure(d.handler, d.peer_id, d.error), + FromSwarm::ListenFailure(_) => {} + FromSwarm::NewListener(l) => { + tracing::trace!("listener {:?}: created", l.listener_id); + LISTENERS.inc(); + self.notify(Event::NewListener(l.listener_id)); + } + FromSwarm::NewListenAddr(l) => { + tracing::trace!("listener {:?}: new listen addr {}", l.listener_id, l.addr); + if self.listeners.write().insert(l.addr.clone()) { + LISTEN_ADDRS.inc(); } + self.notify(Event::NewListenAddr(l.listener_id, l.addr.clone())); } - } - tracing::trace!("dial failure {}", peer_id); - DIAL_FAILURE.inc(); - if self.peers.contains_key(peer_id) { - DISCOVERED.dec(); - self.notify(Event::Unreachable(*peer_id)); - if self.prune_addresses { - self.peers.remove(peer_id); + FromSwarm::ExpiredListenAddr(l) => { + tracing::trace!( + "listener {:?}: expired listen addr {}", + l.listener_id, + l.addr + ); + if self.listeners.write().remove(l.addr) { + LISTEN_ADDRS.dec(); + } + self.notify(Event::ExpiredListenAddr(l.listener_id, l.addr.clone())); + } + FromSwarm::ListenerError(l) => { + let err = format!("{:#}", l.err); + tracing::trace!("listener {:?}: listener error {}", l.listener_id, err); + LISTENER_ERROR.inc(); + self.notify(Event::ListenerError(l.listener_id, err)); + } + FromSwarm::ListenerClosed(l) => { + tracing::trace!( + "listener {:?}: closed for reason {:?}", + l.listener_id, + l.reason + ); + LISTENERS.dec(); + self.notify(Event::ListenerClosed(l.listener_id)); + } + FromSwarm::NewExternalAddr(a) => { + self.refresh_external = true; + let mut addr = a.addr.clone(); + normalize_addr(&mut addr, self.local_peer_id()); + tracing::trace!("new external addr {}", addr); + EXTERNAL_ADDRS.inc(); + self.notify(Event::NewExternalAddr(addr)); + } + FromSwarm::ExpiredExternalAddr(a) => { + self.refresh_external = true; + let mut addr = a.addr.clone(); + normalize_addr(&mut addr, self.local_peer_id()); + tracing::trace!("expired external addr {}", addr); + EXTERNAL_ADDRS.dec(); + self.notify(Event::ExpiredExternalAddr(addr)); } } } - - fn inject_new_listener(&mut self, id: ListenerId) { - tracing::trace!("listener {:?}: created", id); - LISTENERS.inc(); - self.notify(Event::NewListener(id)); - } - - fn inject_new_listen_addr(&mut self, id: ListenerId, addr: &Multiaddr) { - tracing::trace!("listener {:?}: new listen addr {}", id, addr); - LISTEN_ADDRS.inc(); - self.notify(Event::NewListenAddr(id, addr.clone())); - } - - fn inject_expired_listen_addr(&mut self, id: ListenerId, addr: &Multiaddr) { - tracing::trace!("listener {:?}: expired listen addr {}", id, addr); - LISTEN_ADDRS.dec(); - self.notify(Event::ExpiredListenAddr(id, addr.clone())); - } - - fn inject_listener_error(&mut self, id: ListenerId, err: &(dyn std::error::Error + 'static)) { - tracing::trace!("listener {:?}: listener error {}", id, err); - LISTENER_ERROR.inc(); - } - - fn inject_listener_closed(&mut self, id: ListenerId, reason: Result<(), &std::io::Error>) { - tracing::trace!("listener {:?}: closed for reason {:?}", id, reason); - LISTENERS.dec(); - self.notify(Event::ListenerClosed(id)); - } - - fn inject_new_external_addr(&mut self, addr: &Multiaddr) { - tracing::trace!("new external addr {}", addr); - EXTERNAL_ADDRS.inc(); - self.notify(Event::NewExternalAddr(addr.clone())); - } - - fn inject_expired_external_addr(&mut self, addr: &Multiaddr) { - tracing::trace!("expired external addr {}", addr); - EXTERNAL_ADDRS.dec(); - self.notify(Event::ExpiredExternalAddr(addr.clone())); - } } -#[cfg(test)] -mod tests { - use super::*; - use crate::generate_keypair; - use futures::stream::StreamExt; - - #[async_std::test] - async fn test_dial_basic() { - let mut book = AddressBook::new( - PeerId::random(), - "".into(), - generate_keypair().public, - false, - true, - ); - let mut stream = book.swarm_events(); - let peer_a = PeerId::random(); - let addr_1: Multiaddr = "/ip4/1.1.1.1/tcp/3333".parse().unwrap(); - let mut addr_1_2 = addr_1.clone(); - addr_1_2.push(Protocol::P2p(peer_a.into())); - let addr_2: Multiaddr = "/ip4/2.2.2.2/tcp/3333".parse().unwrap(); - let error = std::io::Error::new(std::io::ErrorKind::Other, ""); - book.add_address(&peer_a, addr_1.clone(), AddressSource::Mdns); - book.add_address(&peer_a, addr_1_2, AddressSource::User); - book.add_address(&peer_a, addr_2.clone(), AddressSource::Peer); - assert_eq!(stream.next().await, Some(Event::Discovered(peer_a))); - let peers = book.peers().collect::>(); - assert_eq!(peers, vec![&peer_a]); - book.inject_addr_reach_failure(Some(&peer_a), &addr_1, &error); - book.inject_addr_reach_failure(Some(&peer_a), &addr_2, &error); - book.inject_dial_failure(&peer_a); - assert_eq!(stream.next().await, Some(Event::Unreachable(peer_a))); - #[allow(clippy::needless_collect)] - let peers = book.peers().collect::>(); - assert!(peers.is_empty()); - } - - #[async_std::test] - async fn test_dial_with_added_addrs() { - let mut book = AddressBook::new( - PeerId::random(), - "".into(), - generate_keypair().public, - false, - true, - ); - let mut stream = book.swarm_events(); - let peer_a = PeerId::random(); - let addr_1: Multiaddr = "/ip4/1.1.1.1/tcp/3333".parse().unwrap(); - let addr_2: Multiaddr = "/ip4/2.2.2.2/tcp/3333".parse().unwrap(); - let error = std::io::Error::new(std::io::ErrorKind::Other, ""); - book.add_address(&peer_a, addr_1.clone(), AddressSource::Mdns); - assert_eq!(stream.next().await, Some(Event::Discovered(peer_a))); - book.add_address(&peer_a, addr_2.clone(), AddressSource::Peer); - book.inject_addr_reach_failure(Some(&peer_a), &addr_1, &error); - book.inject_dial_failure(&peer_a); - // book.poll - let peers = book.peers().collect::>(); - assert_eq!(peers, vec![&peer_a]); - book.inject_addr_reach_failure(Some(&peer_a), &addr_2, &error); - book.inject_dial_failure(&peer_a); - assert_eq!(stream.next().await, Some(Event::Unreachable(peer_a))); - #[allow(clippy::needless_collect)] - let peers = book.peers().collect::>(); - assert!(peers.is_empty()); +fn is_sim_open(error: &TransportError) -> bool { + match error { + libp2p::TransportError::MultiaddrNotSupported(_x) => false, + libp2p::TransportError::Other(err) => { + let err = err + .get_ref() + .and_then(|e| e.downcast_ref::>()); + if let Some(DnsErr::Transport(err)) = err { + let err = err + .get_ref() + .and_then(|e| e.downcast_ref::()); + if let Some(TransportTimeoutError::Other(EitherError::A(EitherError::B( + UpgradeError::Apply(NoiseError::Io(err)), + )))) = err + { + err.kind() == ErrorKind::InvalidData + } else { + false + } + } else { + false + } + } } } diff --git a/src/net/tests.rs b/src/net/tests.rs new file mode 100644 index 0000000..abbb611 --- /dev/null +++ b/src/net/tests.rs @@ -0,0 +1,593 @@ +use super::{address_handler::IntoAddressHandler, *}; +use crate::net::{peer_info::ConnectionFailureKind, peers::AddressBook}; +use async_executor::LocalExecutor; +use futures::{future::ready, stream::StreamExt}; +use libp2p::{ + core::{connection::ConnectionId, ConnectedPoint, Endpoint}, + identify, + identity::ed25519::Keypair, + multiaddr::Protocol, + swarm::{ + derive_prelude::{ConnectionEstablished as CE, FromSwarm}, + DialError, NetworkBehaviour, NetworkBehaviourAction, + }, + TransportError, +}; +use std::{cell::RefCell, collections::HashMap, io::ErrorKind}; +use tracing_subscriber::EnvFilter; +use Event::*; + +struct Events<'a> { + events: &'a RefCell>, + swarm: LocalExecutor<'a>, +} + +impl<'a> Events<'a> { + fn new(s: SwarmEvents, events: &'a RefCell>) -> Self { + let swarm = LocalExecutor::new(); + swarm + .spawn(s.for_each(move |e| { + events.borrow_mut().push(e); + ready(()) + })) + .detach(); + Self { events, swarm } + } + + pub fn next(&self) -> Vec { + while self.swarm.try_tick() {} + std::mem::take(&mut *self.events.borrow_mut()) + } +} + +#[test] +fn test_dial_basic() { + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::default()) + .try_init() + .ok(); + + let mut book = AddressBook::new( + PeerId::random(), + false, + false, + false, + Writer::new(HashSet::default()), + Writer::new(HashMap::default()), + Writer::new(vec![]), + ); + + let events = Default::default(); + let (tx, rx) = mpsc::unbounded(); + book.swarm_events(tx); + let events = Events::new(SwarmEvents::new(rx), &events); + + let peer_a = PeerId::random(); + let addr_1: Multiaddr = "/ip4/1.1.1.1/tcp/3333".parse().unwrap(); + let addr_1p = addr_1.clone().with(Protocol::P2p(peer_a.into())); + let addr_2: Multiaddr = "/ip4/2.2.2.2/tcp/3333".parse().unwrap(); + let addr_2p = addr_2.clone().with(Protocol::P2p(peer_a.into())); + let error = std::io::Error::new(ErrorKind::Other, "my error"); + book.add_address(&peer_a, addr_1.clone(), AddressSource::Mdns); + assert_eq!(events.next(), vec!(NewInfo(peer_a))); + assert_eq!(dials(&mut book), vec!(Dial::A(addr_1p.clone()))); + book.add_address(&peer_a, addr_1p.clone(), AddressSource::User); + assert_eq!(events.next(), vec!(Discovered(peer_a), NewInfo(peer_a))); + book.add_address(&peer_a, addr_2.clone(), AddressSource::Incoming); + assert_eq!(events.next(), vec!(NewInfo(peer_a))); + let peers = book.peers(); + assert_eq!(peers, vec![peer_a]); + book.dial_failure( + IntoAddressHandler(Some((addr_1.clone(), 3)), false), + Some(peer_a), + &DialError::ConnectionIo(error), + ); + assert_eq!( + events.next(), + vec!( + DialFailure( + peer_a, + addr_1, + "Dial error: An I/O error occurred on the connection: \ + Custom { kind: Other, error: \"my error\" }." + .to_owned() + ), + NewInfo(peer_a) + ) + ); + let error = std::io::Error::new(ErrorKind::Other, "my other error"); + book.dial_failure( + IntoAddressHandler(None, false), + Some(peer_a), + &DialError::Transport(vec![(addr_2.clone(), TransportError::Other(error))]), + ); + assert_eq!( + events.next(), + vec!( + DialFailure( + peer_a, + addr_2.clone(), + "Other(Custom { kind: Other, error: \"my other error\" })".to_owned() + ), + Unreachable(peer_a), + NewInfo(peer_a) + ) + ); + assert_eq!(book.peers().into_iter().next(), Some(peer_a)); + let failure = book + .info(&peer_a) + .unwrap() + .recent_failures() + .next() + .cloned() + .unwrap(); + assert_eq!(failure.kind(), ConnectionFailureKind::DialError); + assert_eq!(failure.addr(), &addr_2); + assert_eq!(failure.display(), "transport error: my other error"); + assert_eq!(dials(&mut book), vec![]); + + assert_eq!( + addrs(&book, peer_a), + vec![ + (addr_1p.clone(), AddressSource::User), + (addr_2p.clone(), AddressSource::Incoming) + ] + ); + book.remove_address(&peer_a, &addr_1p); + book.remove_address(&peer_a, &addr_2p); + assert_eq!(addrs(&book, peer_a), vec![]); + + book.add_address(&peer_a, addr_2.clone(), AddressSource::Kad); + assert_eq!( + addrs(&book, peer_a), + vec![(addr_2p.clone(), AddressSource::Kad)] + ); + assert_eq!(events.next(), vec![NewInfo(peer_a)]); + + book.add_address(&peer_a, addr_2, AddressSource::User); + assert_eq!(addrs(&book, peer_a), vec![(addr_2p, AddressSource::User)]); + assert_eq!(events.next(), vec![Discovered(peer_a), NewInfo(peer_a)]); +} + +// #[test] +// fn test_dial_with_added_addrs() { +// let mut book = AddressBook::new( +// PeerId::random(), +// "".into(), +// Keypair::generate().public(), +// false, +// false, +// ); + +// let events = Default::default(); +// let events = Events::new(book.swarm_events(), &events); + +// let peer_a = PeerId::random(); +// let addr_1: Multiaddr = "/ip4/1.1.1.1/tcp/3333".parse().unwrap(); +// let addr_1p = addr_1.clone().with(Protocol::P2p(peer_a.into())); +// let addr_2: Multiaddr = "/ip4/2.2.2.2/tcp/3333".parse().unwrap(); +// let error = std::io::Error::new(std::io::ErrorKind::Other, "my error"); + +// book.add_address(&peer_a, addr_1.clone(), AddressSource::Mdns); +// assert_eq!(events.next(), vec!(Discovered(peer_a), NewInfo(peer_a))); +// assert_eq!(dials(&mut book), vec!(Dial::A(addr_1p))); + +// book.add_address(&peer_a, addr_2.clone(), AddressSource::Incoming); +// assert_eq!(events.next(), vec!(NewInfo(peer_a))); + +// book.dial_failure( +// Some(peer_a), +// IntoAddressHandler(Some(addr_1)), +// &DialError::ConnectionIo(error), +// ); +// assert_eq!( +// events.next(), +// vec!( +// DialFailure(peer_a, addr_1.clone(), "my error".to_owned()), +// NewInfo(peer_a) +// ) +// ); + +// book.dial_failure(peer_a); +// // Incoming addresses are not eligible for dialling until verified +// assert_eq!(dials(&mut book), vec!(Dial::P(peer_a, vec!()))); +// let peers = book.peers().collect::>(); +// assert_eq!(peers, vec![&peer_a]); + +// book.dial_failure(Some(&peer_a), &addr_2, &error); +// assert_eq!( +// events.next(), +// vec!( +// DialFailure(peer_a, addr_2.clone(), "my error".to_owned()), +// NewInfo(peer_a) +// ) +// ); + +// book.dial_failure(&peer_a); +// assert_eq!(events.next(), vec!(NewInfo(peer_a), Unreachable(peer_a))); +// assert!(book.peers().next().is_none()); +// } + +#[test] +fn from_docker_host() { + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::default()) + .try_init() + .ok(); + + let peer_a = PeerId::random(); + let addr_a_1: Multiaddr = "/ip4/10.0.0.2/tcp/4001".parse().unwrap(); + let addr_a_1p = addr_a_1.clone().with(Protocol::P2p(peer_a.into())); + + let mut book = AddressBook::new( + peer_a, + false, + false, + false, + Writer::new(HashSet::default()), + Writer::new(HashMap::default()), + Writer::new(vec![]), + ); + let events = Default::default(); + let (tx, rx) = mpsc::unbounded(); + book.swarm_events(tx); + let events = Events::new(SwarmEvents::new(rx), &events); + + let key_b = libp2p::identity::PublicKey::Ed25519(Keypair::generate().public()); + let peer_b = PeerId::from(&key_b); + let addr_b_1: Multiaddr = "/ip4/10.0.0.10/tcp/57634".parse().unwrap(); + let addr_b_1p = addr_b_1.with(Protocol::P2p(peer_b.into())); + let addr_b_2: Multiaddr = "/ip4/10.0.0.10/tcp/4001".parse().unwrap(); + let addr_b_2p = addr_b_2.clone().with(Protocol::P2p(peer_b.into())); + let addr_b_3: Multiaddr = "/ip4/172.17.0.3/tcp/4001".parse().unwrap(); + let addr_b_3p = addr_b_3.clone().with(Protocol::P2p(peer_b.into())); + let addr_b_4: Multiaddr = "/ip4/127.0.0.1/tcp/4001".parse().unwrap(); + + let id = ConnectionId::new(1); + let cp = ConnectedPoint::Listener { + local_addr: addr_a_1p, + send_back_addr: addr_b_1p.clone(), + }; + book.on_swarm_event(FromSwarm::ConnectionEstablished(CE { + peer_id: peer_b, + connection_id: id, + endpoint: &cp, + failed_addresses: &[], + other_established: 0, + })); + assert_eq!( + events.next(), + vec![ + NewInfo(peer_b), + Connected(peer_b), + ConnectionEstablished(peer_b, cp.clone()) + ] + ); + assert_eq!(dials(&mut book), vec![]); + assert_eq!( + addrs(&book, peer_b), + vec![(addr_b_1p, AddressSource::Incoming)] + ); + + let info = identify::Info { + public_key: key_b, + protocol_version: "my protocol".to_owned(), + agent_version: "my agent".to_owned(), + listen_addrs: vec![addr_b_2, addr_b_3, addr_b_4], + protocols: vec!["my proto".to_owned()], + observed_addr: addr_a_1, + }; + book.set_info(&peer_b, info); + assert_eq!(events.next(), vec![NewInfo(peer_b)]); + assert_eq!( + dials(&mut book), + vec![Dial::A(addr_b_2p.clone()), Dial::A(addr_b_3p.clone())] + ); + assert_eq!( + addrs(&book, peer_b), + vec![ + (addr_b_2p.clone(), AddressSource::Listen), + (addr_b_3p.clone(), AddressSource::Listen) + ] + ); + + let id2 = ConnectionId::new(2); + let cp2 = ConnectedPoint::Dialer { + address: addr_b_2p.clone(), + role_override: Endpoint::Dialer, + }; + book.on_swarm_event(FromSwarm::ConnectionEstablished(CE { + peer_id: peer_b, + connection_id: id2, + endpoint: &cp2, + failed_addresses: &[], + other_established: 0, + })); + assert_eq!( + events.next(), + vec![ + Discovered(peer_b), + NewInfo(peer_b), + Connected(peer_b), + ConnectionEstablished(peer_b, cp2) + ] + ); + assert_eq!(dials(&mut book), vec![]); + assert_eq!( + addrs(&book, peer_b), + vec![ + (addr_b_2p.clone(), AddressSource::Dial), + (addr_b_3p.clone(), AddressSource::Listen) + ] + ); + + let error = std::io::Error::new(ErrorKind::Other, "didn’t work, mate!"); + book.dial_failure( + IntoAddressHandler(Some((addr_b_3p.clone(), 3)), false), + Some(peer_b), + &DialError::ConnectionIo(error), + ); + assert_eq!( + events.next(), + vec![ + DialFailure( + peer_b, + addr_b_3p, + "Dial error: An I/O error occurred on the connection: \ + Custom { kind: Other, error: \"didn’t work, mate!\" }." + .to_owned() + ), + NewInfo(peer_b) + ] + ); + assert_eq!(dials(&mut book), vec![]); + assert_eq!(addrs(&book, peer_b), vec![(addr_b_2p, AddressSource::Dial)]); +} + +#[test] +fn from_docker_container() { + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::default()) + .try_init() + .ok(); + + let peer_a = PeerId::random(); + let addr_a_1: Multiaddr = "/ip4/10.0.0.2/tcp/4001".parse().unwrap(); + let addr_a_1p = addr_a_1.clone().with(Protocol::P2p(peer_a.into())); + + let mut book = AddressBook::new( + peer_a, + false, + false, + false, + Writer::new(HashSet::default()), + Writer::new(HashMap::default()), + Writer::new(vec![]), + ); + let events = Default::default(); + let (tx, rx) = mpsc::unbounded(); + book.swarm_events(tx); + let events = Events::new(SwarmEvents::new(rx), &events); + + let key_b = libp2p::identity::PublicKey::Ed25519(Keypair::generate().public()); + let peer_b = PeerId::from(&key_b); + let addr_b_1: Multiaddr = "/ip4/10.0.0.10/tcp/57634".parse().unwrap(); + let addr_b_1p = addr_b_1.with(Protocol::P2p(peer_b.into())); + let addr_b_2: Multiaddr = "/ip4/10.0.0.10/tcp/4001".parse().unwrap(); + let addr_b_2p = addr_b_2.clone().with(Protocol::P2p(peer_b.into())); + let addr_b_3: Multiaddr = "/ip4/172.17.0.3/tcp/4001".parse().unwrap(); + let addr_b_3p = addr_b_3.clone().with(Protocol::P2p(peer_b.into())); + let addr_b_4: Multiaddr = "/ip4/127.0.0.1/tcp/4001".parse().unwrap(); + + let id = ConnectionId::new(1); + let cp = ConnectedPoint::Listener { + local_addr: addr_a_1p, + send_back_addr: addr_b_1p.clone(), + }; + book.on_swarm_event(FromSwarm::ConnectionEstablished(CE { + peer_id: peer_b, + connection_id: id, + endpoint: &cp, + failed_addresses: &[], + other_established: 0, + })); + assert_eq!( + events.next(), + vec![ + NewInfo(peer_b), + Connected(peer_b), + ConnectionEstablished(peer_b, cp.clone()) + ] + ); + assert_eq!(dials(&mut book), vec![]); + assert_eq!( + addrs(&book, peer_b), + vec![(addr_b_1p, AddressSource::Incoming)] + ); + + let info = identify::Info { + public_key: key_b.clone(), + protocol_version: "my protocol".to_owned(), + agent_version: "my agent".to_owned(), + listen_addrs: vec![addr_b_3.clone(), addr_b_4.clone()], + protocols: vec!["my proto".to_owned()], + observed_addr: addr_a_1.clone(), + }; + book.set_info(&peer_b, info); + assert_eq!(events.next(), vec![NewInfo(peer_b)]); + assert_eq!( + dials(&mut book), + vec![Dial::A(addr_b_2p.clone()), Dial::A(addr_b_3p.clone())] + ); + assert_eq!( + addrs(&book, peer_b), + vec![ + (addr_b_2p.clone(), AddressSource::Listen), + (addr_b_3p.clone(), AddressSource::Listen) + ] + ); + + // here we assume that our observeration of that peer’s address will eventually + // be included by that peer in its Identify info + + let info = identify::Info { + public_key: key_b, + protocol_version: "my protocol".to_owned(), + agent_version: "my agent".to_owned(), + listen_addrs: vec![addr_b_2, addr_b_3, addr_b_4], + protocols: vec!["my proto".to_owned()], + observed_addr: addr_a_1, + }; + book.set_info(&peer_b, info); + assert_eq!(events.next(), vec![NewInfo(peer_b)]); + // no dials: we dialled all addresses last time already + assert_eq!(dials(&mut book), vec![]); + assert_eq!( + addrs(&book, peer_b), + vec![ + (addr_b_2p.clone(), AddressSource::Listen), + (addr_b_3p.clone(), AddressSource::Listen) + ] + ); + + let error = std::io::Error::new(ErrorKind::Other, "play it again, Sam"); + book.dial_failure( + IntoAddressHandler(Some((addr_b_3p.clone(), 3)), false), + Some(peer_b), + &DialError::ConnectionIo(error), + ); + assert_eq!( + events.next(), + vec![ + DialFailure( + peer_b, + addr_b_3p, + "Dial error: An I/O error occurred on the connection: \ + Custom { kind: Other, error: \"play it again, Sam\" }." + .to_owned() + ), + NewInfo(peer_b) + ] + ); + assert_eq!(dials(&mut book), vec![]); + assert_eq!( + addrs(&book, peer_b), + vec![(addr_b_2p.clone(), AddressSource::Listen)] + ); + + let id2 = ConnectionId::new(2); + let cp2 = ConnectedPoint::Dialer { + address: addr_b_2p.clone(), + role_override: Endpoint::Dialer, + }; + book.on_swarm_event(FromSwarm::ConnectionEstablished(CE { + peer_id: peer_b, + connection_id: id2, + endpoint: &cp2, + failed_addresses: &[], + other_established: 0, + })); + assert_eq!( + events.next(), + vec![ + Discovered(peer_b), + NewInfo(peer_b), + Connected(peer_b), + ConnectionEstablished(peer_b, cp2) + ] + ); + assert_eq!(dials(&mut book), vec![]); + assert_eq!( + addrs(&book, peer_b), + vec![(addr_b_2p.clone(), AddressSource::Dial)] + ); + + let error = std::io::Error::new(ErrorKind::Other, "play it yet another time, Sam"); + book.dial_failure( + IntoAddressHandler(Some((addr_b_2p.clone(), 3)), false), + Some(peer_b), + &DialError::ConnectionIo(error), + ); + assert_eq!( + events.next(), + vec![ + DialFailure( + peer_b, + addr_b_2p.clone(), + "Dial error: An I/O error occurred on the connection: \ + Custom { kind: Other, error: \"play it yet another time, Sam\" }." + .to_owned() + ), + NewInfo(peer_b) + ] + ); + assert_eq!(dials(&mut book), vec![]); + assert_eq!(addrs(&book, peer_b), vec![(addr_b_2p, AddressSource::Dial)]); + + assert_eq!( + book.info(&peer_b) + .unwrap() + .recent_failures() + .map(|cf| ( + cf.addr().to_string(), + cf.display().to_owned(), + cf.debug().to_owned() + )) + .collect::>(), + vec![ + ( + "/ip4/10.0.0.10/tcp/4001".to_owned(), + "I/O error: play it yet another time, Sam".to_owned(), + "ConnectionIo(Custom { kind: Other, error: \"play it yet another time, Sam\" })" + .to_owned(), + ), + ( + "/ip4/172.17.0.3/tcp/4001".to_owned(), + "I/O error: play it again, Sam".to_owned(), + "ConnectionIo(Custom { kind: Other, error: \"play it again, Sam\" })".to_owned(), + ) + ] + ); +} + +fn addrs(book: &AddressBook, peer_id: PeerId) -> Vec<(Multiaddr, AddressSource)> { + let mut v = book + .info(&peer_id) + .unwrap() + .addresses() + .map(|(a, s, _dt)| (a.clone(), s)) + .collect::>(); + v.sort(); + v +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +enum Dial { + A(Multiaddr), + P(PeerId, Vec), +} + +fn dials(book: &mut AddressBook) -> Vec { + let mut v = book + .actions + .drain(..) + .filter_map(|a| match a { + NetworkBehaviourAction::Dial { + handler: IntoAddressHandler(Some((address, _retries)), false), + .. + } => Some(Dial::A(address)), + NetworkBehaviourAction::Dial { opts, .. } => opts + .get_peer_id() + .map(|peer_id| Dial::P(peer_id, Vec::new())), + _ => None, + }) + .collect::>(); + v.sort(); + for d in &mut v { + match d { + Dial::A(_) => {} + Dial::P(peer, addrs) => addrs.extend(book.addresses_of_peer(peer)), + } + } + v +} diff --git a/src/telemetry.rs b/src/telemetry.rs index e331edd..7d4f563 100644 --- a/src/telemetry.rs +++ b/src/telemetry.rs @@ -1,8 +1,6 @@ use crate::Ipfs; use anyhow::Result; -use libipld::codec::References; -use libipld::store::StoreParams; -use libipld::Ipld; +use libipld::{codec::References, store::StoreParams, Ipld}; use prometheus::Encoder; /// Telemetry server diff --git a/src/test_util.rs b/src/test_util.rs index 2d7b9e1..c9a6342 100644 --- a/src/test_util.rs +++ b/src/test_util.rs @@ -1,7 +1,5 @@ use anyhow::Result; -use libipld::cbor::DagCborCodec; -use libipld::multihash::Code; -use libipld::{Block, Cid, DagCbor, DefaultParams}; +use libipld::{cbor::DagCborCodec, multihash::Code, Block, Cid, DagCbor, DefaultParams}; use rand::RngCore; #[derive(Debug, DagCbor)] diff --git a/src/variable.rs b/src/variable.rs new file mode 100644 index 0000000..dadf615 --- /dev/null +++ b/src/variable.rs @@ -0,0 +1,62 @@ +use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; +use std::sync::Arc; + +struct Inner { + value: RwLock, +} + +/// Write-side of a variable with read capability to use as single source of truth +/// +/// Usage of [`read`] and [`write`] should be non-blocking so that readers can always +/// quickly access the latest value. +#[derive(Clone)] +pub struct Writer(Arc>); + +impl std::fmt::Debug for Writer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("Writer").finish() + } +} + +impl Writer { + pub fn new(value: T) -> Self { + Self(Arc::new(Inner { + value: RwLock::new(value), + })) + } + + pub fn write(&self) -> RwLockWriteGuard<'_, T> { + self.0.value.write() + } + + pub fn read(&self) -> RwLockReadGuard<'_, T> { + self.0.value.read() + } + + pub fn reader(&self) -> Reader { + Reader(self.0.clone()) + } +} + +/// Read-side of a variable, intentionally limited to avoid blocking the writer +#[derive(Clone)] +pub struct Reader(Arc>); + +impl Reader { + pub fn project(&self, f: impl Fn(&T) -> U) -> U { + let value = self.0.value.read(); + f(&*value) + } +} + +impl Reader { + pub fn get(&self) -> T { + *self.0.value.read() + } +} + +impl Reader { + pub fn get_cloned(&self) -> T { + self.0.value.read().clone() + } +} diff --git a/tests/gc.rs b/tests/gc.rs index 4f1618c..a761111 100644 --- a/tests/gc.rs +++ b/tests/gc.rs @@ -1,18 +1,18 @@ use anyhow::Result; use fnv::FnvHashSet; use ipfs_embed::{ - generate_keypair, Block, Cid, Config, DefaultParams, Ipfs, NetworkConfig, StorageConfig, + identity::ed25519::Keypair, Block, Cid, Config, DefaultParams, Ipfs, NetworkConfig, + StorageConfig, }; -use libipld::cbor::DagCborCodec; -use libipld::multihash::Code; -use libipld::DagCbor; +use libipld::{cbor::DagCborCodec, multihash::Code, DagCbor}; use rand::{thread_rng, RngCore}; use std::time::{Duration, Instant}; -use tempdir::TempDir; +use tracing_subscriber::fmt::format::FmtSpan; fn tracing_try_init() { tracing_subscriber::fmt() .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .with_span_events(FmtSpan::ENTER | FmtSpan::CLOSE) .try_init() .ok(); } @@ -29,6 +29,8 @@ async fn gc() -> Result<()> { let now = Instant::now(); builder.check().await?; println!("checked dags in {}ms", now.elapsed().as_millis()); + let heads = builder.heads(); + println!(" {} heads", heads); for _ in 0..builder.heads() { let now = Instant::now(); builder.remove_head()?; @@ -48,21 +50,19 @@ struct Node { struct DagBuilder { ipfs: Ipfs, heads: FnvHashSet, - _tmp: TempDir, } impl DagBuilder { async fn new() -> Result { - let tmp = TempDir::new("gc-test")?; + let tmp = tempdir::TempDir::new("gc")?; let config = Config { - storage: StorageConfig::new(None, None, 0, Duration::from_secs(1000)), - network: NetworkConfig::new(tmp.path().into(), generate_keypair()), + storage: StorageConfig::new(Some(tmp.into_path()), None, 0, Duration::from_secs(1000)), + network: NetworkConfig::new(Keypair::generate()), }; let ipfs = Ipfs::new(config).await?; Ok(Self { ipfs, heads: Default::default(), - _tmp: tmp, }) } @@ -88,12 +88,13 @@ impl DagBuilder { let node = Node { nonce, children }; let block = Block::encode(DagCborCodec, Code::Blake3_256, &node)?; self.ipfs.alias(block.cid().to_bytes(), Some(block.cid()))?; - self.ipfs.insert(&block)?; + let cid = *block.cid(); + self.ipfs.insert(block)?; for cid in node.children.into_iter().take(n_children_rm) { self.ipfs.alias(cid.to_bytes(), None)?; self.heads.remove(&cid); } - self.heads.insert(*block.cid()); + self.heads.insert(cid); Ok(()) }