diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index b182c4037..b08084d9f 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -1,18 +1,17 @@ name: Security audit on: - push: - paths: - - "**/Cargo.toml" - - "**/Cargo.lock" - schedule: - - cron: "0 0 * * *" + workflow_dispatch: false + # push: + # paths: + # - "**/Cargo.toml" + # - "**/Cargo.lock" + # schedule: + # - cron: "0 0 * * *" jobs: audit: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - with: - submodules: true - - uses: actions-rs/audit-check@v1 + - uses: rustsec/audit-check@v2.0.0 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8b43d1c62..c61bbff07 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,8 @@ jobs: test_all: name: Test - runs-on: ubuntu-latest + # Use more powerful runner + runs-on: freenet-core-ci strategy: max-parallel: 1 @@ -38,6 +39,9 @@ jobs: targets: wasm32-unknown-unknown - uses: Swatinem/rust-cache@v2 + if: success() || steps.test.conclusion == 'failure' + with: + save-if: ${{ github.ref == 'refs/heads/main' }} - name: Install stdlib packages working-directory: stdlib/typescript @@ -45,9 +49,10 @@ jobs: - name: Build run: | + cargo install --locked --force --path ./crates/core + cargo install --locked --force --path ./crates/fdev + make -C apps/freenet-ping -f run-ping.mk build cargo build --locked - cargo install --path ./crates/core - cargo install --path ./crates/fdev - name: Test - features run: cargo test --workspace ${{ matrix.args }} @@ -75,9 +80,16 @@ jobs: with: toolchain: stable components: clippy + targets: wasm32-unknown-unknown - uses: Swatinem/rust-cache@v2 + - name: Build + run: | + cargo install --locked --force --path ../../crates/fdev + make -f run-ping.mk build + working-directory: apps/freenet-ping + - name: clippy run: cargo clippy -- -D warnings diff --git a/.gitignore b/.gitignore index 2a3f4ec7a..ede5bc90f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ - ### Rust ### # Generated by Cargo # will have compiled files and executables @@ -25,3 +24,4 @@ config.toml .rustc* rustc-ice*.txt +.aider* diff --git a/Cargo.lock b/Cargo.lock index f27e50c42..72beb8255 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,17 +52,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - [[package]] name = "ahash" version = "0.8.11" @@ -86,9 +75,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" [[package]] name = "android-tzdata" @@ -107,9 +96,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -122,43 +111,43 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.90" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37bf3594c4c988a53154954629820791dde498571819ae4ca50ca811e060cc95" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" [[package]] name = "approx" @@ -171,9 +160,9 @@ dependencies = [ [[package]] name = "arbitrary" -version = "1.3.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" dependencies = [ "derive_arbitrary", ] @@ -221,17 +210,17 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.4" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" dependencies = [ "async-lock", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.3.0", + "futures-lite 2.5.0", "parking", - "polling 3.7.3", + "polling 3.7.4", "rustix", "slab", "tracing", @@ -268,7 +257,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.80", + "syn 2.0.89", ] [[package]] @@ -279,7 +268,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.80", + "syn 2.0.89", ] [[package]] @@ -327,9 +316,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" -version = "0.7.7" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" dependencies = [ "async-trait", "axum-core", @@ -353,7 +342,7 @@ dependencies = [ "serde_path_to_error", "serde_urlencoded", "sha1", - "sync_wrapper 1.0.1", + "sync_wrapper 1.0.2", "tokio", "tokio-tungstenite", "tower 0.5.1", @@ -376,7 +365,7 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 1.0.1", + "sync_wrapper 1.0.2", "tower-layer", "tower-service", ] @@ -391,7 +380,7 @@ dependencies = [ "cfg-if", "libc", "miniz_oxide", - "object", + "object 0.36.5", "rustc-demangle", "windows-targets 0.52.6", ] @@ -423,6 +412,26 @@ dependencies = [ "serde", ] +[[package]] +name = "bindgen" +version = "0.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.89", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -438,18 +447,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - [[package]] name = "blake3" version = "1.5.4" @@ -494,8 +491,20 @@ version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" dependencies = [ - "bytecheck_derive", - "ptr_meta", + "bytecheck_derive 0.6.12", + "ptr_meta 0.1.4", + "simdutf8", +] + +[[package]] +name = "bytecheck" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c8f430744b23b54ad15161fcbc22d82a29b73eacbe425fea23ec822600bc6f" +dependencies = [ + "bytecheck_derive 0.8.0", + "ptr_meta 0.3.0", + "rancor", "simdutf8", ] @@ -510,11 +519,22 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "bytecheck_derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523363cbe1df49b68215efdf500b103ac3b0fb4836aed6d15689a076eadb8fff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "bytemuck" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" [[package]] name = "byteorder" @@ -524,9 +544,30 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.2" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" + +[[package]] +name = "bzip2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] [[package]] name = "cache-padded" @@ -542,13 +583,24 @@ checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" [[package]] name = "cc" -version = "1.1.31" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" dependencies = [ + "jobserver", + "libc", "shlex", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -612,11 +664,22 @@ dependencies = [ "zeroize", ] +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" -version = "4.5.20" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" dependencies = [ "clap_builder", "clap_derive", @@ -624,9 +687,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.20" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" dependencies = [ "anstream", "anstyle", @@ -643,20 +706,29 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.80", + "syn 2.0.89", ] [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" + +[[package]] +name = "cmake" +version = "0.1.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c682c223677e0e5b6b7f63a64b9351844c3f1b1678a68b7ee617e30fb082620e" +dependencies = [ + "cc", +] [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "concurrent-queue" @@ -708,96 +780,102 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "corosensei" -version = "0.1.4" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80128832c58ea9cbd041d2a759ec449224487b2c1e400453d99d244eead87a8e" +checksum = "ad067b451c08956709f8762dba86e049c124ea52858e3ab8d076ba2892caa437" dependencies = [ "autocfg", "cfg-if", "libc", "scopeguard", - "windows-sys 0.33.0", + "windows-sys 0.59.0", ] [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] [[package]] name = "cranelift-bforest" -version = "0.91.1" +version = "0.110.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2ab4512dfd3a6f4be184403a195f76e81a8a9f9e6c898e19d2dc3ce20e0115" +checksum = "305d51c180ebdc46ef61bc60c54ae6512db3bc9a05842a1f1e762e45977019ab" dependencies = [ "cranelift-entity", ] +[[package]] +name = "cranelift-bitset" +version = "0.110.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "690d8ae6c73748e5ce3d8fe59034dceadb8823e6c8994ba324141c5eae909b0e" + [[package]] name = "cranelift-codegen" -version = "0.91.1" +version = "0.110.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98b022ed2a5913a38839dfbafe6cf135342661293b08049843362df4301261dc" +checksum = "bd7ca95e831c18d1356da783765c344207cbdffea91e13e47fa9327dbb2e0719" dependencies = [ - "arrayvec", "bumpalo", "cranelift-bforest", + "cranelift-bitset", "cranelift-codegen-meta", "cranelift-codegen-shared", - "cranelift-egraph", + "cranelift-control", "cranelift-entity", "cranelift-isle", - "gimli 0.26.2", + "gimli 0.28.1", + "hashbrown 0.14.5", "log", "regalloc2", + "rustc-hash", "smallvec", "target-lexicon", ] [[package]] name = "cranelift-codegen-meta" -version = "0.91.1" +version = "0.110.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "639307b45434ad112a98f8300c0f0ab085cbefcd767efcdef9ef19d4c0756e74" +checksum = "f0a2d2ab65e6cbf91f81781d8da65ec2005510f18300eff21a99526ed6785863" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.91.1" +version = "0.110.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "278e52e29c53fcf32431ef08406c295699a70306d05a0715c5b1bf50e33a9ab7" +checksum = "efcff860573cf3db9ae98fbd949240d78b319df686cc306872e7fab60e9c84d7" [[package]] -name = "cranelift-egraph" -version = "0.91.1" +name = "cranelift-control" +version = "0.110.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624b54323b06e675293939311943ba82d323bb340468ce1889be5da7932c8d73" +checksum = "69d70e5b75c2d5541ef80a99966ccd97aaa54d2a6af19ea31759a28538e1685a" dependencies = [ - "cranelift-entity", - "fxhash", - "hashbrown 0.12.3", - "indexmap 1.9.3", - "log", - "smallvec", + "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.91.1" +version = "0.110.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a59bcbca89c3f1b70b93ab3cbba5e5e0cbf3e63dadb23c7525cb142e21a9d4c" +checksum = "a48cb0a194c9ba82fec35a1e492055388d89b2e3c03dee9dcf2488892be8004d" +dependencies = [ + "cranelift-bitset", +] [[package]] name = "cranelift-frontend" -version = "0.91.1" +version = "0.110.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d70abacb8cfef3dc8ff7e8836e9c1d70f7967dfdac824a4cd5e30223415aca6" +checksum = "8327afc6c1c05f4be62fefce5b439fa83521c65363a322e86ea32c85e7ceaf64" dependencies = [ "cranelift-codegen", "log", @@ -807,9 +885,9 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.91.1" +version = "0.110.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "393bc73c451830ff8dbb3a07f61843d6cb41a084f9996319917c0b291ed785bb" +checksum = "56b08621c00321efcfa3eee6a3179adc009e21ea8d24ca7adc3c326184bc3f48" [[package]] name = "crc" @@ -826,6 +904,15 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam" version = "0.8.4" @@ -893,6 +980,27 @@ dependencies = [ "typenum", ] +[[package]] +name = "csv" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +dependencies = [ + "memchr", +] + [[package]] name = "ctr" version = "0.9.2" @@ -929,9 +1037,9 @@ dependencies = [ [[package]] name = "curl-sys" -version = "0.4.77+curl-8.10.1" +version = "0.4.78+curl-8.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f469e8a5991f277a208224f6c7ad72ecb5f986e36d09ae1f2c1bb9259478a480" +checksum = "8eec768341c5c7789611ae51cf6c459099f22e64a5d5d0ce4892434e33821eaf" dependencies = [ "cc", "libc", @@ -963,7 +1071,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.80", + "syn 2.0.89", ] [[package]] @@ -974,7 +1082,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.80", + "syn 2.0.89", ] [[package]] @@ -997,6 +1105,12 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +[[package]] +name = "deflate64" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b" + [[package]] name = "delegate" version = "0.13.1" @@ -1005,7 +1119,7 @@ checksum = "bc2323e10c92e1cf4d86e11538512e6dc03ceb586842970b6332af3d4046a046" dependencies = [ "proc-macro2", "quote", - "syn 2.0.80", + "syn 2.0.89", ] [[package]] @@ -1030,25 +1144,25 @@ dependencies = [ ] [[package]] -name = "derivative" -version = "2.2.0" +name = "derive_arbitrary" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.89", ] [[package]] -name = "derive_arbitrary" -version = "1.3.2" +name = "derive_more" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "proc-macro2", "quote", - "syn 2.0.80", + "syn 2.0.89", ] [[package]] @@ -1072,6 +1186,16 @@ dependencies = [ "dirs-sys", ] +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + [[package]] name = "dirs-sys" version = "0.4.1" @@ -1084,6 +1208,28 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "dotenvy" version = "0.15.7" @@ -1099,11 +1245,17 @@ dependencies = [ "serde", ] +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] @@ -1117,7 +1269,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.80", + "syn 2.0.89", ] [[package]] @@ -1158,7 +1310,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.80", + "syn 2.0.89", ] [[package]] @@ -1219,9 +1371,9 @@ dependencies = [ [[package]] name = "fallible-iterator" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fastrand" @@ -1234,9 +1386,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" [[package]] name = "fdev" @@ -1256,6 +1408,7 @@ dependencies = [ "glob", "http 1.1.0", "pico-args", + "prettytable-rs", "rand", "reqwest", "semver", @@ -1263,7 +1416,7 @@ dependencies = [ "serde_json", "serde_with", "tar", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-tungstenite", "toml", @@ -1294,6 +1447,16 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "flume" version = "0.11.1" @@ -1366,14 +1529,14 @@ dependencies = [ "futures", "headers", "hickory-resolver", - "itertools", + "itertools 0.13.0", "notify", "once_cell", "opentelemetry 0.26.0", "opentelemetry-jaeger", "opentelemetry-otlp", "opentelemetry_sdk 0.26.0", - "ordered-float 4.4.0", + "ordered-float 4.5.0", "parking_lot", "pav_regression", "pico-args", @@ -1391,7 +1554,7 @@ dependencies = [ "stretto", "tar", "tempfile", - "thiserror", + "thiserror 1.0.69", "time", "tokio", "tokio-tungstenite", @@ -1412,7 +1575,45 @@ version = "0.0.5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.80", + "syn 2.0.89", +] + +[[package]] +name = "freenet-ping" +version = "0.1.0" +dependencies = [ + "chrono", + "clap", + "freenet-ping-types", + "freenet-stdlib", + "futures", + "names", + "serde_json", + "tokio", + "tokio-tungstenite", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "freenet-ping-contract" +version = "0.1.0" +dependencies = [ + "freenet-ping-types", + "freenet-stdlib", + "serde_json", +] + +[[package]] +name = "freenet-ping-types" +version = "0.1.0" +dependencies = [ + "chrono", + "clap", + "freenet-stdlib", + "humantime", + "humantime-serde", + "serde", ] [[package]] @@ -1437,7 +1638,7 @@ dependencies = [ "serde_bytes", "serde_json", "serde_with", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-tungstenite", "tracing", @@ -1456,12 +1657,6 @@ dependencies = [ "libc", ] -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - [[package]] name = "futures" version = "0.3.31" @@ -1538,9 +1733,9 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.3.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" dependencies = [ "futures-core", "pin-project-lite", @@ -1554,7 +1749,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.80", + "syn 2.0.89", ] [[package]] @@ -1587,15 +1782,6 @@ dependencies = [ "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.7" @@ -1631,12 +1817,12 @@ dependencies = [ [[package]] name = "gimli" -version = "0.26.2" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" dependencies = [ "fallible-iterator", - "indexmap 1.9.3", + "indexmap 2.7.0", "stable_deref_trait", ] @@ -1654,9 +1840,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "h2" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" dependencies = [ "atomic-waker", "bytes", @@ -1664,7 +1850,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.1.0", - "indexmap 2.6.0", + "indexmap 2.7.0", "slab", "tokio", "tokio-util", @@ -1676,8 +1862,14 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.7.8", + "ahash", ] [[package]] @@ -1686,15 +1878,15 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ - "ahash 0.8.11", + "ahash", "allocator-api2", ] [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" [[package]] name = "hashlink" @@ -1772,7 +1964,7 @@ dependencies = [ "rand", "rustls 0.21.12", "rustls-pemfile 1.0.4", - "thiserror", + "thiserror 1.0.69", "tinyvec", "tokio", "tokio-rustls 0.24.1", @@ -1797,7 +1989,7 @@ dependencies = [ "resolv-conf", "rustls 0.21.12", "smallvec", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-rustls 0.24.1", "tracing", @@ -1904,11 +2096,27 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "humantime-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" +dependencies = [ + "humantime", + "serde", +] + [[package]] name = "hyper" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" +checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" dependencies = [ "bytes", "futures-channel", @@ -1935,7 +2143,7 @@ dependencies = [ "http 1.1.0", "hyper", "hyper-util", - "rustls 0.23.15", + "rustls 0.23.20", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -1944,9 +2152,9 @@ dependencies = [ [[package]] name = "hyper-timeout" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ "hyper", "hyper-util", @@ -1973,9 +2181,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-channel", @@ -2013,6 +2221,124 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -2031,12 +2357,23 @@ dependencies = [ [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -2052,12 +2389,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown 0.15.1", "serde", ] @@ -2123,6 +2460,17 @@ version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +[[package]] +name = "is-terminal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +dependencies = [ + "hermit-abi 0.4.0", + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -2154,6 +2502,15 @@ dependencies = [ "waker-fn", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.13.0" @@ -2165,9 +2522,18 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "540654e97a3f4470a492cd30ff187bc95d89557a903a2bbf112e2fae98104ef2" + +[[package]] +name = "jobserver" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] [[package]] name = "js-sys" @@ -2215,15 +2581,25 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.161" +version = "0.2.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" + +[[package]] +name = "libloading" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] [[package]] name = "libm" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "libredox" @@ -2271,6 +2647,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + [[package]] name = "lock_api" version = "0.4.12" @@ -2281,6 +2663,12 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "lockfree-object-pool" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" + [[package]] name = "log" version = "0.4.22" @@ -2296,6 +2684,16 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "lzma-rs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" +dependencies = [ + "byteorder", + "crc", +] + [[package]] name = "lzma-sys" version = "0.1.20" @@ -2442,6 +2840,26 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" +[[package]] +name = "munge" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64142d38c84badf60abf06ff9bd80ad2174306a5b11bd4706535090a30a419df" +dependencies = [ + "munge_macro", +] + +[[package]] +name = "munge_macro" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb5c1d8184f13f7d0ccbeeca0def2f9a181bce2624302793005f5ca8aa62e5e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "nalgebra" version = "0.32.6" @@ -2468,7 +2886,16 @@ checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.80", + "syn 2.0.89", +] + +[[package]] +name = "names" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bddcd3bf5144b6392de80e04c347cd7fab2508f6df16a85fc496ecd5cec39bc" +dependencies = [ + "rand", ] [[package]] @@ -2622,6 +3049,20 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "crc32fast", + "flate2", + "hashbrown 0.14.5", + "indexmap 2.7.0", + "memchr", + "ruzstd", +] + [[package]] name = "object" version = "0.36.5" @@ -2666,7 +3107,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.80", + "syn 2.0.89", ] [[package]] @@ -2698,7 +3139,7 @@ dependencies = [ "js-sys", "once_cell", "pin-project-lite", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2712,7 +3153,7 @@ dependencies = [ "js-sys", "once_cell", "pin-project-lite", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2726,7 +3167,7 @@ dependencies = [ "js-sys", "once_cell", "pin-project-lite", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2773,7 +3214,7 @@ dependencies = [ "opentelemetry-proto", "opentelemetry_sdk 0.26.0", "prost", - "thiserror", + "thiserror 1.0.69", "tokio", "tonic", ] @@ -2809,10 +3250,10 @@ dependencies = [ "lazy_static", "once_cell", "opentelemetry 0.23.0", - "ordered-float 4.4.0", + "ordered-float 4.5.0", "percent-encoding", "rand", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", ] @@ -2832,7 +3273,7 @@ dependencies = [ "opentelemetry 0.25.0", "percent-encoding", "rand", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2851,7 +3292,7 @@ dependencies = [ "percent-encoding", "rand", "serde_json", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", ] @@ -2882,9 +3323,9 @@ dependencies = [ [[package]] name = "ordered-float" -version = "4.4.0" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e7ccb95e240b7c9506a3d544f10d935e142cc90b0a1d56954fb44d89ad6b97" +checksum = "c65ee1f9701bf938026630b455d5315f490640234259037edb259798b3bcf85e" dependencies = [ "num-traits", ] @@ -2941,6 +3382,16 @@ dependencies = [ "thiserror", ] +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -2964,29 +3415,29 @@ checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] name = "pin-project" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.80", + "syn 2.0.89", ] [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -3039,9 +3490,9 @@ dependencies = [ [[package]] name = "polling" -version = "3.7.3" +version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" dependencies = [ "cfg-if", "concurrent-queue", @@ -3077,15 +3528,15 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" +checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" [[package]] name = "portable-atomic-util" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90a7d5beecc52a491b54d6dd05c7a45ba1801666a5baad9fdbfc6fef8d2d206c" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" dependencies = [ "portable-atomic", ] @@ -3106,34 +3557,56 @@ dependencies = [ ] [[package]] -name = "proc-macro-error" -version = "1.0.4" +name = "prettyplease" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +dependencies = [ + "proc-macro2", + "syn 2.0.89", +] + +[[package]] +name = "prettytable-rs" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a" +dependencies = [ + "csv", + "encode_unicode", + "is-terminal", + "lazy_static", + "term", + "unicode-width 0.1.14", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" dependencies = [ - "proc-macro-error-attr", "proc-macro2", "quote", - "syn 1.0.109", - "version_check", ] [[package]] -name = "proc-macro-error-attr" -version = "1.0.4" +name = "proc-macro-error2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" dependencies = [ + "proc-macro-error-attr2", "proc-macro2", "quote", - "version_check", + "syn 2.0.89", ] [[package]] name = "proc-macro2" -version = "1.0.88" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -3155,10 +3628,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" dependencies = [ "anyhow", - "itertools", + "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.80", + "syn 2.0.89", ] [[package]] @@ -3167,7 +3640,16 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" dependencies = [ - "ptr_meta_derive", + "ptr_meta_derive 0.1.4", +] + +[[package]] +name = "ptr_meta" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9e76f66d3f9606f44e45598d155cb13ecf09f4a28199e48daf8c8fc937ea90" +dependencies = [ + "ptr_meta_derive 0.3.0", ] [[package]] @@ -3181,6 +3663,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ptr_meta_derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -3197,10 +3690,13 @@ dependencies = [ ] [[package]] -name = "radium" -version = "0.7.0" +name = "rancor" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +checksum = "caf5f7161924b9d1cea0e4cabc97c372cea92b5f927fc13c6bca67157a0ad947" +dependencies = [ + "ptr_meta 0.3.0", +] [[package]] name = "rand" @@ -3270,9 +3766,9 @@ dependencies = [ [[package]] name = "redb" -version = "2.1.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "074373f3e7e5d27d8741d19512232adb47be8622d3daef3a45bcae72050c3d2a" +checksum = "84b1de48a7cf7ba193e81e078d17ee2b786236eed1d3f7c60f8a09545efc4925" dependencies = [ "libc", ] @@ -3294,30 +3790,31 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", "libredox", - "thiserror", + "thiserror 1.0.69", ] [[package]] name = "regalloc2" -version = "0.5.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300d4fbfb40c1c66a78ba3ddd41c1110247cf52f97b87d0f2fc9209bd49b030c" +checksum = "ad156d539c879b7a24a363a2016d77961786e71f48f2e2fc8302a92abd2429a6" dependencies = [ - "fxhash", + "hashbrown 0.13.2", "log", + "rustc-hash", "slice-group-by", "smallvec", ] [[package]] name = "regex" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "regex-syntax 0.8.5", ] @@ -3332,9 +3829,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -3367,18 +3864,18 @@ dependencies = [ [[package]] name = "rend" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +checksum = "a35e8a6bf28cd121053a66aa2e6a2e3eaffad4a60012179f0e864aa5ffeff215" dependencies = [ - "bytecheck", + "bytecheck 0.8.0", ] [[package]] name = "reqwest" -version = "0.12.8" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" dependencies = [ "base64 0.22.1", "bytes", @@ -3405,7 +3902,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 1.0.1", + "sync_wrapper 1.0.2", "system-configuration", "tokio", "tokio-native-tls", @@ -3444,32 +3941,32 @@ dependencies = [ [[package]] name = "rkyv" -version = "0.7.45" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" +checksum = "b11a153aec4a6ab60795f8ebe2923c597b16b05bb1504377451e705ef1a45323" dependencies = [ - "bitvec", - "bytecheck", - "bytes", - "hashbrown 0.12.3", - "indexmap 1.9.3", - "ptr_meta", + "bytecheck 0.8.0", + "bytes", + "hashbrown 0.15.1", + "indexmap 2.7.0", + "munge", + "ptr_meta 0.3.0", + "rancor", "rend", "rkyv_derive", - "seahash", "tinyvec", "uuid", ] [[package]] name = "rkyv_derive" -version = "0.7.45" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" +checksum = "beb382a4d9f53bd5c0be86b10d8179c3f8a14c30bf774ff77096ed6581e35981" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.89", ] [[package]] @@ -3499,6 +3996,12 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc_version" version = "0.4.1" @@ -3510,9 +4013,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" dependencies = [ "bitflags 2.6.0", "errno", @@ -3535,10 +4038,11 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.15" +version = "0.23.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993" +checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" dependencies = [ + "log", "once_cell", "ring", "rustls-pki-types", @@ -3598,6 +4102,17 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" +[[package]] +name = "ruzstd" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c4eb8a81997cf040a091d1f7e1938aeab6749d3a0dfa73af43cdc32393483d" +dependencies = [ + "byteorder", + "derive_more", + "twox-hash", +] + [[package]] name = "ryu" version = "1.0.18" @@ -3624,9 +4139,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ "windows-sys 0.59.0", ] @@ -3668,9 +4183,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" dependencies = [ "core-foundation-sys", "libc", @@ -3693,9 +4208,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.210" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] @@ -3733,20 +4248,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.80", + "syn 2.0.89", ] [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -3795,7 +4310,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.6.0", + "indexmap 2.7.0", "serde", "serde_derive", "serde_json", @@ -3812,7 +4327,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.80", + "syn 2.0.89", ] [[package]] @@ -3894,6 +4409,12 @@ dependencies = [ "wide", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "simdutf8" version = "0.1.5" @@ -4008,20 +4529,20 @@ dependencies = [ "hashbrown 0.14.5", "hashlink", "hex", - "indexmap 2.6.0", + "indexmap 2.7.0", "log", "memchr", "once_cell", "paste", "percent-encoding", - "rustls 0.23.15", + "rustls 0.23.20", "rustls-pemfile 2.2.0", "serde", "serde_json", "sha2", "smallvec", "sqlformat", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", "tracing", @@ -4039,7 +4560,7 @@ dependencies = [ "quote", "sqlx-core", "sqlx-macros-core", - "syn 2.0.80", + "syn 2.0.89", ] [[package]] @@ -4062,7 +4583,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.80", + "syn 2.0.89", "tempfile", "tokio", "url", @@ -4105,7 +4626,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror", + "thiserror 1.0.69", "tracing", "whoami", ] @@ -4143,7 +4664,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror", + "thiserror 1.0.69", "tracing", "whoami", ] @@ -4177,6 +4698,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "statrs" version = "0.17.1" @@ -4204,7 +4731,7 @@ dependencies = [ "parking_lot", "rand", "seahash", - "thiserror", + "thiserror 1.0.69", "tracing", "wg", "xxhash-rust", @@ -4246,9 +4773,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.80" +version = "2.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e185e337f816bc8da115b8afcb3324006ccc82eeaddf35113888d3bd8e44ac" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" dependencies = [ "proc-macro2", "quote", @@ -4263,13 +4790,24 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "sync_wrapper" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ "futures-core", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "system-configuration" version = "0.6.1" @@ -4291,17 +4829,11 @@ dependencies = [ "libc", ] -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - [[package]] name = "tar" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ff6c40d3aedb5e06b57c6f669ad17ab063dd1e63d977c6a88e7f4dfa4f04020" +checksum = "c65998313f8e17d0d553d28f91a0df93e4dbbbf770279c7bc21ca0f09ea1a1f6" dependencies = [ "filetime", "libc", @@ -4316,35 +4848,66 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", - "fastrand 2.1.1", + "fastrand 2.2.0", "once_cell", "rustix", "windows-sys 0.59.0", ] +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + [[package]] name = "thiserror" -version = "1.0.64" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" +dependencies = [ + "thiserror-impl 2.0.9", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ - "thiserror-impl", + "proc-macro2", + "quote", + "syn 2.0.89", ] [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.80", + "syn 2.0.89", ] [[package]] @@ -4410,6 +4973,16 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -4427,9 +5000,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.40.0" +version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" dependencies = [ "backtrace", "bytes", @@ -4451,7 +5024,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.80", + "syn 2.0.89", ] [[package]] @@ -4480,7 +5053,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.15", + "rustls 0.23.20", "rustls-pki-types", "tokio", ] @@ -4527,7 +5100,7 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.0", "serde", "serde_spanned", "toml_datetime", @@ -4549,7 +5122,7 @@ version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.0", "serde", "serde_spanned", "toml_datetime", @@ -4624,9 +5197,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8437150ab6bbc8c5f0f519e3d5ed4aa883a83dd4cdd3d1b21f9482936046cb97" +checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" dependencies = [ "bitflags 2.6.0", "bytes", @@ -4679,7 +5252,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.80", + "syn 2.0.89", ] [[package]] @@ -4779,10 +5352,20 @@ dependencies = [ "log", "rand", "sha1", - "thiserror", + "thiserror 1.0.69", "utf-8", ] +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "static_assertions", +] + [[package]] name = "typenum" version = "1.17.0" @@ -4815,9 +5398,9 @@ checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-normalization" @@ -4840,6 +5423,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "unicode_categories" version = "0.1.1" @@ -4873,14 +5462,30 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "ureq" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" +dependencies = [ + "base64 0.22.1", + "flate2", + "log", + "once_cell", + "rustls 0.23.20", + "rustls-pki-types", + "url", + "webpki-roots", +] + [[package]] name = "url" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna 1.0.3", "percent-encoding", ] @@ -4890,6 +5495,18 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -4981,7 +5598,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.80", + "syn 2.0.89", "wasm-bindgen-shared", ] @@ -5015,7 +5632,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.80", + "syn 2.0.89", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5028,22 +5645,24 @@ checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "wasm-encoder" -version = "0.32.0" +version = "0.222.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba64e81215916eaeb48fee292f29401d69235d62d8b8fd92a7b2844ec5ae5f7" +checksum = "3432682105d7e994565ef928ccf5856cf6af4ba3dddebedb737f61caed70f956" dependencies = [ "leb128", + "wasmparser 0.222.0", ] [[package]] name = "wasmer" -version = "4.4.0" +version = "5.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d920d06243e9f456c336c428a34560357dedf59d9febaae14f1995ac120cff6" +checksum = "998dea47d6bb6a8fc7dd8a17b13bf8de277e007229f270c67105cb67fc2d9657" dependencies = [ + "bindgen", "bytes", "cfg-if", - "derivative", + "cmake", "indexmap 1.9.3", "js-sys", "more-asserts", @@ -5051,9 +5670,11 @@ dependencies = [ "serde", "serde-wasm-bindgen 0.4.5", "shared-buffer", + "tar", "target-lexicon", - "thiserror", + "thiserror 1.0.69", "tracing", + "ureq", "wasm-bindgen", "wasmer-compiler", "wasmer-compiler-cranelift", @@ -5062,13 +5683,15 @@ dependencies = [ "wasmer-vm", "wat", "windows-sys 0.59.0", + "xz", + "zip", ] [[package]] name = "wasmer-compiler" -version = "4.4.0" +version = "5.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e01832173aa52345e480965f18c638a8a5a9e5e4d85a48675bdf1964147dc7f" +checksum = "082f48ba006cce2358f6c63d8dba732c9d12ba5833008c9ff2944290eb72c3dc" dependencies = [ "backtrace", "bytes", @@ -5080,29 +5703,32 @@ dependencies = [ "libc", "memmap2", "more-asserts", + "object 0.32.2", "region", "rkyv", "self_cell", "shared-buffer", "smallvec", - "thiserror", + "target-lexicon", + "thiserror 1.0.69", "wasmer-types", "wasmer-vm", - "wasmparser", + "wasmparser 0.216.0", "windows-sys 0.59.0", "xxhash-rust", ] [[package]] name = "wasmer-compiler-cranelift" -version = "4.4.0" +version = "5.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c1618f53b492cf6649beeb372930e376e0f52d9842c0c5eb5aa2b548251dab6" +checksum = "0272ad7daf59d43419cda9ae58b9cf8df6a115c8632fbb91c600892dff52f7a8" dependencies = [ "cranelift-codegen", "cranelift-entity", "cranelift-frontend", - "gimli 0.26.2", + "gimli 0.28.1", + "itertools 0.12.1", "more-asserts", "rayon", "smallvec", @@ -5114,11 +5740,11 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.4.0" +version = "5.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c5875633aea92153b6a561cb07363785ca9e07792ca6cd7c1cc371761001d8f" +checksum = "87b16fa0b2199083143705698ea9fc2ffd0328d7061f54e0e20ccd0ec2020466" dependencies = [ - "proc-macro-error", + "proc-macro-error2", "proc-macro2", "quote", "syn 1.0.109", @@ -5126,29 +5752,29 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.4.0" +version = "5.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb32f0d231b591e4c8a65e81d4647fa3180496d71a123d4948dba8551bba9c2" +checksum = "41b0b9b3242c1a6269e544401b1741a56502a5f79db8e1b318cacf1b34b2690d" dependencies = [ - "bytecheck", + "bytecheck 0.6.12", "enum-iterator", "enumset", "getrandom", "hex", - "indexmap 1.9.3", + "indexmap 2.7.0", "more-asserts", "rkyv", "sha2", "target-lexicon", - "thiserror", + "thiserror 1.0.69", "xxhash-rust", ] [[package]] name = "wasmer-vm" -version = "4.4.0" +version = "5.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e38e9301f5bb9f18da9cda4002d74d2cb6ac1f36dcf919fd77f91fca321fb1e5" +checksum = "92c7f8dbaceb0ab7901702e3c2bb43b09d2635aaa5ad39679c6560bcb9acde7c" dependencies = [ "backtrace", "cc", @@ -5156,10 +5782,9 @@ dependencies = [ "corosensei", "crossbeam-queue", "dashmap", - "derivative", "enum-iterator", "fnv", - "indexmap 1.9.3", + "indexmap 2.7.0", "lazy_static", "libc", "mach2", @@ -5167,39 +5792,53 @@ dependencies = [ "more-asserts", "region", "scopeguard", - "thiserror", + "thiserror 1.0.69", "wasmer-types", "windows-sys 0.59.0", ] [[package]] name = "wasmparser" -version = "0.121.2" +version = "0.216.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcdee6bea3619d311fb4b299721e89a986c3470f804b6d534340e412589028e3" +dependencies = [ + "ahash", + "bitflags 2.6.0", + "hashbrown 0.14.5", + "indexmap 2.7.0", + "semver", +] + +[[package]] +name = "wasmparser" +version = "0.222.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dbe55c8f9d0dbd25d9447a5a889ff90c0cc3feaa7395310d3d826b2c703eaab" +checksum = "4adf50fde1b1a49c1add6a80d47aea500c88db70551805853aa8b88f3ea27ab5" dependencies = [ "bitflags 2.6.0", - "indexmap 2.6.0", + "indexmap 2.7.0", "semver", ] [[package]] name = "wast" -version = "64.0.0" +version = "222.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a259b226fd6910225aa7baeba82f9d9933b6d00f2ce1b49b80fa4214328237cc" +checksum = "5ce7191f4b7da0dd300cc32476abae6457154e4625d9b1bc26890828a9a26f6e" dependencies = [ + "bumpalo", "leb128", "memchr", - "unicode-width", + "unicode-width 0.2.0", "wasm-encoder", ] [[package]] name = "wat" -version = "1.0.71" +version = "1.222.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53253d920ab413fca1c7dc2161d601c79b4fdf631d0ba51dd4343bf9b556c3f6" +checksum = "8fde61b4b52f9a84ae31b5e8902a2cd3162ea45d8bf564c729c3288fe52f4334" dependencies = [ "wast", ] @@ -5226,9 +5865,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.6" +version = "0.26.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" dependencies = [ "rustls-pki-types", ] @@ -5258,9 +5897,9 @@ dependencies = [ [[package]] name = "wide" -version = "0.7.28" +version = "0.7.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b828f995bf1e9622031f8009f8481a85406ce1f4d4588ff746d872043e855690" +checksum = "58e6db2670d2be78525979e9a5f9c69d296fd7d670549fe9ebf70f8708cb5019" dependencies = [ "bytemuck", "safe_arch", @@ -5342,19 +5981,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43dbb096663629518eb1dfa72d80243ca5a6aca764cae62a2df70af760a9be75" -dependencies = [ - "windows_aarch64_msvc 0.33.0", - "windows_i686_gnu 0.33.0", - "windows_i686_msvc 0.33.0", - "windows_x86_64_gnu 0.33.0", - "windows_x86_64_msvc 0.33.0", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -5425,12 +6051,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -5443,12 +6063,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e" - [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -5467,12 +6081,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0" - [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -5485,12 +6093,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784" - [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -5515,12 +6117,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa" - [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -5553,13 +6149,16 @@ dependencies = [ ] [[package]] -name = "wyz" -version = "0.5.1" +name = "write16" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "xattr" @@ -5578,6 +6177,15 @@ version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a5cbf750400958819fb6178eaa83bee5cd9c29a26a40cc241df8c70fdd46984" +[[package]] +name = "xz" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c887690ff2a2e233e8e49633461521f98ec57fbff9d59a884c9a4f04ec1da34" +dependencies = [ + "xz2", +] + [[package]] name = "xz2" version = "0.1.7" @@ -5587,6 +6195,30 @@ dependencies = [ "lzma-sys", ] +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -5605,7 +6237,28 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.80", + "syn 2.0.89", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", + "synstructure", ] [[package]] @@ -5613,3 +6266,110 @@ name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "zip" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae9c1ea7b3a5e1f4b922ff856a129881167511563dc219869afe3787fc0c1a45" +dependencies = [ + "aes", + "arbitrary", + "bzip2", + "constant_time_eq", + "crc32fast", + "crossbeam-utils", + "deflate64", + "displaydoc", + "flate2", + "hmac", + "indexmap 2.7.0", + "lzma-rs", + "memchr", + "pbkdf2", + "rand", + "sha1", + "thiserror 2.0.9", + "time", + "zeroize", + "zopfli", + "zstd", +] + +[[package]] +name = "zopfli" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" +dependencies = [ + "bumpalo", + "crc32fast", + "lockfree-object-pool", + "log", + "once_cell", + "simd-adler32", +] + +[[package]] +name = "zstd" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.13+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml index c6e949da3..3ed2ca875 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,11 @@ [workspace] resolver = "2" -members = ["crates/*"] +members = [ + "crates/*", + "apps/freenet-ping/app", + "apps/freenet-ping/types", + "apps/freenet-ping/contracts/ping" +] [workspace.dependencies] arrayvec = { version = "0.7", features = ["serde"] } @@ -22,7 +27,7 @@ serde_json = "1" serde_with = "3" tracing = "0.1" tracing-subscriber = "0.3" -wasmer = "4.3" +wasmer = "5.0.4" freenet-stdlib = { path = "./stdlib/rust/", features = ["unstable"] } # freenet-stdlib = { version = "0.0.8" } diff --git a/README.md b/README.md index b4d2540d4..f879ce77c 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,7 @@ will be easy to use, scalable, and secured through cryptography. To learn more about Freenet as a developer read [The User Manual](https://docs.freenet.org/). For an introduction to Freenet watch **Ian's -talk and Q&A** - [YouTube](https://youtu.be/d31jmv5Tx5k) / -[Vimeo](https://vimeo.com/manage/videos/740461100). +talk and Q&A** - [YouTube](https://youtu.be/d31jmv5Tx5k). ## Status diff --git a/apps/freenet-email-app/Cargo.lock b/apps/freenet-email-app/Cargo.lock index 76a3a6c7a..5a29cfa90 100644 --- a/apps/freenet-email-app/Cargo.lock +++ b/apps/freenet-email-app/Cargo.lock @@ -3649,9 +3649,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.21.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" dependencies = [ "futures-util", "log", @@ -3767,9 +3767,9 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.21.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" dependencies = [ "byteorder", "bytes", @@ -3780,7 +3780,6 @@ dependencies = [ "rand 0.8.5", "sha1", "thiserror", - "url", "utf-8", ] diff --git a/apps/freenet-email-app/contracts/inbox/src/lib.rs b/apps/freenet-email-app/contracts/inbox/src/lib.rs index d89573acb..b6979c153 100644 --- a/apps/freenet-email-app/contracts/inbox/src/lib.rs +++ b/apps/freenet-email-app/contracts/inbox/src/lib.rs @@ -395,13 +395,6 @@ impl ContractInterface for Inbox { } } - fn validate_delta( - _parameters: Parameters<'static>, - delta: StateDelta<'static>, - ) -> Result { - Ok(UpdateInbox::try_from(delta).ok().is_some()) - } - fn update_state( parameters: Parameters<'static>, state: State<'static>, diff --git a/apps/freenet-email-app/web/container/src/lib.rs b/apps/freenet-email-app/web/container/src/lib.rs index 18ffad32f..7c92e8f2f 100644 --- a/apps/freenet-email-app/web/container/src/lib.rs +++ b/apps/freenet-email-app/web/container/src/lib.rs @@ -13,14 +13,7 @@ impl ContractInterface for Contract { ) -> Result { Ok(ValidateResult::Valid) } - - fn validate_delta( - _parameters: Parameters<'static>, - _delta: StateDelta<'static>, - ) -> Result { - Ok(true) - } - + fn update_state( _parameters: Parameters<'static>, state: State<'static>, diff --git a/apps/freenet-microblogging/contracts/posts/src/lib.rs b/apps/freenet-microblogging/contracts/posts/src/lib.rs index 3ef747768..43f8964f9 100644 --- a/apps/freenet-microblogging/contracts/posts/src/lib.rs +++ b/apps/freenet-microblogging/contracts/posts/src/lib.rs @@ -115,14 +115,6 @@ impl ContractInterface for PostsFeed { PostsFeed::try_from(state).map(|_| ValidateResult::Valid) } - fn validate_delta( - _parameters: Parameters<'static>, - delta: StateDelta<'static>, - ) -> Result { - serde_json::from_slice::>(&delta).map_err(|_| ContractError::InvalidDelta)?; - Ok(true) - } - fn update_state( parameters: Parameters<'static>, state: State<'static>, @@ -262,24 +254,6 @@ mod test { Ok(()) } - #[test] - fn validate_delta() -> Result<(), Box> { - let json = r#"[ - { - "author": "IDG", - "date": "2022-05-10T00:00:00Z", - "title": "Lore ipsum", - "content": "..." - } - ]"#; - let valid = PostsFeed::validate_delta( - [].as_ref().into(), - StateDelta::from(json.as_bytes().to_vec()), - )?; - assert!(valid); - Ok(()) - } - #[test] fn update_state() -> Result<(), Box> { let state = r#"{"messages":[{"author":"IDG","content":"...", diff --git a/apps/freenet-microblogging/web/container/src/lib.rs b/apps/freenet-microblogging/web/container/src/lib.rs index aa6f1111b..fd4d80283 100644 --- a/apps/freenet-microblogging/web/container/src/lib.rs +++ b/apps/freenet-microblogging/web/container/src/lib.rs @@ -15,13 +15,6 @@ impl ContractInterface for Contract { Ok(ValidateResult::Valid) } - fn validate_delta( - _parameters: Parameters<'static>, - _delta: StateDelta<'static>, - ) -> Result { - Ok(true) - } - fn update_state( _parameters: Parameters<'static>, state: State<'static>, diff --git a/apps/freenet-ping/Cargo.lock b/apps/freenet-ping/Cargo.lock index 029ae1989..9562a1348 100644 --- a/apps/freenet-ping/Cargo.lock +++ b/apps/freenet-ping/Cargo.lock @@ -1293,9 +1293,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "becd34a233e7e31a3dbf7c7241b38320f57393dcae8e7324b0167d21b8e320b0" +checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" dependencies = [ "futures-util", "log", @@ -1367,9 +1367,9 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" +checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" dependencies = [ "byteorder", "bytes", diff --git a/apps/freenet-ping/README.md b/apps/freenet-ping/README.md index 7c09b79c8..41ba43cea 100644 --- a/apps/freenet-ping/README.md +++ b/apps/freenet-ping/README.md @@ -12,7 +12,7 @@ Note: This application is for testing and demonstration purposes only. It does n ## Code Overview -- **[app/src/main.rs](https://github.com/freenet/freenet-core/blob/main/apps/freenet-ping/app/src/main.rs)**: Command line app +- **[app/src/main.rs](https://github.com/freenet/freenet-core/blob/main/apps/freenet-ping/app/src/main.rs)**: Command line app that publishes the Ping contract, updates it periodically, and subscribes to changes. - **[contracts/ping/src/lib.rs](https://github.com/freenet/freenet-core/blob/main/apps/freenet-ping/contracts/ping/src/lib.rs)**: Ping contract implementation. - **[types/src/lib.rs](https://github.com/freenet/freenet-core/blob/main/apps/freenet-ping/types/src/lib.rs)**: Structs and other types that are shared between the command line app and the contract. @@ -75,54 +75,104 @@ Ensure you have the following prerequisites installed: rustup target add wasm32-unknown-unknown ``` -## Build Contract +## Setup Local Network -- Build the contract using the Freenet development tool: +### Configuration Files - ```bash - # You should be in freenet-core/apps/freenet-ping - cd contracts/ping - CARGO_TARGET_DIR=./target fdev build --features contract - cd - - ``` +This project includes two Makefiles to help you set up and run a local Freenet network: -## Run Freenet Locally +- `local-network.mk`: Manages the local Freenet network (nodes and gateways) +- `run-ping.mk`: Builds and runs the ping application -- Start the Freenet network locally: +### Start Local Network - ```bash - freenet local - ``` +1. First, clean any previous setup and create necessary directories: + ```bash + make -f local-network.mk clean + make -f local-network.mk setup N_GATEWAYS=1 N_NODES=2 + ``` -## Run Ping Application +2. Generate RSA keys for nodes and gateway: + ```bash + make -f local-network.mk generate-keys N_GATEWAYS=1 N_NODES=2 + ``` -- Run the ping application: +3. Start the gateway: + ```bash + make -f local-network.mk start-gateways N_GATEWAYS=1 + ``` - ```bash - cd app - cargo install --path . - freenet-ping - ``` - - You will see something like this: +4. Start the nodes: + ```bash + make -f local-network.mk start-nodes N_NODES=2 + ``` - ```bash - $ freenet-ping - 2024-05-14T15:33:20.685412Z INFO freenet_ping: 154: put ping contract successfully! key=Cuj4LbFao6vzZ5VtvZAKZ64Y99qNh7MpTUdaCcEkU4oR - 2024-05-14T15:33:20.729883Z INFO freenet_ping: 146: Hello, ubiquitous-letters! - 2024-05-14T15:33:22.154174Z INFO freenet_ping: 146: Hello, unwieldy-level! - 2024-05-14T15:33:23.668494Z INFO freenet_ping: 146: Hello, woozy-pin! - 2024-05-14T15:33:23.668504Z INFO freenet_ping: 146: Hello, conscious-crayon! - 2024-05-14T15:33:25.153814Z INFO freenet_ping: 146: Hello, unequal-kite! - 2024-05-14T15:33:26.668615Z INFO freenet_ping: 146: Hello, absorbed-wren! - 2024-05-14T15:33:26.668628Z INFO freenet_ping: 146: Hello, glib-disease! - 2024-05-14T15:33:28.154082Z INFO freenet_ping: 146: Hello, secret-floor! - 2024-05-14T15:33:29.669125Z INFO freenet_ping: 146: Hello, opposite-border! - 2024-05-14T15:33:29.669135Z INFO freenet_ping: 146: Hello, plucky-stretch! - ^C2024-05-14T15:33:29.713473Z INFO freenet_ping: 169: shutting down... - $ - ``` +5. Verify network status: + ```bash + make -f local-network.mk status + ``` + +The network will be configured with: +- 1 Gateway on port 3101 +- Node 1: WebSocket port 3001, Network port 3102 +- Node 2: WebSocket port 3002, Network port 3103 + +### Monitoring and Control + +You can monitor your network using these commands: + +```bash +# View logs from a specific node +make -f local-network.mk logs node=n1 # For node 1 +make -f local-network.mk logs node=gw1 # For gateway 1 + +# Stop specific node +make -f local-network.mk stop-node node=n1 +``` + +## Build and Run Ping + +1. View available options: + ```bash + make -f run-ping.mk help + ``` + +2. Build the contract and application: + ```bash + make -f run-ping.mk build + ``` + +3. Run ping towards node 1: + ```bash + make -f run-ping.mk run WS_PORT=3001 + ``` + +You will see something like this: +```bash +2024-05-14T15:33:20.685412Z INFO freenet_ping: 154: put ping contract successfully! key=Cuj4LbFao6vzZ5VtvZAKZ64Y99qNh7MpTUdaCcEkU4oR +2024-05-14T15:33:20.729883Z INFO freenet_ping: 146: Hello, ubiquitous-letters! +2024-05-14T15:33:22.154174Z INFO freenet_ping: 146: Hello, unwieldy-level! +2024-05-14T15:33:23.668494Z INFO freenet_ping: 146: Hello, woozy-pin! +``` + +You can customize the ping behavior with these parameters: +```bash +make -f run-ping.mk run WS_PORT=3001 FREQUENCY=2000 TTL=7200 +``` + +### Cleanup + +When you're done, clean up the network and build artifacts: + +```bash +# Stop the network +make -f local-network.mk stop + +# Clean up all files +make -f local-network.mk clean +make -f run-ping.mk clean +``` ## Feedback -If you encounter any issues please let us know in our [Matrix](https://matrix.to/#/#freenet-locutus:matrix.org) channel or by submitting a [Github Issue](https://github.com/freenet/freenet-core/issues). +If you encounter any issues please let us know in our [Matrix](https://matrix.to/#/#freenet-locutus:matrix.org) channel or by submitting a [Github Issue](https://github.com/freenet/freenet-core/issues). \ No newline at end of file diff --git a/apps/freenet-ping/app/Cargo.toml b/apps/freenet-ping/app/Cargo.toml index 3c73a2712..9271a9e0c 100644 --- a/apps/freenet-ping/app/Cargo.toml +++ b/apps/freenet-ping/app/Cargo.toml @@ -7,11 +7,11 @@ edition = "2021" clap = { version = "4", features = ["derive"] } futures = "0.3" tokio = { version = "1", features = ["full"] } -tokio-tungstenite = "0.23" +tokio-tungstenite = "0.24" tracing = { version = "0.1", features = ["log"] } tracing-subscriber = { version = "0.3", features = ["env-filter"] } freenet-stdlib = { workspace = true, features = ["net"] } names = { version = "0.14.0", default-features = false } serde_json = "1" -freenet-ping-types = { workspace = true, features = ["std", "clap"] } +freenet-ping-types = { path = "../types", features = ["std", "clap"] } chrono = { workspace = true, features = ["default"] } \ No newline at end of file diff --git a/apps/freenet-ping/app/src/main.rs b/apps/freenet-ping/app/src/main.rs index f54800cc0..87d4422b1 100644 --- a/apps/freenet-ping/app/src/main.rs +++ b/apps/freenet-ping/app/src/main.rs @@ -1,4 +1,3 @@ -use chrono::Utc; use clap::Parser; use freenet_ping_types::{Ping, PingContractOptions}; use freenet_stdlib::{ @@ -51,20 +50,22 @@ async fn main() -> Result<(), Box // try to fetch the old state from the host. client .send(ClientRequest::ContractOp(ContractRequest::Get { - key: contract_key.clone(), - fetch_contract: false, + key: contract_key, + return_contract_code: false, })) .await?; let resp = client.recv().await; + let mut is_subcribed = false; let mut local_state = match resp { Ok(HostResponse::ContractResponse(ContractResponse::GetResponse { key, contract: _, state, })) => { - if contract_key != key || state.is_empty() { + tracing::info!(key=%key, "fetched state successfully!"); + if contract_key != key || (*state).is_empty() { client .send(ClientRequest::ContractOp(ContractRequest::Put { contract: container, @@ -85,7 +86,7 @@ async fn main() -> Result<(), Box // the contract already put, so we subscribe to the contract. client .send(ClientRequest::ContractOp(ContractRequest::Subscribe { - key: contract_key.clone(), + key: contract_key, summary: None, })) .await?; @@ -94,6 +95,7 @@ async fn main() -> Result<(), Box } } _ => { + tracing::info!("failed to fetch state, putting a new contract..."); client .send(ClientRequest::ContractOp(ContractRequest::Put { contract: container, @@ -110,94 +112,103 @@ async fn main() -> Result<(), Box let mut generator = Generator::default(); loop { tokio::select! { - _ = send_tick.tick() => { - let name = generator.next().unwrap(); - let mut ping = Ping::default(); - ping.insert(name.clone()); - if let Err(e) = client.send(ClientRequest::ContractOp(ContractRequest::Update { - key: contract_key.clone(), - data: UpdateData::Delta(StateDelta::from(serde_json::to_vec(&ping).unwrap())), - })).await { - tracing::error!(err=%e, "failed to send update request"); - } - }, - res = client.recv() => { - match res { - Ok(resp) => match resp { - HostResponse::ContractResponse(resp) => { - match resp { - ContractResponse::PutResponse { key } => { - tracing::info!(key=%key, "put ping contract successfully!"); - // we successfully put the contract, so we subscribe to the contract. - if key == contract_key { - if let Err(e) = client.send(ClientRequest::ContractOp(ContractRequest::Subscribe { key, summary: None })).await { - tracing::error!(err=%e); - return Err(e.into()); - } - } - }, - ContractResponse::UpdateNotification { key, update } => { - if key == contract_key { - let mut handle_update = |state: &[u8]| { - let ping = if state.is_empty() { - Ping::default() - } else { - match serde_json::from_slice::(state) { - Ok(p) => p, - Err(e) => return Err(e), - } - }; - - for (name, created) in ping.iter() { - if !local_state.contains_key(name) && (*created + chrono::Duration::hours(1) > Utc::now()) { - tracing::info!("Hello, {}!", name); - } - } - - local_state.merge(ping, args.parameters.ttl); - Ok(()) - }; - - match update { - UpdateData::State(state) => { - if let Err(e) = handle_update(&state) { - tracing::error!(err=%e); - } - }, - UpdateData::Delta(delta) => { - if let Err(e) = handle_update(&delta) { - tracing::error!(err=%e); - } - }, - UpdateData::StateAndDelta { state, delta } => { - if let Err(e) = handle_update(&state) { - tracing::error!(err=%e); - } + _ = send_tick.tick() => { + if is_subcribed { + let name = generator.next().unwrap(); + let mut ping = Ping::default(); + ping.insert(name.clone()); + if let Err(e) = client.send(ClientRequest::ContractOp(ContractRequest::Update { + key: contract_key, + data: UpdateData::Delta(StateDelta::from(serde_json::to_vec(&ping).unwrap())), + })).await { + tracing::error!(err=%e, "failed to send update request"); + } + } + }, + res = client.recv() => { + match res { + Ok(resp) => match resp { + HostResponse::ContractResponse(resp) => { + match resp { + ContractResponse::PutResponse { key } => { + tracing::info!(key=%key, "put ping contract successfully!"); + // we successfully put the contract, so we subscribe to the contract. + if key == contract_key { + if let Err(e) = client.send(ClientRequest::ContractOp(ContractRequest::Subscribe { key, summary: None })).await { + tracing::error!(err=%e); + return Err(e.into()); + } + } + }, + ContractResponse::SubscribeResponse { key, .. } => { + tracing::debug!(key=%key, "Received subscribe response"); + if key == contract_key { + tracing::debug!(key=%key, "Marking as subscribed"); + is_subcribed = true; + } + }, + ContractResponse::UpdateNotification { key, update } => { + if key == contract_key { + let mut handle_update = |state: &[u8]| { + let ping = if state.is_empty() { + Ping::default() + } else { + match serde_json::from_slice::(state) { + Ok(p) => p, + Err(e) => return Err(e), + } + }; + + for (name, created) in ping.iter() { + if !local_state.contains_key(name) { + tracing::info!("Hello, {}! @ ({created})", name); + } + } + + local_state.merge(ping, args.parameters.ttl); + Ok(()) + }; + + match update { + UpdateData::State(state) => { + if let Err(e) = handle_update(&state) { + tracing::error!(err=%e); + } + }, + UpdateData::Delta(delta) => { + if let Err(e) = handle_update(&delta) { + tracing::error!(err=%e); + } + }, + UpdateData::StateAndDelta { state, delta } => { + if let Err(e) = handle_update(&state) { + tracing::error!(err=%e); + } - if let Err(e) = handle_update(&delta) { - tracing::error!(err=%e); + if let Err(e) = handle_update(&delta) { + tracing::error!(err=%e); + } + }, + _ => unreachable!("unknown state"), + } + } + }, + _ => {}, } - }, - _ => unreachable!("unknown state"), - } - } + }, + HostResponse::DelegateResponse { .. } => {}, + HostResponse::Ok => {}, + _ => unreachable!(), }, - _ => {}, - } - }, - HostResponse::DelegateResponse { .. } => {}, - HostResponse::Ok => {}, - _ => unreachable!(), - }, - Err(e) => { - tracing::error!(err=%e); - }, + Err(e) => { + tracing::error!(err=%e); + }, + } + } + _ = tokio::signal::ctrl_c() => { + tracing::info!("shutting down..."); + break; } - } - _ = tokio::signal::ctrl_c() => { - tracing::info!("shutting down..."); - break; - } } } Ok(()) diff --git a/apps/freenet-ping/contracts/ping/Cargo.toml b/apps/freenet-ping/contracts/ping/Cargo.toml index 53c14ff06..febc57a63 100644 --- a/apps/freenet-ping/contracts/ping/Cargo.toml +++ b/apps/freenet-ping/contracts/ping/Cargo.toml @@ -8,7 +8,8 @@ crate-type = ["cdylib"] [dependencies] freenet-stdlib.workspace = true -freenet-ping-types.workspace = true +freenet-stdlib.features = ["contract"] +freenet-ping-types.path = "../../types" serde_json = "1" [features] diff --git a/apps/freenet-ping/contracts/ping/src/lib.rs b/apps/freenet-ping/contracts/ping/src/lib.rs index 61c9a66a8..daf6680d4 100644 --- a/apps/freenet-ping/contracts/ping/src/lib.rs +++ b/apps/freenet-ping/contracts/ping/src/lib.rs @@ -20,21 +20,6 @@ impl ContractInterface for Contract { Ok(ValidateResult::Valid) } - fn validate_delta( - _parameters: Parameters<'static>, - delta: StateDelta<'static>, - ) -> Result { - let bytes = delta.as_ref(); - // allow empty delta - if bytes.is_empty() { - return Ok(true); - } - let _ = serde_json::from_slice::(bytes) - .map_err(|e| ContractError::Deser(e.to_string()))?; - - Ok(true) - } - fn update_state( parameters: Parameters<'static>, state: State<'static>, @@ -83,9 +68,9 @@ impl ContractInterface for Contract { _ => return Err(ContractError::InvalidUpdate), } } - return Ok(UpdateModification::valid(State::from( + Ok(UpdateModification::valid(State::from( serde_json::to_vec(&ping).map_err(|e| ContractError::Other(e.to_string()))?, - ))); + ))) } fn summarize_state( diff --git a/apps/freenet-ping/local-network.mk b/apps/freenet-ping/local-network.mk new file mode 100644 index 000000000..9785b2ec3 --- /dev/null +++ b/apps/freenet-ping/local-network.mk @@ -0,0 +1,236 @@ +# ========================================== +# Freenet Local Network Configuration +# ========================================== + +SHELL := /bin/bash + +# Directory Structure +# ------------------------------------------ +HOME_DIR := $(HOME) +BASE_DIR := $(HOME_DIR)/.cache/freenet +KEYS_DIR := $(BASE_DIR)/keys +PID_DIR := $(BASE_DIR)/pids +LOGS_DIR := $(BASE_DIR)/logs + +# Network Configuration +# ------------------------------------------ +N_NODES := 2 +N_GATEWAYS := 1 +BASE_PORT := 3100 +WS_BASE_PORT:= 3000 + +# Files +# ------------------------------------------ +GW_CONFIG := $(BASE_DIR)/gateways.toml + +# Environment +# ------------------------------------------ +ENV_VARS := RUST_BACKTRACE=1 RUST_LOG="info,freenet=debug,freenet-stdlib=debug,fdev=debug" +FREENET_CORE_PATH := ../../crates/core/ + +# Log Command with ANSI color removal +# ------------------------------------------ +define LOG_CMD +1> >(stdbuf -o0 sed 's/\x1b\[[0-9;]*m//g' >> $(1)) 2>&1 +endef + +# PHONY Targets +# ------------------------------------------ +.PHONY: help setup start stop clean logs status +.DEFAULT_GOAL := help + +# Help Command +# ------------------------------------------ +help: + @echo "Freenet Local Network Management" + @echo "" + @echo "Main Commands:" + @echo " make -f local-network.mk setup - Create directories and generate keys" + @echo " make -f local-network.mk start - Start network (gateways and nodes)" + @echo " make -f local-network.mk stop - Stop all processes" + @echo " make -f local-network.mk clean - Clean all files and stop processes" + @echo " make -f local-network.mk status - Show network status" + @echo "" + @echo "Node Management:" + @echo " make -f local-network.mk logs node=n1 - Show logs for node 1" + @echo " make -f local-network.mk logs node=gw1 - Show logs for gateway 1" + @echo " make -f local-network.mk stop-node node=n1- Stop node 1" + @echo "" + @echo "Configuration:" + @echo " N_NODES=$(N_NODES), N_GATEWAYS=$(N_GATEWAYS)" + @echo " Base ports: network=$(BASE_PORT), websocket=$(WS_BASE_PORT)" + +# Setup Commands +# ------------------------------------------ +setup: build-freenet create-dirs generate-keys + +build-freenet: + @echo "→ Building Freenet..." + @cargo install --path $(FREENET_CORE_PATH) --features "local-simulation" + +create-dirs: + @echo "→ Creating directories..." + @mkdir -p $(KEYS_DIR) $(PID_DIR) $(LOGS_DIR) + @for i in $$(seq 1 $(N_GATEWAYS)); do mkdir -p $(BASE_DIR)/gw$$i; done + @for i in $$(seq 1 $(N_NODES)); do mkdir -p $(BASE_DIR)/n$$i; done + +generate-keys: + @echo "→ Generating RSA keys..." + @for i in $$(seq 1 $(N_GATEWAYS)); do \ + openssl genpkey -algorithm RSA -out $(KEYS_DIR)/gw$${i}_private_key.pem && \ + openssl rsa -pubout -in $(KEYS_DIR)/gw$${i}_private_key.pem \ + -out $(KEYS_DIR)/gw$${i}_public_key.pem; \ + done + @for i in $$(seq 1 $(N_NODES)); do \ + openssl genpkey -algorithm RSA -out $(BASE_DIR)/n$$i/n$${i}_private_key.pem && \ + openssl rsa -pubout -in $(BASE_DIR)/n$$i/n$${i}_private_key.pem \ + -out $(BASE_DIR)/n$$i/n$${i}_public_key.pem; \ + done + +# Network Management +# ------------------------------------------ +start: start-gateways start-nodes + @echo "→ Network started successfully" + @$(MAKE) -f local-network.mk status + +start-gateways: generate-gw-config + @echo "→ Starting gateways..." + @for i in $$(seq 1 $(N_GATEWAYS)); do \ + port=$$(( $(BASE_PORT) + $$i )); \ + ($(ENV_VARS) freenet network \ + --is-gateway \ + --public-network-address 127.0.0.1 \ + --public-network-port $$port \ + --db-dir $(BASE_DIR)/gw$$i \ + --transport-keypair $(KEYS_DIR)/gw$${i}_private_key.pem \ + --network-port $$port $(call LOG_CMD,$(LOGS_DIR)/gw$$i.log)) & \ + echo $$! > $(PID_DIR)/gw$$i.pid; \ + echo " Gateway $$i: port=$$port (PID: $$!)"; \ + done + @echo " Waiting 2 seconds for gateways to initialize..." + @sleep 2 + +start-nodes: + @echo "→ Starting nodes..." + @for i in $$(seq 1 $(N_NODES)); do \ + network_port=$$(( $(BASE_PORT) + $(N_GATEWAYS) + $$i )); \ + ws_port=$$(( $(WS_BASE_PORT) + $$i )); \ + public_port=$$(( $(WS_BASE_PORT) + $(N_NODES) + $$i )); \ + ($(ENV_VARS) freenet network \ + --config-dir $(BASE_DIR) \ + --ws-api-port $$ws_port \ + --public-network-port $$public_port \ + --db-dir $(BASE_DIR)/n$$i \ + --network-port $$network_port \ + --transport-keypair $(BASE_DIR)/n$$i/n$${i}_private_key.pem $(call LOG_CMD,$(LOGS_DIR)/n$$i.log)) & \ + echo $$! > $(PID_DIR)/n$$i.pid; \ + echo " Node $$i: network=$$network_port, ws=$$ws_port, public=$$public_port (PID: $$!)"; \ + if [ $$i -lt $(N_NODES) ]; then \ + echo " Waiting 1 second before starting next node..."; \ + sleep 1; \ + fi; \ + done + +generate-gw-config: + @echo "→ Generating gateway configuration..." + @echo "# Freenet Gateway Configuration" > $(GW_CONFIG) + @for i in $$(seq 1 $(N_GATEWAYS)); do \ + port=$$(( $(BASE_PORT) + $$i )); \ + echo "" >> $(GW_CONFIG); \ + echo "[[gateways]]" >> $(GW_CONFIG); \ + echo "address = { host_address = \"127.0.0.1:$$port\" }" >> $(GW_CONFIG); \ + echo "public_key = \"$(KEYS_DIR)/gw$${i}_public_key.pem\"" >> $(GW_CONFIG); \ + done + +# Monitoring and Control +# ------------------------------------------ +logs: + @if [ -z "$(node)" ]; then \ + echo "Usage: make -f local-network.mk logs node= (e.g., n1, gw1)"; \ + exit 1; \ + fi + @if [ ! -f "$(LOGS_DIR)/$(node).log" ]; then \ + echo "No logs found for $(node)"; \ + exit 1; \ + fi + @tail -f $(LOGS_DIR)/$(node).log + +status: + @echo "Network Status:" + @echo "→ Gateways:" + @for i in $$(seq 1 $(N_GATEWAYS)); do \ + if [ -f "$(PID_DIR)/gw$$i.pid" ]; then \ + pid=$$(cat "$(PID_DIR)/gw$$i.pid"); \ + if kill -0 $$pid 2>/dev/null; then \ + echo " Gateway $$i: Running (PID: $$pid)"; \ + else \ + echo " Gateway $$i: Crashed (PID: $$pid)"; \ + fi; \ + else \ + echo " Gateway $$i: Stopped"; \ + fi; \ + done + @echo "→ Nodes:" + @for i in $$(seq 1 $(N_NODES)); do \ + if [ -f "$(PID_DIR)/n$$i.pid" ]; then \ + pid=$$(cat "$(PID_DIR)/n$$i.pid"); \ + if kill -0 $$pid 2>/dev/null; then \ + echo " Node $$i: Running (PID: $$pid)"; \ + else \ + echo " Node $$i: Crashed (PID: $$pid)"; \ + fi; \ + else \ + echo " Node $$i: Stopped"; \ + fi; \ + done + +# Cleanup +# ------------------------------------------ +stop-node: + @if [ -z "$(node)" ]; then \ + echo "Usage: make -f local-network.mk stop-node node= (e.g., n1, gw1)"; \ + exit 1; \ + fi + @if [ -f "$(PID_DIR)/$(node).pid" ]; then \ + pid=$$(cat "$(PID_DIR)/$(node).pid"); \ + kill $$pid 2>/dev/null || true; \ + rm "$(PID_DIR)/$(node).pid"; \ + echo "→ $(node) stopped (PID: $$pid)"; \ + else \ + echo "→ $(node) is not running"; \ + fi + +stop: + @echo "→ Stopping all processes..." + @# Stop processes using PID files + @for pid_file in $(PID_DIR)/*.pid; do \ + if [ -f "$$pid_file" ]; then \ + pid=$$(cat "$$pid_file"); \ + if kill -0 $$pid 2>/dev/null; then \ + kill $$pid 2>/dev/null || true; \ + echo " Stopped process PID: $$pid"; \ + else \ + echo " Process PID $$pid not running"; \ + fi; \ + rm -f "$$pid_file"; \ + fi; \ + done; \ + # Then find and stop any remaining freenet processes, excluding make and the current shell + @current_pid=$$$$; \ + make_pid=$$(ps -o ppid= -p $$current_pid | tr -d ' '); \ + pids=$$(pgrep -f "freenet network" | grep -v $$current_pid | grep -v $$make_pid || true); \ + if [ ! -z "$$pids" ]; then \ + echo " Found additional freenet processes..."; \ + for pid in $$pids; do \ + if kill -0 $$pid 2>/dev/null; then \ + kill $$pid 2>/dev/null || true; \ + echo " Stopped additional process (PID: $$pid)"; \ + fi; \ + done; \ + fi + @echo "→ All processes stopped" + +clean: stop + @echo "→ Cleaning all files..." + @rm -rf $(BASE_DIR) + @echo "→ Cleanup complete" diff --git a/apps/freenet-ping/run-ping.mk b/apps/freenet-ping/run-ping.mk new file mode 100644 index 000000000..15235399b --- /dev/null +++ b/apps/freenet-ping/run-ping.mk @@ -0,0 +1,115 @@ +# ========================================== +# Freenet Ping Application Builder +# ========================================== + +# Use bash shell +SHELL := /bin/bash + +# Project Structure +# ------------------------------------------ +PROJECT_ROOT := $(shell pwd) +PING_CONTRACT := $(PROJECT_ROOT)/contracts/ping +PING_APP := $(PROJECT_ROOT)/app +BUILD_DIR := $(PING_CONTRACT)/target + + +# Log Command with ANSI color removal +# ------------------------------------------ +define LOG_CMD +1> >(stdbuf -o0 sed 's/\x1b\[[0-9;]*m//g' >> $(1)) 2>&1 +endef + +# Build Tools +# ------------------------------------------ +CARGO := cargo +FDEV := fdev + +# Ping Configuration +# ------------------------------------------ +WS_PORT ?= 3001 # WebSocket port for node connection +LOG_LEVEL ?= debug # Logging level +FREQUENCY ?= 1000ms # Update frequency with time unit +TTL ?= 3600s # Time to live with time unit + +# PHONY Targets +# ------------------------------------------ +.PHONY: help all verify build clean run install +.DEFAULT_GOAL := help + +# Help Command +# ------------------------------------------ +help: + @echo "Freenet Ping Application Management" + @echo "" + @echo "Main Commands:" + @echo " make -f run-ping.mk build - Build contract and application" + @echo " make -f run-ping.mk install - Install freenet-ping tool" + @echo " make -f run-ping.mk run - Run freenet-ping tool" + @echo " make -f run-ping.mk clean - Clean build artifacts" + @echo "" + @echo "Configuration:" + @echo " WS_PORT=$(WS_PORT) - WebSocket port for node connection" + @echo " FREQUENCY=$(FREQUENCY) - Update frequency (e.g., 1000ms, 1s)" + @echo " TTL=$(TTL) - Time to live (e.g., 3600s, 1h)" + @echo "" + @echo "Example:" + @echo " make -f run-ping.mk run WS_PORT=3002 FREQUENCY=2000ms TTL=7200s" + +# Verification +# ------------------------------------------ +verify: + @echo "→ Verifying project structure..." + @if [ ! -d "$(PING_CONTRACT)" ]; then \ + echo "Error: contracts/ping directory not found"; \ + echo "Please run from freenet-ping root directory"; \ + exit 1; \ + fi + @if [ ! -d "$(PING_APP)" ]; then \ + echo "Error: app directory not found"; \ + echo "Please run from freenet-ping root directory"; \ + exit 1; \ + fi + @echo "✓ Project structure verified" + +# Build Commands +# ------------------------------------------ +build: verify build-contract build-app + +build-contract: + @echo "→ Building ping contract..." + @mkdir -p $(BUILD_DIR) + @cd $(PING_CONTRACT) && CARGO_TARGET_DIR=$(BUILD_DIR) $(FDEV) build --features contract + @echo "✓ Contract built successfully" + +build-app: + @echo "→ Building ping application..." + @cd $(PING_APP) && $(CARGO) build + @echo "✓ Application built successfully" + +# Install Application +# ------------------------------------------ +install: build + @echo "→ Installing freenet-ping tool..." + @cd $(PING_APP) && $(CARGO) install --path . + @echo "✓ Tool installed successfully" + +# Run Application +# ------------------------------------------ +run: install + @echo "→ Running freenet-ping..." + @echo " WebSocket Port: $(WS_PORT)" + @echo " Update Frequency: $(FREQUENCY)" + @echo " TTL: $(TTL)" + @freenet-ping \ + --host "localhost:$(WS_PORT)" \ + --log-level $(LOG_LEVEL) \ + --frequency $(FREQUENCY) \ + --ttl $(TTL) + +# Cleanup +# ------------------------------------------ +clean: + @echo "→ Cleaning build artifacts..." + @cd $(PING_APP) && $(CARGO) clean + @rm -rf $(BUILD_DIR) + @echo "✓ Clean completed" \ No newline at end of file diff --git a/apps/freenet-ping/types/src/lib.rs b/apps/freenet-ping/types/src/lib.rs index 19daa99b3..6437c0668 100644 --- a/apps/freenet-ping/types/src/lib.rs +++ b/apps/freenet-ping/types/src/lib.rs @@ -79,7 +79,6 @@ impl Ping { } } - #[cfg(test)] mod tests { use super::*; @@ -91,8 +90,12 @@ mod tests { ping.insert("Bob".to_string()); let mut other = Ping::new(); - other.from.insert("Alice".to_string(), Utc::now() - Duration::from_secs(6)); - other.from.insert("Charlie".to_string(), Utc::now() - Duration::from_secs(6)); + other + .from + .insert("Alice".to_string(), Utc::now() - Duration::from_secs(6)); + other + .from + .insert("Charlie".to_string(), Utc::now() - Duration::from_secs(6)); ping.merge(other, Duration::from_secs(5)); @@ -109,8 +112,12 @@ mod tests { ping.insert("Bob".to_string()); let mut other = Ping::new(); - other.from.insert("Alice".to_string(), Utc::now() - Duration::from_secs(4)); - other.from.insert("Charlie".to_string(), Utc::now() - Duration::from_secs(4)); + other + .from + .insert("Alice".to_string(), Utc::now() - Duration::from_secs(4)); + other + .from + .insert("Charlie".to_string(), Utc::now() - Duration::from_secs(4)); ping.merge(other, Duration::from_secs(5)); diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 6796e0fb5..796130f59 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -92,9 +92,7 @@ tracing = "0.1" [features] default = ["redb", "trace", "websocket"] -local-mode = [] local-simulation = [] -network-mode = [] sqlite = ["sqlx"] trace = ["tracing-subscriber"] trace-ot = ["opentelemetry-jaeger", "trace", "tracing-opentelemetry", "opentelemetry-otlp"] diff --git a/crates/core/src/client_events.rs b/crates/core/src/client_events.rs index 674995f01..092255b81 100644 --- a/crates/core/src/client_events.rs +++ b/crates/core/src/client_events.rs @@ -1,15 +1,28 @@ //! Clients events related logic and type definitions. For example, receival of client events from applications throught the HTTP gateway. -use freenet_stdlib::client_api::ClientRequest; -use freenet_stdlib::client_api::{ClientError, ContractResponse, HostResponse}; -use futures::future::BoxFuture; +use freenet_stdlib::{ + client_api::{ + ClientError, ClientRequest, ContractRequest, ContractResponse, ErrorKind, HostResponse, + QueryResponse, + }, + prelude::*, +}; +use futures::stream::FuturesUnordered; +use futures::{future::BoxFuture, StreamExt}; use std::fmt::Debug; use std::fmt::Display; use std::sync::atomic::AtomicUsize; use std::sync::Arc; +use tracing::Instrument; use serde::{Deserialize, Serialize}; -use tokio::sync::mpsc::UnboundedSender; +use tokio::sync::mpsc::{self, UnboundedSender}; + +use crate::contract::{ClientResponsesReceiver, ContractHandlerEvent}; +use crate::message::{NodeEvent, QueryResult}; +use crate::node::OpManager; +use crate::operations::{get, put, update, OpError}; +use crate::{config::GlobalExecutor, contract::StoreResponse}; pub(crate) mod combinator; #[cfg(feature = "websocket")] @@ -33,11 +46,6 @@ static CLIENT_ID: AtomicUsize = AtomicUsize::new(1); impl ClientId { pub const FIRST: Self = ClientId(0); - #[cfg(test)] - pub(crate) const fn new(id: usize) -> ClientId { - Self(id) - } - pub fn next() -> Self { ClientId(CLIENT_ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst)) } @@ -152,6 +160,296 @@ pub trait ClientEventsProxy { ) -> BoxFuture>; } +/// Process client events. +pub async fn client_event_handling( + op_manager: Arc, + mut client_events: ClientEv, + mut client_responses: ClientResponsesReceiver, + node_controller: tokio::sync::mpsc::Sender, +) where + ClientEv: ClientEventsProxy + Send + 'static, +{ + let mut callbacks = FuturesUnordered::new(); + loop { + tokio::select! { + client_request = client_events.recv() => { + let req = match client_request { + Ok(request) => { + tracing::debug!(%request, "got client request event"); + request + } + Err(error) if matches!(error.kind(), ErrorKind::Shutdown) => { + node_controller.send(NodeEvent::Disconnect { cause: None }).await.ok(); + break; + } + Err(error) => { + tracing::debug!(%error, "client error"); + continue; + } + }; + // fixme: only allow in certain modes (e.g. while testing) + if let ClientRequest::Disconnect { cause } = &*req.request { + node_controller.send(NodeEvent::Disconnect { cause: cause.clone() }).await.ok(); + break; + } + let cli_id = req.client_id; + if let Some(mut cb) = process_open_request(req, op_manager.clone()).await { + callbacks.push(async move { cb.recv().await.map(|r| (cli_id, r)) }); + } + } + res = client_responses.recv() => { + if let Some((cli_id, res)) = res { + if let Ok(result) = &res { + tracing::debug!(%result, "sending client response"); + } + if let Err(err) = client_events.send(cli_id, res).await { + tracing::debug!("channel closed: {err}"); + break; + } + } + } + res = callbacks.next(), if !callbacks.is_empty() => { + if let Some(Some((cli_id, res))) = res { + let res = match res { + QueryResult::Connections(conns) => { + Ok(HostResponse::QueryResponse(QueryResponse::ConnectedPeers { + peers: conns.into_iter().map(|p| (p.pub_key.to_string(), p.addr)).collect() } + )) + } + QueryResult::GetResult { key, state, contract } => { + Ok(HostResponse::ContractResponse(ContractResponse::GetResponse { + key, + state, + contract, + })) + } + }; + if let Err(err) = client_events.send(cli_id, res).await { + tracing::debug!("channel closed: {err}"); + break; + } + } + } + } + } +} + +#[inline] +async fn process_open_request( + mut request: OpenRequest<'static>, + op_manager: Arc, +) -> Option> { + let (callback_tx, callback_rx) = if matches!( + &*request.request, + ClientRequest::NodeQueries(_) | ClientRequest::ContractOp(ContractRequest::Get { .. }) + ) { + let (tx, rx) = mpsc::channel(1); + (Some(tx), Some(rx)) + } else { + (None, None) + }; + + // this will indirectly start actions on the local contract executor + let fut = async move { + let client_id = request.client_id; + + // fixme: communicate back errors in this loop to the client somehow + let subscription_listener: Option> = + request.notification_channel.take(); + match *request.request { + ClientRequest::ContractOp(ops) => { + match ops { + ContractRequest::Put { + state, + contract, + related_contracts, + } => { + let peer_id = op_manager + .ring + .connection_manager + .get_peer_key() + .expect("Peer id not found at put op, it should be set"); + // Initialize a put op. + tracing::debug!( + this_peer = %peer_id, + "Received put from user event", + ); + let op = put::start_op( + contract, + related_contracts, + state, + op_manager.ring.max_hops_to_live, + ); + let op_id = op.id; + let _ = op_manager + .ch_outbound + .waiting_for_transaction_result(op_id, client_id) + .await; + if let Err(err) = put::request_put(&op_manager, op).await { + tracing::error!("{}", err); + } + } + ContractRequest::Update { key, data } => { + let peer_id = op_manager + .ring + .connection_manager + .get_peer_key() + .expect("Peer id not found at update op, it should be set"); + tracing::debug!( + this_peer = %peer_id, + "Received update from user event", + ); + + let related_contracts = RelatedContracts::default(); + + let new_state = match op_manager + .notify_contract_handler(ContractHandlerEvent::UpdateQuery { + key, + data, + related_contracts: related_contracts.clone(), + }) + .await + { + Ok(ContractHandlerEvent::UpdateResponse { + new_value: Ok(new_val), + }) => Ok(new_val), + Ok(ContractHandlerEvent::UpdateResponse { + new_value: Err(err), + }) => Err(OpError::from(err)), + Err(err) => Err(err.into()), + Ok(_) => Err(OpError::UnexpectedOpState), + } + .expect("update query failed"); + + let op = update::start_op(key, new_state, related_contracts); + + let _ = op_manager + .ch_outbound + .waiting_for_transaction_result(op.id, client_id) + .await; + + if let Err(err) = update::request_update(&op_manager, op).await { + tracing::error!("request update error {}", err) + } + } + ContractRequest::Get { + key, + return_contract_code, + } => { + let peer_id = op_manager + .ring + .connection_manager + .get_peer_key() + .expect("Peer id not found at get op, it should be set"); + let (state, contract) = match op_manager + .notify_contract_handler(ContractHandlerEvent::GetQuery { + key, + return_contract_code, + }) + .await + { + Ok(ContractHandlerEvent::GetResponse { + response: Ok(StoreResponse { state, contract }), + .. + }) => Ok((state, contract)), + Ok(ContractHandlerEvent::GetResponse { + response: Err(err), .. + }) => Err(err.into()), + Err(err) => Err(err.into()), + Ok(_) => Err(OpError::UnexpectedOpState), + } + .expect("get query failed"); + + if (!return_contract_code && state.is_some()) + || (return_contract_code && state.is_some() && contract.is_some()) + { + if let Some(state) = state { + tracing::debug!( + this_peer = %peer_id, + "Contract found, returning get result", + ); + callback_tx + .unwrap() + .send(QueryResult::GetResult { + key, + state, + contract, + }) + .await + .ok(); + } + } else { + // Initialize a get op. + tracing::debug!( + this_peer = %peer_id, + "Contract not found, starting get op", + ); + let op = get::start_op(key, return_contract_code); + let _ = op_manager + .ch_outbound + .waiting_for_transaction_result(op.id, client_id) + .await; + if let Err(err) = get::request_get(&op_manager, op, vec![]).await { + tracing::error!("{}", err); + } + } + } + ContractRequest::Subscribe { key, summary } => { + let op_id = + match crate::node::subscribe(op_manager.clone(), key, Some(client_id)) + .await + { + Ok(op_id) => op_id, + Err(err) => { + tracing::error!("Subscribe error: {}", err); + return; + } + }; + let Some(subscriber_listener) = subscription_listener else { + tracing::error!(%op_id, %client_id, "No subscriber listener"); + return; + }; + let _ = op_manager + .notify_contract_handler(ContractHandlerEvent::RegisterSubscriberListener { + key, + client_id, + summary, + subscriber_listener, + }) + .await.inspect_err(|err| { + tracing::error!(%op_id, %client_id, "Register subscriber listener error: {}", err); + }); + let _ = op_manager + .ch_outbound + .waiting_for_transaction_result(op_id, client_id) + .await; + } + _ => { + tracing::error!("Op not supported"); + } + } + } + ClientRequest::DelegateOp(_op) => todo!("FIXME: delegate op"), + ClientRequest::Disconnect { .. } => unreachable!(), + ClientRequest::NodeQueries(_) => { + tracing::debug!("Received node queries from user event"); + let _ = op_manager + .notify_node_event(NodeEvent::QueryConnections { + callback: callback_tx.expect("should be set"), + }) + .await; + } + _ => { + tracing::error!("Op not supported"); + } + } + }; + GlobalExecutor::spawn(fut.instrument( + tracing::info_span!(parent: tracing::Span::current(), "process_client_request"), + )); + callback_rx +} + pub(crate) mod test { use std::{ collections::{HashMap, HashSet}, @@ -510,7 +808,7 @@ pub(crate) mod test { let key = contract.key(); let request = ContractRequest::Get { key, - fetch_contract: true, + return_contract_code: true, }; return Some(request.into()); } diff --git a/crates/core/src/client_events/combinator.rs b/crates/core/src/client_events/combinator.rs index 145d4b858..f80219361 100644 --- a/crates/core/src/client_events/combinator.rs +++ b/crates/core/src/client_events/combinator.rs @@ -1,36 +1,33 @@ -use std::future::Future; -use std::pin::Pin; -use std::task::Context; -use std::{collections::HashMap, task::Poll}; +use std::collections::HashMap; use freenet_stdlib::client_api::{ErrorKind, HostResponse}; use futures::future::BoxFuture; -use futures::task::AtomicWaker; -use futures::FutureExt; +use futures::stream::FuturesUnordered; +use futures::{FutureExt, StreamExt}; use tokio::sync::mpsc::{channel, Receiver, Sender}; use super::{BoxedClient, ClientError, ClientId, HostResult, OpenRequest}; type HostIncomingMsg = Result, ClientError>; +type ClientEventsFut = + BoxFuture<'static, (usize, Receiver, Option)>; + /// This type allows combining different sources of events into one and interoperation between them. pub struct ClientEventsCombinator { + pending_futs: FuturesUnordered, /// receiving end of the different client applications from the node clients: [Sender<(ClientId, HostResult)>; N], - /// receiving end of the host node from the different client applications - hosts_rx: [Receiver; N], /// a map of the individual protocols, external, sending client events ids to an internal list of ids external_clients: [HashMap; N], /// a map of the external id to which protocol it belongs (represented by the index in the array) /// and the original id (reverse of indexes) internal_clients: HashMap, - #[allow(clippy::type_complexity)] - pend_futs: - [Option> + Sync + Send + 'static>>>; N], } impl ClientEventsCombinator { pub fn new(clients: [BoxedClient; N]) -> Self { + let pending_futs = FuturesUnordered::new(); let channels = clients.map(|client| { let (tx, rx) = channel(1); let (tx_host, rx_host) = channel(1); @@ -43,49 +40,37 @@ impl ClientEventsCombinator { clients[i] = Some(tx); hosts_rx[i] = Some(rx_host); } - let hosts_rx = hosts_rx.map(|h| h.unwrap()); let external_clients = [(); N].map(|_| HashMap::new()); + + for (i, rx) in hosts_rx.iter_mut().enumerate() { + let Some(mut rx) = rx.take() else { + continue; + }; + pending_futs.push( + async move { + let res = rx.recv().await; + (i, rx, res) + } + .boxed(), + ); + } + Self { clients: clients.map(|c| c.unwrap()), - hosts_rx, external_clients, internal_clients: HashMap::new(), - pend_futs: [(); N].map(|_| None), + pending_futs, } } } impl super::ClientEventsProxy for ClientEventsCombinator { - fn recv<'a>(&'_ mut self) -> BoxFuture<'_, Result, ClientError>> { - Box::pin(async { - let mut futs_opt = [(); N].map(|_| None); - let pend_futs = &mut self.pend_futs; - for (i, pend) in pend_futs.iter_mut().enumerate() { - let fut = &mut futs_opt[i]; - if let Some(pend_fut) = pend.take() { - *fut = Some(pend_fut); - } else { - // this receiver ain't awaiting, queue a new one - // SAFETY: is safe here to extend the lifetime since clients are required to be 'static - // and we take ownership, so they will be alive for the duration of the program - let f = Box::pin(self.hosts_rx[i].recv()) - as Pin + Send + Sync + '_>>; + fn recv(&mut self) -> BoxFuture<'_, Result, ClientError>> { + async { + let Some((idx, mut rx, res)) = self.pending_futs.next().await else { + unreachable!(); + }; - type ExtendedLife<'a, 'b> = Pin< - Box< - dyn Future, ClientError>>> - + Send - + Sync - + 'b, - >, - >; - let new_pend = unsafe { - std::mem::transmute::, ExtendedLife<'_, '_>>(f) - }; - *fut = Some(new_pend); - } - } - let (res, idx, mut others) = select_all(futs_opt.map(|f| f.unwrap())).await; let res = res .map(|res| { match res { @@ -95,19 +80,15 @@ impl super::ClientEventsProxy for ClientEventsCombinator { notification_channel, token, }) => { - tracing::debug!( - "received request; internal_id={external}; req={request}" - ); - let id = - *self.external_clients[idx] - .entry(external) - .or_insert_with(|| { - // add a new mapped external client id - let internal = ClientId::next(); - self.internal_clients.insert(internal, (idx, external)); - internal - }); - + let id = *self.external_clients[idx] + .entry(external) + .or_insert_with(|| { + // add a new mapped external client id + let internal = ClientId::next(); + self.internal_clients.insert(internal, (idx, external)); + internal + }); + tracing::debug!("received request for proxy #{idx}; internal_id={id}; external_id={external}; req={request}"); Ok(OpenRequest { client_id: id, request, @@ -119,15 +100,18 @@ impl super::ClientEventsProxy for ClientEventsCombinator { } }) .unwrap_or_else(|| Err(ErrorKind::TransportProtocolDisconnect.into())); - // place back futs - debug_assert!(pend_futs.iter().all(|f| f.is_none())); - debug_assert_eq!( - others.iter().filter(|a| a.is_some()).count(), - pend_futs.len() - 1 + + self.pending_futs.push( + async move { + let res = rx.recv().await; + (idx, rx, res) + } + .boxed(), ); - std::mem::swap(pend_futs, &mut others); + res - }) + } + .boxed() } fn send<'a>( @@ -135,7 +119,7 @@ impl super::ClientEventsProxy for ClientEventsCombinator { internal: ClientId, response: Result, ) -> BoxFuture<'_, Result<(), ClientError>> { - Box::pin(async move { + async move { let (idx, external) = self .internal_clients .get(&internal) @@ -145,7 +129,8 @@ impl super::ClientEventsProxy for ClientEventsCombinator { .await .map_err(|_| ErrorKind::TransportProtocolDisconnect)?; Ok(()) - }) + } + .boxed() } } @@ -189,62 +174,10 @@ async fn client_fn( tracing::error!("Client shut down"); } -/// An optimized for the use case version of `futures::select_all` which keeps ordering. -#[must_use = "futures do nothing unless you `.await` or poll them"] -struct SelectAll { - waker: AtomicWaker, - inner: [Option; N], -} - -impl Unpin for SelectAll {} - -impl Future for SelectAll { - type Output = (Fut::Output, usize, [Option; N]); - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - macro_rules! recv { - () => { - let item = self - .inner - .iter_mut() - .enumerate() - .find_map(|(i, f)| { - f.as_mut().map(|f| match f.poll_unpin(cx) { - Poll::Pending => None, - Poll::Ready(e) => Some((i, e)), - }) - }) - .flatten(); - match item { - Some((idx, res)) => { - self.inner[idx] = None; - let rest = std::mem::replace(&mut self.inner, [(); N].map(|_| None)); - return Poll::Ready((res, idx, rest)); - } - None => {} - } - }; - } - recv!(); - self.waker.register(cx.waker()); - recv!(); - Poll::Pending - } -} - -fn select_all(iter: [F; N]) -> SelectAll -where - F: Future + Unpin, -{ - SelectAll { - waker: AtomicWaker::new(), - inner: iter.map(|f| Some(f)), - } -} - #[cfg(test)] mod test { use freenet_stdlib::client_api::ClientRequest; + use futures::try_join; use super::*; use crate::client_events::ClientEventsProxy; @@ -252,11 +185,12 @@ mod test { struct SampleProxy { id: usize, rx: Receiver, + tx: Sender, } impl SampleProxy { - fn new(id: usize, rx: Receiver) -> Self { - Self { id, rx } + fn new(id: usize, rx: Receiver, tx: Sender) -> Self { + Self { id, rx, tx } } } @@ -269,9 +203,8 @@ mod test { .await .ok_or_else::(|| ErrorKind::ChannelClosed.into())?; assert_eq!(id, self.id); - eprintln!("#{}, received msg {id}", self.id); Ok(OpenRequest::new( - ClientId::new(id), + ClientId::next(), Box::new(ClientRequest::Disconnect { cause: None }), )) }) @@ -282,38 +215,108 @@ mod test { _id: ClientId, _response: Result, ) -> BoxFuture<'_, Result<(), ClientError>> { - todo!() + async { + self.tx + .send(self.id) + .await + .map_err(|_| ErrorKind::ChannelClosed.into()) + } + .boxed() } } - #[ignore] - #[tokio::test] - async fn combinator_recv() { + fn setup_proxies() -> ([BoxedClient; 3], Vec>, Vec>) { let mut cnt = 0; let mut senders = vec![]; - let proxies = [None::<()>; 3].map(|_| { - let (tx, rx) = channel(1); - senders.push(tx); - let r = Box::new(SampleProxy::new(cnt, rx)) as _; + let mut receivers = vec![]; + let clients = [None::<()>; 3].map(|_| { + let (tx1, rx1) = channel(1); + let (tx2, rx2) = channel(1); + let r = Box::new(SampleProxy::new(cnt, rx1, tx2)) as _; + senders.push(tx1); + receivers.push(rx2); cnt += 1; r }); + (clients, senders, receivers) + } + + #[tokio::test] + async fn test_recv() { + let (proxies, mut senders, _) = setup_proxies(); let mut combinator = ClientEventsCombinator::new(proxies); - let _senders = tokio::task::spawn(async move { - for (id, tx) in senders.iter_mut().enumerate() { - tx.send(id).await.unwrap(); - eprintln!("sent msg {id}"); + let sending = async { + for _ in 1..4 { + for (id, tx) in senders.iter_mut().enumerate() { + tx.send(id).await?; + } + } + Ok::<_, Box>(senders) + }; + + let combinator = async { + let client_ids = combinator + .internal_clients + .keys() + .cloned() + .collect::>(); + for _ in 0..3 { + for id in client_ids.iter() { + let OpenRequest { + client_id: req_id, .. + } = combinator.recv().await?; + assert_eq!(*id, req_id); + } } - senders - }) - .await - .unwrap(); + Ok::<_, Box>(()) + }; - for i in 0..3 { - let OpenRequest { client_id: id, .. } = combinator.recv().await.unwrap(); - eprintln!("received: {id:?}"); - assert_eq!(ClientId::new(i), id); + try_join!(sending, combinator).unwrap(); + } + + #[tokio::test] + async fn test_send() { + let (proxies, mut senders, mut receivers) = setup_proxies(); + let mut combinator = ClientEventsCombinator::new(proxies); + + // Create the internal client mapping implicitly. + for (idx, sender) in senders.iter_mut().enumerate() { + sender.send(idx).await.unwrap(); + combinator.recv().await.unwrap(); } + + let receiving = async { + // Test sending a response through the combinator for each proxy. + for (idx, receiver) in receivers.iter_mut().enumerate() { + // Assert that the receiver received the expected message. + let received_id = receiver + .recv() + .await + .ok_or(format!("missing {idx} sender"))?; + assert_eq!(received_id, idx); + } + Ok::<_, Box>(()) + }; + + let sending = async { + for (i, cli_id) in combinator + .internal_clients + .keys() + .cloned() + .collect::>() + .into_iter() + .enumerate() + { + // Send a sample response through the combinator. + combinator + .send(cli_id, Ok(HostResponse::Ok)) + .await + .map_err(|err| format!("Send failed for client {i}: {err}",))?; + } + Ok::<_, Box>(()) + }; + + try_join!(sending, receiving).unwrap(); } } diff --git a/crates/core/src/client_events/websocket.rs b/crates/core/src/client_events/websocket.rs index c84d43ba9..ba6faf8b8 100644 --- a/crates/core/src/client_events/websocket.rs +++ b/crates/core/src/client_events/websocket.rs @@ -211,6 +211,7 @@ async fn websocket_commands( Extension(rs): Extension, ) -> axum::response::Response { let on_upgrade = move |ws: WebSocket| async move { + tracing::debug!(protoc = ?ws.protocol(), "websocket connection established"); if let Err(error) = websocket_interface(rs.clone(), auth_token, encoding_protoc, ws).await { tracing::error!("{error}"); } @@ -225,7 +226,7 @@ async fn websocket_interface( ws: WebSocket, ) -> anyhow::Result<()> { let (mut response_rx, client_id) = new_client_connection(&request_sender).await?; - let (mut tx, mut rx) = ws.split(); + let (mut server_sink, mut client_stream) = ws.split(); let contract_updates: Arc)>>> = Arc::new(Mutex::new(VecDeque::new())); loop { @@ -245,7 +246,8 @@ async fn websocket_interface( active_listeners.push_back((key, listener)); } Err(err @ mpsc::error::TryRecvError::Disconnected) => { - return Err(anyhow::anyhow!(err)) + tracing::debug!(err = ?err, "listener channel disconnected"); + return Err(anyhow::anyhow!(err)); } } } @@ -256,7 +258,7 @@ async fn websocket_interface( }; let client_req_task = async { - let next_msg = match rx + let next_msg = match client_stream .next() .await .ok_or_else::(|| ErrorKind::Disconnect.into()) @@ -278,7 +280,7 @@ async fn websocket_interface( }; tokio::select! { biased; - msg = async { process_host_response(response_rx.recv().await, client_id, encoding_protoc, &mut tx).await } => { + msg = async { process_host_response(response_rx.recv().await, client_id, encoding_protoc, &mut server_sink).await } => { let active_listeners = contract_updates.clone(); if let Some(NewSubscription { key, callback }) = msg? { tracing::debug!(cli_id = %client_id, contract = %key, "added new notification listener"); @@ -289,11 +291,19 @@ async fn websocket_interface( process_client_request = client_req_task => { match process_client_request { Ok(Some(error)) => { - tx.send(error).await?; + server_sink.send(error).await.inspect_err(|err| { + tracing::debug!(err = %err, "error sending message to client"); + })?; } Ok(None) => continue, - Err(None) => return Ok(()), - Err(Some(err)) => return Err(err), + Err(None) => { + tracing::debug!("client channel closed on request"); + return Ok(()) + }, + Err(Some(err)) => { + tracing::debug!(err = %err, "client channel error on request"); + return Err(err) + }, } } response = listeners_task => { @@ -309,7 +319,9 @@ async fn websocket_interface( }, EncodingProtocol::Native => bincode::serialize(&response)?, }; - tx.send(Message::Binary(serialized_res)).await?; + server_sink.send(Message::Binary(serialized_res)).await.inspect_err(|err| { + tracing::debug!(err = %err, "error sending message to client"); + })?; } } } diff --git a/crates/core/src/config.rs b/crates/core/src/config.rs index 7f90315e5..67d43ee6b 100644 --- a/crates/core/src/config.rs +++ b/crates/core/src/config.rs @@ -744,7 +744,7 @@ impl<'a> Iterator for ConfigPathsIter<'a> { } } -impl<'a> core::iter::FusedIterator for ConfigPathsIter<'a> {} +impl core::iter::FusedIterator for ConfigPathsIter<'_> {} impl Config { pub fn db_dir(&self) -> PathBuf { diff --git a/crates/core/src/contract.rs b/crates/core/src/contract.rs index 2d5206c0d..89cbd7c37 100644 --- a/crates/core/src/contract.rs +++ b/crates/core/src/contract.rs @@ -24,7 +24,7 @@ pub use executor::{Executor, ExecutorError, OperationMode}; use executor::ContractExecutor; use tracing::Instrument; -pub(crate) async fn contract_handling<'a, CH>(mut contract_handler: CH) -> Result<(), ContractError> +pub(crate) async fn contract_handling(mut contract_handler: CH) -> Result<(), ContractError> where CH: ContractHandler + Send + 'static, { @@ -34,26 +34,23 @@ where match event { ContractHandlerEvent::GetQuery { key, - fetch_contract, + return_contract_code, } => { match contract_handler .executor() - .fetch_contract(key, fetch_contract) - .instrument(tracing::info_span!("fetch_contract", %key, %fetch_contract)) + .fetch_contract(key, return_contract_code) + .instrument(tracing::info_span!("fetch_contract", %key, %return_contract_code)) .await { Ok((state, contract)) => { - tracing::debug!(with_contract = %fetch_contract, has_contract = %contract.is_some(), "Fetched contract {key}"); + tracing::debug!(with_contract_code = %return_contract_code, has_contract = %contract.is_some(), "Fetched contract {key}"); contract_handler .channel() .send_to_sender( id, ContractHandlerEvent::GetResponse { key, - response: Ok(StoreResponse { - state: Some(state), - contract, - }), + response: Ok(StoreResponse { state, contract }), }, ) .await @@ -92,6 +89,7 @@ where .upsert_contract_state(key, Either::Left(state), related_contracts, contract) .instrument(tracing::info_span!("upsert_contract_state", %key)) .await; + contract_handler .channel() .send_to_sender( @@ -108,17 +106,19 @@ where } ContractHandlerEvent::UpdateQuery { key, - state, + data, related_contracts, } => { + let update_value: Either> = match data { + freenet_stdlib::prelude::UpdateData::State(state) => { + Either::Left(WrappedState::from(state.into_bytes())) + } + freenet_stdlib::prelude::UpdateData::Delta(delta) => Either::Right(delta), + _ => unreachable!(), + }; let update_result = contract_handler .executor() - .upsert_contract_state( - key, - Either::Left(state.clone()), - related_contracts, - None, - ) + .upsert_contract_state(key, update_value, related_contracts, None) .instrument(tracing::info_span!("upsert_contract_state", %key)) .await; @@ -136,6 +136,19 @@ where error })?; } + ContractHandlerEvent::RegisterSubscriberListener { + key, + client_id, + summary, + subscriber_listener, + } => { + let _ = contract_handler + .executor() + .register_contract_notifier(key, client_id, subscriber_listener, summary) + .inspect_err(|err| { + tracing::warn!("Error while registering subscriber listener: {err}"); + }); + } _ => unreachable!(), } } diff --git a/crates/core/src/contract/executor.rs b/crates/core/src/contract/executor.rs index 36109e38c..a0d9217aa 100644 --- a/crates/core/src/contract/executor.rs +++ b/crates/core/src/contract/executor.rs @@ -22,11 +22,6 @@ use tokio::sync::mpsc::{self}; use crate::config::Config; use crate::message::Transaction; use crate::node::OpManager; -#[cfg(any( - not(feature = "local-mode"), - feature = "network-mode", - all(not(feature = "local-mode"), not(feature = "network-mode")) -))] use crate::operations::get::GetResult; use crate::operations::{OpEnum, OpError}; use crate::wasm_runtime::{ @@ -340,16 +335,16 @@ where #[allow(unused)] struct GetContract { key: ContractKey, - fetch_contract: bool, + return_contract_code: bool, } impl ComposeNetworkMessage for GetContract { fn initiate_op(self, _op_manager: &OpManager) -> operations::get::GetOp { - operations::get::start_op(self.key, self.fetch_contract) + operations::get::start_op(self.key, self.return_contract_code) } async fn resume_op(op: operations::get::GetOp, op_manager: &OpManager) -> Result<(), OpError> { - operations::get::request_get(op_manager, op).await + operations::get::request_get(op_manager, op, vec![]).await } } @@ -423,8 +418,9 @@ pub(crate) trait ContractExecutor: Send + 'static { fn fetch_contract( &mut self, key: ContractKey, - fetch_contract: bool, - ) -> impl Future), ExecutorError>> + Send; + return_contract_code: bool, + ) -> impl Future, Option), ExecutorError>> + + Send; fn upsert_contract_state( &mut self, @@ -433,6 +429,14 @@ pub(crate) trait ContractExecutor: Send + 'static { related_contracts: RelatedContracts<'static>, code: Option, ) -> impl Future> + Send; + + fn register_contract_notifier( + &mut self, + key: ContractKey, + cli_id: ClientId, + notification_ch: tokio::sync::mpsc::UnboundedSender, + summary: Option>, + ) -> Result<(), Box>; } /// A WASM executor which will run any contracts, delegates, etc. registered. @@ -441,10 +445,6 @@ pub(crate) trait ContractExecutor: Send + 'static { /// Consumers of the executor are required to poll for new changes in order to be notified /// of changes or can alternatively use the notification channel. pub struct Executor { - #[cfg(any( - all(feature = "local-mode", feature = "network-mode"), - all(not(feature = "local-mode"), not(feature = "network-mode")), - ))] mode: OperationMode, runtime: R, pub state_store: StateStore, @@ -469,10 +469,6 @@ impl Executor { ctrl_handler()?; Ok(Self { - #[cfg(any( - all(feature = "local-mode", feature = "network-mode"), - all(not(feature = "local-mode"), not(feature = "network-mode")), - ))] mode, runtime, state_store, diff --git a/crates/core/src/contract/executor/mock_runtime.rs b/crates/core/src/contract/executor/mock_runtime.rs index 238ceca49..f3addfb8e 100644 --- a/crates/core/src/contract/executor/mock_runtime.rs +++ b/crates/core/src/contract/executor/mock_runtime.rs @@ -1,4 +1,5 @@ use super::*; +use tokio::sync::mpsc::UnboundedSender; pub(crate) struct MockRuntime { pub contract_store: ContractStore, @@ -34,10 +35,10 @@ impl Executor { Ok(executor) } - pub async fn handle_request<'a>( + pub async fn handle_request( &mut self, _id: ClientId, - _req: ClientRequest<'a>, + _req: ClientRequest<'_>, _updates: Option>>, ) -> Response { unreachable!() @@ -48,8 +49,8 @@ impl ContractExecutor for Executor { async fn fetch_contract( &mut self, key: ContractKey, - fetch_contract: bool, - ) -> Result<(WrappedState, Option), ExecutorError> { + return_contract_code: bool, + ) -> Result<(Option, Option), ExecutorError> { let Some(parameters) = self .state_store .get_params(&key) @@ -60,7 +61,7 @@ impl ContractExecutor for Executor { "missing state and/or parameters for contract {key}" ))); }; - let contract = if fetch_contract { + let contract = if return_contract_code { self.runtime .contract_store .fetch_contract(&key, ¶meters) @@ -72,7 +73,7 @@ impl ContractExecutor for Executor { "missing state for contract {key}" ))); }; - Ok((state, contract)) + Ok((Some(state), contract)) } async fn upsert_contract_state( @@ -108,6 +109,16 @@ impl ContractExecutor for Executor { (update, contract) => unreachable!("{update:?}, {contract:?}"), } } + + fn register_contract_notifier( + &mut self, + _key: ContractKey, + _cli_id: ClientId, + _notification_ch: UnboundedSender, + _summary: Option>, + ) -> Result<(), Box> { + Ok(()) + } } #[cfg(test)] diff --git a/crates/core/src/contract/executor/runtime.rs b/crates/core/src/contract/executor/runtime.rs index 6461cc0cc..9382b5a12 100644 --- a/crates/core/src/contract/executor/runtime.rs +++ b/crates/core/src/contract/executor/runtime.rs @@ -4,18 +4,11 @@ impl ContractExecutor for Executor { async fn fetch_contract( &mut self, key: ContractKey, - fetch_contract: bool, - ) -> Result<(WrappedState, Option), ExecutorError> { - match self.perform_contract_get(fetch_contract, key).await { - Ok(HostResponse::ContractResponse(ContractResponse::GetResponse { - contract, - state, - .. - })) => Ok((state, contract)), + return_contract_code: bool, + ) -> Result<(Option, Option), ExecutorError> { + match self.perform_contract_get(return_contract_code, key).await { + Ok((state, code)) => Ok((state, code)), Err(err) => Err(err), - Ok(_) => { - unreachable!() - } } } @@ -71,7 +64,12 @@ impl ContractExecutor for Executor { ExecutorError::other(err) })?; match result { - ValidateResult::Valid => {} + ValidateResult::Valid => { + self.state_store + .store(key, incoming_state.clone(), params.clone()) + .await + .map_err(ExecutorError::other)?; + } ValidateResult::Invalid => { return Err(ExecutorError::request(StdContractError::invalid_put(key))); } @@ -89,21 +87,8 @@ impl ContractExecutor for Executor { vec![UpdateData::State(incoming_state.clone().into())] } Either::Right(delta) => { - let valid = self - .runtime - .validate_delta(&key, ¶ms, &delta) - .map_err(|err| { - if remove_if_fail { - let _ = self.runtime.contract_store.remove_contract(&key); - } - ExecutorError::other(err) - })?; - if !valid { - return Err(ExecutorError::request(StdContractError::invalid_update( - key, - ))); - } // todo: forward delta like we are doing with puts + tracing::warn!("Delta updates are not yet supported"); vec![UpdateData::Delta(delta)] } }; @@ -111,6 +96,7 @@ impl ContractExecutor for Executor { let current_state = match self.state_store.get(&key).await { Ok(s) => s, Err(StateStoreError::MissingContract(_)) => { + tracing::warn!("Missing contract {key} for upsert"); return Err(ExecutorError::request(StdContractError::MissingContract { key: key.into(), })); @@ -145,6 +131,41 @@ impl ContractExecutor for Executor { }; Ok(updated_state) } + + fn register_contract_notifier( + &mut self, + key: ContractKey, + cli_id: ClientId, + notification_ch: tokio::sync::mpsc::UnboundedSender, + summary: Option>, + ) -> Result<(), Box> { + let channels = self.update_notifications.entry(key).or_default(); + if let Ok(i) = channels.binary_search_by_key(&&cli_id, |(p, _)| p) { + let (_, existing_ch) = &channels[i]; + if !existing_ch.same_channel(¬ification_ch) { + return Err(RequestError::from(StdContractError::Subscribe { + key, + cause: format!("Peer {cli_id} already subscribed").into(), + }) + .into()); + } + } else { + channels.push((cli_id, notification_ch)); + } + + if self + .subscriber_summaries + .entry(key) + .or_default() + .insert(cli_id, summary.map(StateSummary::into_owned)) + .is_some() + { + tracing::warn!( + "contract {key} already was registered for peer {cli_id}; replaced summary" + ); + } + Ok(()) + } } impl Executor { @@ -229,10 +250,10 @@ impl Executor { } } - pub async fn handle_request<'a>( + pub async fn handle_request( &mut self, id: ClientId, - req: ClientRequest<'a>, + req: ClientRequest<'_>, updates: Option>>, ) -> Response { match req { @@ -248,7 +269,7 @@ impl Executor { } } - /// Responde to requests made through any API's from client applications locally. + /// Responde to requests made through any API's from client applications in local mode. pub async fn contract_requests( &mut self, req: ContractRequest<'_>, @@ -265,26 +286,26 @@ impl Executor { .await } ContractRequest::Update { key, data } => self.perform_contract_update(key, data).await, + // FIXME ContractRequest::Get { key, - fetch_contract: contract, - } => self.perform_contract_get(contract, key).await, + return_contract_code, + } => match self.perform_contract_get(return_contract_code, key).await { + Ok(_) => todo!(), + Err(_) => todo!(), + }, ContractRequest::Subscribe { key, summary } => { + tracing::debug!("subscribing to contract {key}"); let updates = updates.ok_or_else(|| { ExecutorError::other(anyhow::anyhow!("missing update channel")) })?; self.register_contract_notifier(key, cli_id, updates, summary)?; + // by default a subscribe op has an implicit get - let res = self.perform_contract_get(false, key).await?; - #[cfg(any( - all(not(feature = "local-mode"), not(feature = "network-mode")), - all(feature = "local-mode", feature = "network-mode"), - all(feature = "network-mode", not(feature = "network-mode")) - ))] - { - self.subscribe(key).await?; - } - Ok(res) + let _res = self.perform_contract_get(true, key).await?; + self.subscribe(key).await?; + // FIXME + todo!() } _ => Err(ExecutorError::other(anyhow::anyhow!("not supported"))), } @@ -458,25 +479,12 @@ impl Executor { self.send_update_notification(&key, ¶meters, &new_state) .await?; - #[cfg(any( - not(feature = "local-mode"), - feature = "network-mode", - all(not(feature = "local-mode"), not(feature = "network-mode")) - ))] - { - #[cfg(any( - all(feature = "local-mode", feature = "network-mode"), - all(not(feature = "local-mode"), not(feature = "network-mode")) - ))] - { - if self.mode == OperationMode::Local { - return Ok(ContractResponse::UpdateResponse { key, summary }.into()); - } - } - // notify peers with deltas from summary in network - let request = UpdateContract { key, new_state }; - let _op: operations::update::UpdateResult = self.op_request(request).await?; + if self.mode == OperationMode::Local { + return Ok(ContractResponse::UpdateResponse { key, summary }.into()); } + // notify peers with deltas from summary in network + let request = UpdateContract { key, new_state }; + let _op: operations::update::UpdateResult = self.op_request(request).await?; Ok(ContractResponse::UpdateResponse { key, summary }.into()) } @@ -566,14 +574,9 @@ impl Executor { state: state.into(), }); } - #[cfg(any( - all(not(feature = "local-mode"), not(feature = "network-mode")), - all(feature = "local-mode", feature = "network-mode") - ))] - Err(StateStoreError::MissingContract(_)) - if self.mode == OperationMode::Network => - { - let state = match self.local_state_or_from_network(&id).await? { + + Err(StateStoreError::MissingContract(_)) => { + let state = match self.local_state_or_from_network(&id, false).await? { Either::Left(state) => state, Either::Right(GetResult { state, contract, .. @@ -582,7 +585,7 @@ impl Executor { return Err(ExecutorError::request( RequestError::ContractError(StdContractError::Get { key, - cause: "missing-contract".into(), + cause: "Missing contract".into(), }), )); }; @@ -603,28 +606,6 @@ impl Executor { } } } - #[cfg(all(not(feature = "local-mode"), feature = "network-mode"))] - Err(StateStoreError::MissingContract(_)) => { - let state = match self.local_state_or_from_network(&id).await? { - Either::Left(state) => current_state, - Either::Right(GetResult { state, contract }) => { - self.verify_and_store_contract( - current_state.clone(), - contract, - RelatedContracts::default(), - ) - .await?; - current_state - } - }; - updates.push(UpdateData::State(current_state.into())); - match mode { - RelatedMode::StateOnce => {} - RelatedMode::StateThenSubscribe => { - self.subscribe(id.into()).await?; - } - } - } Err(other_err) => { let _ = mode; return Err(ExecutorError::other(other_err)); @@ -644,68 +625,22 @@ impl Executor { Ok(new_state) } - async fn perform_contract_get(&mut self, fetch_contract: bool, key: ContractKey) -> Response { - let mut got_contract = None; + async fn perform_contract_get( + &mut self, + return_contract_code: bool, + key: ContractKey, + ) -> Result<(Option, Option), ExecutorError> { + let mut got_contract: Option = None; - #[cfg(any( - all(not(feature = "local-mode"), not(feature = "network-mode")), - all(feature = "local-mode", feature = "network-mode"), - ))] - { - if fetch_contract && self.mode == OperationMode::Local { - let Some(contract) = self.get_contract_locally(&key).await? else { - return Err(ExecutorError::request(RequestError::from( - StdContractError::Get { - key, - cause: "Missing contract and/or parameters".into(), - }, - ))); - }; + if return_contract_code { + if let Some(contract) = self.get_contract_locally(&key).await? { got_contract = Some(contract); - } else if fetch_contract { - got_contract = self.get_contract_from_network(key).await?; - } - } - - #[cfg(all(feature = "local-mode", not(feature = "network-mode")))] - if fetch_contract { - let Some(contract) = self - .get_contract_locally(&key) - .await - .map_err(Either::Left)? - else { - return Err(ExecutorError::request(RequestError::from( - StdContractError::Get { - key, - cause: "Missing contract or parameters".into(), - }, - ))); - }; - got_contract = Some(fetch_contract); - } - - #[cfg(all(feature = "network-mode", not(feature = "local-mode")))] - if fetch_contract { - if let Ok(Some(contract)) = self.get_contract_locally(&key).await { - got_contract = Some(fetch_contract); - } else { - got_contract = self.get_contract_from_network(key).await?; } } match self.state_store.get(&key).await { - Ok(state) => Ok(ContractResponse::GetResponse { - key, - contract: got_contract, - state, - } - .into()), - Err(StateStoreError::MissingContract(_)) => Err(ExecutorError::request( - RequestError::from(StdContractError::Get { - key, - cause: "Missing contract state".into(), - }), - )), + Ok(state) => Ok((Some(state), got_contract)), + Err(StateStoreError::MissingContract(_)) => Ok((None, got_contract)), Err(err) => Err(ExecutorError::request(RequestError::from( StdContractError::Get { key, @@ -731,11 +666,11 @@ impl Executor { Ok(State::from(state)) } - async fn verify_and_store_contract<'a>( + async fn verify_and_store_contract( &mut self, state: WrappedState, trying_container: ContractContainer, - mut related_contracts: RelatedContracts<'a>, + mut related_contracts: RelatedContracts<'_>, ) -> Result<(), ExecutorError> { let key = trying_container.key(); let params = trying_container.params(); @@ -780,39 +715,24 @@ impl Executor { related_contracts.missing(related); for (id, related) in related_contracts.update() { if related.is_none() { - #[cfg(all(feature = "local-mode", not(feature = "network-mode")))] - { - let current_state = self.get_local_contract(id).await?; - *related = Some(current_state); - } - - #[cfg(any( - all(not(feature = "local-mode"), not(feature = "network-mode")), - all(feature = "local-mode", feature = "network-mode"), - all(not(feature = "local-mode"), feature = "network-mode") - ))] - { - match self.local_state_or_from_network(id).await? { - Either::Left(state) => { - *related = Some(state.into()); - } - Either::Right(result) => { - let Some(contract) = result.contract else { - return Err(ExecutorError::request( - RequestError::ContractError( - StdContractError::Get { - key: (*id).into(), - cause: "missing-contract".into(), - }, - ), - )); - }; - trying_key = (*id).into(); - trying_params = contract.params(); - trying_state = result.state; - trying_contract = Some(contract); - continue; - } + match self.local_state_or_from_network(id, false).await? { + Either::Left(state) => { + *related = Some(state.into()); + } + Either::Right(result) => { + let Some(contract) = result.contract else { + return Err(ExecutorError::request( + RequestError::ContractError(StdContractError::Get { + key: (*id).into(), + cause: "missing-contract".into(), + }), + )); + }; + trying_key = (*id).into(); + trying_params = contract.params(); + trying_state = result.state; + trying_contract = Some(contract); + continue; } } } @@ -848,10 +768,10 @@ impl Executor { Ok(()) } - async fn send_update_notification<'a>( + async fn send_update_notification( &mut self, key: &ContractKey, - params: &Parameters<'a>, + params: &Parameters<'_>, new_state: &WrappedState, ) -> Result<(), ExecutorError> { tracing::debug!(contract = %key, "notify of contract update"); @@ -911,21 +831,10 @@ impl Executor { } } -#[cfg(any( - not(feature = "local-mode"), - feature = "network-mode", - all(not(feature = "local-mode"), not(feature = "network-mode")) -))] impl Executor { async fn subscribe(&mut self, key: ContractKey) -> Result<(), ExecutorError> { - #[cfg(any( - all(not(feature = "local-mode"), not(feature = "network-mode")), - all(feature = "local-mode", feature = "network-mode") - ))] - { - if self.mode == OperationMode::Local { - return Ok(()); - } + if self.mode == OperationMode::Local { + return Ok(()); } let request = SubscribeContract { key }; let _sub: operations::subscribe::SubscribeResult = self.op_request(request).await?; @@ -936,60 +845,16 @@ impl Executor { async fn local_state_or_from_network( &mut self, id: &ContractInstanceId, + return_contract_code: bool, ) -> Result, ExecutorError> { if let Ok(contract) = self.state_store.get(&(*id).into()).await { return Ok(Either::Left(contract)); }; let request: GetContract = GetContract { key: (*id).into(), - fetch_contract: true, + return_contract_code, }; let get_result: operations::get::GetResult = self.op_request(request).await?; Ok(Either::Right(get_result)) } - - async fn get_contract_from_network( - &mut self, - key: ContractKey, - ) -> Result, ExecutorError> { - loop { - if let Ok(Some(contract)) = self.get_contract_locally(&key).await { - break Ok(Some(contract)); - } else { - #[cfg(any( - all(not(feature = "local-mode"), not(feature = "network-mode")), - all(feature = "local-mode", feature = "network-mode") - ))] - { - if self.mode == OperationMode::Local { - return Err(ExecutorError::request(RequestError::ContractError( - StdContractError::MissingRelated { key: *key.id() }, - ))); - } - } - match self.local_state_or_from_network(&key.into()).await? { - Either::Right(GetResult { - state, contract, .. - }) => { - let Some(contract) = contract else { - return Err(ExecutorError::request(RequestError::ContractError( - StdContractError::Get { - key, - cause: "missing-contract".into(), - }, - ))); - }; - self.verify_and_store_contract( - state, - contract.clone(), - RelatedContracts::default(), - ) - .await?; - break Ok(Some(contract)); - } - Either::Left(_state) => continue, - } - } - } - } } diff --git a/crates/core/src/contract/handler.rs b/crates/core/src/contract/handler.rs index bf9f3c802..c350c1d4e 100644 --- a/crates/core/src/contract/handler.rs +++ b/crates/core/src/contract/handler.rs @@ -305,7 +305,7 @@ pub(crate) enum ContractHandlerEvent { /// Fetch a supposedly existing contract value in this node, and optionally the contract itself GetQuery { key: ContractKey, - fetch_contract: bool, + return_contract_code: bool, }, /// The response to a get query event GetResponse { @@ -315,13 +315,19 @@ pub(crate) enum ContractHandlerEvent { /// Updates a supposedly existing contract in this node UpdateQuery { key: ContractKey, - state: WrappedState, + data: UpdateData<'static>, related_contracts: RelatedContracts<'static>, }, /// The response to an update query UpdateResponse { new_value: Result, }, + RegisterSubscriberListener { + key: ContractKey, + client_id: ClientId, + summary: Option>, + subscriber_listener: UnboundedSender, + }, } impl std::fmt::Display for ContractHandlerEvent { @@ -351,9 +357,13 @@ impl std::fmt::Display for ContractHandlerEvent { }, ContractHandlerEvent::GetQuery { key, - fetch_contract, + return_contract_code, + .. } => { - write!(f, "get query {{ {key}, fetch contract: {fetch_contract} }}",) + write!( + f, + "get query {{ {key}, return contract code: {return_contract_code} }}", + ) } ContractHandlerEvent::GetResponse { key, response } => match response { Ok(_) => { @@ -374,6 +384,12 @@ impl std::fmt::Display for ContractHandlerEvent { write!(f, "update query failed {{ {e} }}",) } }, + ContractHandlerEvent::RegisterSubscriberListener { key, client_id, .. } => { + write!( + f, + "register subscriber listener {{ {key}, client_id: {client_id} }}", + ) + } } } } diff --git a/crates/core/src/message.rs b/crates/core/src/message.rs index c915624d7..b56c243bc 100644 --- a/crates/core/src/message.rs +++ b/crates/core/src/message.rs @@ -6,7 +6,7 @@ use std::{ time::{Duration, SystemTime}, }; -use freenet_stdlib::prelude::ContractKey; +use freenet_stdlib::prelude::{ContractContainer, ContractKey, WrappedState}; use serde::{Deserialize, Serialize}; use ulid::Ulid; @@ -313,6 +313,18 @@ pub(crate) enum NodeEvent { Disconnect { cause: Option>, }, + QueryConnections { + callback: tokio::sync::mpsc::Sender, + }, +} + +pub(crate) enum QueryResult { + Connections(Vec), + GetResult { + key: ContractKey, + state: WrappedState, + contract: Option, + }, } impl Display for NodeEvent { @@ -330,6 +342,9 @@ impl Display for NodeEvent { NodeEvent::Disconnect { cause: None } => { write!(f, "Disconnect node, reason: unknown") } + NodeEvent::QueryConnections { .. } => { + write!(f, "QueryConnections") + } } } } diff --git a/crates/core/src/node.rs b/crates/core/src/node.rs index 7fd7bb6f6..7d6723411 100644 --- a/crates/core/src/node.rs +++ b/crates/core/src/node.rs @@ -22,8 +22,8 @@ use std::{ use anyhow::Context; use either::Either; use freenet_stdlib::{ - client_api::{ClientRequest, ContractRequest, ErrorKind}, - prelude::{ContractKey, RelatedContracts, WrappedState}, + client_api::{ClientRequest, ErrorKind}, + prelude::ContractKey, }; use rsa::pkcs8::DecodePublicKey; @@ -33,13 +33,13 @@ use tracing::Instrument; use self::p2p_impl::NodeP2P; use crate::{ client_events::{BoxedClient, ClientEventsProxy, ClientId, OpenRequest}, - config::{Address, GatewayConfig, GlobalExecutor, WebsocketApiConfig}, + config::{Address, GatewayConfig, WebsocketApiConfig}, contract::{ - Callback, ClientResponsesReceiver, ClientResponsesSender, ContractError, ExecutorError, - ExecutorToEventLoopChannel, NetworkContractHandler, + Callback, ClientResponsesSender, ContractError, ExecutorError, ExecutorToEventLoopChannel, + NetworkContractHandler, }, local_node::Executor, - message::{NetMessage, NodeEvent, Transaction, TransactionType}, + message::{NetMessage, Transaction, TransactionType}, operations::{ connect::{self, ConnectOp}, get, put, subscribe, update, OpEnum, OpError, OpOutcome, @@ -356,177 +356,6 @@ impl InitPeerNode { } } -/// Process client events. -async fn client_event_handling( - op_manager: Arc, - mut client_events: ClientEv, - mut client_responses: ClientResponsesReceiver, - node_controller: tokio::sync::mpsc::Sender, -) where - ClientEv: ClientEventsProxy + Send + 'static, -{ - loop { - tokio::select! { - client_request = client_events.recv() => { - let req = match client_request { - Ok(request) => { - tracing::debug!(%request, "got client request event"); - request - } - Err(error) if matches!(error.kind(), ErrorKind::Shutdown) => { - node_controller.send(NodeEvent::Disconnect { cause: None }).await.ok(); - break; - } - Err(error) => { - tracing::debug!(%error, "client error"); - continue; - } - }; - // fixme: only allow in certain modes (e.g. while testing) - if let ClientRequest::Disconnect { cause } = &*req.request { - node_controller.send(NodeEvent::Disconnect { cause: cause.clone() }).await.ok(); - break; - } - process_open_request(req, op_manager.clone()).await; - } - res = client_responses.recv() => { - if let Some((cli_id, res)) = res { - if let Ok(result) = &res { - tracing::debug!(%result, "sending client response"); - } - if let Err(err) = client_events.send(cli_id, res).await { - tracing::debug!("channel closed: {err}"); - break; - } - } - } - } - } -} - -#[inline] -async fn process_open_request(request: OpenRequest<'static>, op_manager: Arc) { - // this will indirectly start actions on the local contract executor - let fut = async move { - let client_id = request.client_id; - - // fixme: communicate back errors in this loop to the client somehow - match *request.request { - ClientRequest::ContractOp(ops) => match ops { - ContractRequest::Put { - state, - contract, - related_contracts, - } => { - let peer_id = op_manager - .ring - .connection_manager - .get_peer_key() - .expect("Peer id not found at put op, it should be set"); - // Initialize a put op. - tracing::debug!( - this_peer = %peer_id, - "Received put from user event", - ); - let op = put::start_op( - contract, - related_contracts, - state, - op_manager.ring.max_hops_to_live, - ); - let _ = op_manager - .ch_outbound - .waiting_for_transaction_result(op.id, client_id) - .await; - if let Err(err) = put::request_put(&op_manager, op).await { - tracing::error!("{}", err); - } - } - ContractRequest::Update { key, data } => { - let peer_id = op_manager - .ring - .connection_manager - .get_peer_key() - .expect("Peer id not found at update op, it should be set"); - tracing::debug!( - this_peer = %peer_id, - "Received update from user event", - ); - let state = match data { - freenet_stdlib::prelude::UpdateData::State(s) => s, - _ => { - unreachable!(); - } - }; - - let wrapped_state = WrappedState::from(state.into_bytes()); - - let related_contracts = RelatedContracts::default(); - - let op = update::start_op(key, wrapped_state, related_contracts); - - let _ = op_manager - .ch_outbound - .waiting_for_transaction_result(op.id, client_id) - .await; - - if let Err(err) = update::request_update(&op_manager, op).await { - tracing::error!("request update error {}", err) - } - } - ContractRequest::Get { - key, - fetch_contract: contract, - } => { - let peer_id = op_manager - .ring - .connection_manager - .get_peer_key() - .expect("Peer id not found at get op, it should be set"); - // Initialize a get op. - tracing::debug!( - this_peer = %peer_id, - "Received get from user event", - ); - let op = get::start_op(key, contract); - let _ = op_manager - .ch_outbound - .waiting_for_transaction_result(op.id, client_id) - .await; - if let Err(err) = get::request_get(&op_manager, op).await { - tracing::error!("{}", err); - } - } - ContractRequest::Subscribe { key, .. } => { - subscribe(op_manager, key, Some(client_id)).await; - } - _ => { - tracing::error!("Op not supported"); - } - }, - ClientRequest::DelegateOp(_op) => todo!("FIXME: delegate op"), - ClientRequest::Disconnect { .. } => unreachable!(), - _ => { - tracing::error!("Op not supported"); - } - } - }; - GlobalExecutor::spawn(fut.instrument( - tracing::info_span!(parent: tracing::Span::current(), "process_client_request"), - )); -} - -#[allow(unused)] -macro_rules! log_handling_msg { - ($op:expr, $id:expr, $op_manager:ident) => { - tracing::debug!( - tx = %$id, - this_peer = %$op_manager.ring.peer_key, - concat!("Handling ", $op, " request"), - ); - }; -} - async fn report_result( tx: Option, op_result: Result, OpError>, @@ -785,7 +614,9 @@ async fn process_message_v1( .await; } NetMessageV1::Unsubscribed { ref key, .. } => { - subscribe(op_manager, *key, None).await; + if let Err(error) = subscribe(op_manager, *key, None).await { + tracing::error!(%error, "Failed to subscribe to contract"); + } break; } _ => break, // Exit the loop if no applicable message type is found @@ -794,48 +625,57 @@ async fn process_message_v1( } /// Attempts to subscribe to a contract -async fn subscribe(op_manager: Arc, key: ContractKey, client_id: Option) { +pub async fn subscribe( + op_manager: Arc, + key: ContractKey, + client_id: Option, +) -> Result { const TIMEOUT: Duration = Duration::from_secs(30); - let mut missing_contract = false; + let op = subscribe::start_op(key); + let id = op.id; + if let Some(client_id) = client_id { + let _ = op_manager + .ch_outbound + .waiting_for_transaction_result(id, client_id) + .await; + } + // Initialize a subscribe op. + match subscribe::request_subscribe(&op_manager, op).await { + Err(OpError::ContractError(ContractError::ContractNotFound(key))) => { + tracing::info!(%key, "Trying to subscribe to a contract not present, requesting it first"); + let get_op = get::start_op(key, true); + if let Err(error) = get::request_get(&op_manager, get_op, vec![]).await { + tracing::error!(%key, %error, "Failed getting the contract while previously trying to subscribe; bailing"); + return Err(error); + } + } + Err(err) => { + tracing::error!("{}", err); + return Err(err); + } + Ok(()) => { + return Ok(id); + } + } let timeout = tokio::time::timeout(TIMEOUT, async { - // Initialize a subscribe op. loop { + // just start a new op to check if contract is present let op = subscribe::start_op(key); - if let Some(client_id) = client_id { - let _ = op_manager - .ch_outbound - .waiting_for_transaction_result(op.id, client_id) - .await; - } match subscribe::request_subscribe(&op_manager, op).await { - Err(OpError::ContractError(ContractError::ContractNotFound(key))) - if !missing_contract => - { - tracing::info!(%key, "Trying to subscribe to a contract not present, requesting it first"); - missing_contract = true; - let get_op = get::start_op(key, true); - if let Err(error) = get::request_get(&op_manager, get_op).await { - tracing::error!(%key, %error, "Failed getting the contract while previously trying to subscribe; bailing"); - break Err(error); - } - continue; - } Err(OpError::ContractError(ContractError::ContractNotFound(_))) => { tracing::warn!("Still waiting for {key} contract"); tokio::time::sleep(Duration::from_secs(2)).await } - Err(err) => { - tracing::error!("{}", err); - break Err(err); + Err(error) => { + tracing::error!(%key, %error, "Error while subscribing to contract"); + break Err(error); } Ok(()) => { - if missing_contract { - tracing::debug!(%key, - "Got back the missing contract while subscribing" - ); - } + tracing::debug!(%key, + "Got back the missing contract while subscribing" + ); tracing::debug!(%key, "Starting subscribe request"); - break Ok(()); + break Ok(id); } } } @@ -843,12 +683,15 @@ async fn subscribe(op_manager: Arc, key: ContractKey, client_id: Opti match timeout.await { Err(_) => { tracing::error!(%key, "Timeout while waiting for contract to start subscription"); + Err(OpError::OpNotPresent(id)) } Ok(Err(error)) => { tracing::error!(%key, %error, "Error while subscribing to contract"); + Err(error) } - Ok(Ok(_)) => { + Ok(Ok(op_id)) => { tracing::debug!(%key, "Started subscription to contract"); + Ok(op_id) } } } diff --git a/crates/core/src/node/network_bridge/handshake.rs b/crates/core/src/node/network_bridge/handshake.rs index 57e111aff..59f044cbc 100644 --- a/crates/core/src/node/network_bridge/handshake.rs +++ b/crates/core/src/node/network_bridge/handshake.rs @@ -29,7 +29,7 @@ use crate::{ type Result = std::result::Result; type OutboundConnResult = Result; -const TIMEOUT: Duration = Duration::from_secs(60); +const TIMEOUT: Duration = Duration::from_secs(10); #[derive(Debug)] pub(super) struct ForwardInfo { @@ -114,13 +114,25 @@ impl OutboundMessage { } } +pub(super) enum ExternConnection { + Establish { + peer: PeerId, + tx: Transaction, + is_gw: bool, + }, +} + /// Use for starting a new outboound connection to a peer. -pub(super) struct EstablishConnection(pub(crate) mpsc::Sender<(PeerId, Transaction, bool)>); +pub(super) struct EstablishConnection(pub(crate) mpsc::Sender); impl EstablishConnection { pub async fn establish_conn(&self, remote: PeerId, tx: Transaction, is_gw: bool) -> Result<()> { self.0 - .send((remote, tx, is_gw)) + .send(ExternConnection::Establish { + peer: remote, + tx, + is_gw, + }) .await .map_err(|_| HandshakeError::ChannelClosed)?; Ok(()) @@ -129,7 +141,7 @@ impl EstablishConnection { type OutboundMessageSender = mpsc::Sender; type OutboundMessageReceiver = mpsc::Receiver<(SocketAddr, NetMessage)>; -type EstablishConnectionReceiver = mpsc::Receiver<(PeerId, Transaction, bool)>; +type EstablishConnectionReceiver = mpsc::Receiver; /// Manages the handshake process for establishing connections with peers. /// Handles both inbound and outbound connection attempts, and manages @@ -449,10 +461,12 @@ impl HandshakeHandler { } // Handle requests to establish new connections establish_connection = self.establish_connection_rx.recv() => { - let Some((peer_id, tx, is_gw)) = establish_connection else { - return Err(HandshakeError::ChannelClosed); - }; - self.start_outbound_connection(peer_id, tx, is_gw).await; + match establish_connection { + Some(ExternConnection::Establish { peer, tx, is_gw }) => { + self.start_outbound_connection(peer, tx, is_gw).await; + } + None => return Err(HandshakeError::ChannelClosed), + } } } } @@ -749,12 +763,13 @@ async fn wait_for_gw_confirmation( joiner_key: this_peer.pub_key.clone(), hops_to_live: tracker.total_checks, max_hops_to_live: tracker.total_checks, - skip_list: vec![], + skip_list: vec![this_peer], }, })); tracing::debug!( at=?tracker.gw_conn.my_address(), from=%tracker.gw_conn.remote_addr(), + msg = ?msg, "Sending initial connection message to gw" ); tracker @@ -1155,7 +1170,9 @@ mod tests { vec![], ) .unwrap(); + tracing::trace!(at=?self.my_addr, to=%addr, "Sending message to peer"); packet_sender.send(sym_msg.into_unknown()).await.unwrap(); + tracing::trace!(at=?self.my_addr, from=%addr, "Message sent"); self.packet_id += 1; } @@ -1205,9 +1222,9 @@ mod tests { addr: impl Into, existing_connections: Option>, ) -> (HandshakeHandler, TestVerifier) { - let (outbound_sender, outbound_recv) = mpsc::channel(5); + let (outbound_sender, outbound_recv) = mpsc::channel(100); let outbound_conn_handler = OutboundConnectionHandler::new(outbound_sender); - let (inbound_sender, inbound_recv) = mpsc::channel(5); + let (inbound_sender, inbound_recv) = mpsc::channel(100); let inbound_conn_handler = InboundConnectionHandler::new(inbound_recv); let addr = addr.into(); let keypair = TransportKeypair::new(); @@ -1569,7 +1586,8 @@ mod tests { Ok(()) } - #[tokio::test] + #[ignore = "flaky in ci"] + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_peer_to_gw_outbound_conn_rejected() -> anyhow::Result<()> { // crate::config::set_logger(Some(tracing::level_filters::LevelFilter::TRACE), None); let joiner_addr = ([127, 0, 0, 1], 10001).into(); @@ -1645,44 +1663,58 @@ mod tests { target: joiner_pkloc.clone(), msg: ConnectResponse::AcceptedBy { accepted: i > 3, - acceptor, + acceptor: acceptor.clone(), joiner: joiner_peer_id.clone(), }, }; test.transport .inbound_msg( gw_addr, - NetMessage::V1(NetMessageV1::Connect(forward_response)), + NetMessage::V1(NetMessageV1::Connect(forward_response.clone())), ) .await; if i > 3 { // Create the successful connection - let (remote, ev) = tokio::time::timeout( - Duration::from_secs(2), - test.transport.outbound_recv.recv(), - ) - .await? - .ok_or(anyhow!("Failed to receive event"))?; - let ConnectionEvent::ConnectionStart { - open_connection, .. - } = ev; - let out_symm_key = Aes128Gcm::new_from_slice(&[0; 16]).unwrap(); - let in_symm_key = Aes128Gcm::new_from_slice(&[1; 16]).unwrap(); - let (conn, out, inb) = PeerConnection::new_remote_test( - remote, - joiner_addr, - out_symm_key, - in_symm_key.clone(), - ); - test.transport - .packet_senders - .insert(remote, (in_symm_key, out)); - test.transport.packet_receivers.push(inb); - open_connection - .send(Ok(conn)) - .map_err(|_| anyhow!("failed to open conn"))?; - tracing::info!(conn_num = %i, %remote, "Forward response sent"); + async fn establish_conn( + test: &mut TestVerifier, + i: usize, + joiner_addr: SocketAddr, + ) -> Result<(), anyhow::Error> { + let (remote, ev) = tokio::time::timeout( + Duration::from_secs(10), + test.transport.outbound_recv.recv(), + ) + .await + .inspect_err(|error| { + tracing::error!(%error, conn_num = %i, "failed while receiving connection events"); + }) + .map_err(|_| anyhow!("time out"))? + .ok_or( anyhow!("Failed to receive event"))?; + let ConnectionEvent::ConnectionStart { + open_connection, .. + } = ev; + let out_symm_key = Aes128Gcm::new_from_slice(&[0; 16]).unwrap(); + let in_symm_key = Aes128Gcm::new_from_slice(&[1; 16]).unwrap(); + let (conn, out, inb) = PeerConnection::new_remote_test( + remote, + joiner_addr, + out_symm_key, + in_symm_key.clone(), + ); + test.transport + .packet_senders + .insert(remote, (in_symm_key, out)); + test.transport.packet_receivers.push(inb); + tracing::info!(conn_num = %i, %remote, "Connection established at remote"); + open_connection + .send(Ok(conn)) + .map_err(|_| anyhow!("failed to open conn"))?; + tracing::info!(conn_num = %i, "Returned open conn"); + Ok(()) + } + + establish_conn(&mut test, i, joiner_addr).await?; } } @@ -1693,16 +1725,20 @@ mod tests { let mut conn_count = 0; let mut gw_rejected = false; for conn_num in 3..Ring::DEFAULT_MAX_HOPS_TO_LIVE { - let event = tokio::time::timeout(Duration::from_secs(2), handler.wait_for_events()) - .await??; + let conn_num = conn_num + 2; + let event = + tokio::time::timeout(Duration::from_secs(60), handler.wait_for_events()) + .await + .inspect_err(|_| { + tracing::error!(%conn_num, "failed while waiting for events"); + })? + .inspect_err(|error| { + tracing::error!(%error, %conn_num, "failed while receiving events"); + })?; match event { - Event::OutboundConnectionSuccessful { - peer_id, - connection, - } => { - tracing::info!(%peer_id, %conn_num, "Connection established"); + Event::OutboundConnectionSuccessful { peer_id, .. } => { + tracing::info!(%peer_id, %conn_num, "Connection established at peer"); conn_count += 1; - drop(connection); } Event::OutboundGatewayConnectionRejected { peer_id } => { tracing::info!(%peer_id, "Gateway connection rejected"); diff --git a/crates/core/src/node/network_bridge/p2p_protoc.rs b/crates/core/src/node/network_bridge/p2p_protoc.rs index ff6bf2158..02782bcd2 100644 --- a/crates/core/src/node/network_bridge/p2p_protoc.rs +++ b/crates/core/src/node/network_bridge/p2p_protoc.rs @@ -1,5 +1,5 @@ use super::{ConnectionError, EventLoopNotificationsReceiver, NetworkBridge}; -use crate::message::NetMessageV1; +use crate::message::{NetMessageV1, QueryResult}; use dashmap::DashSet; use either::{Either, Left, Right}; use futures::future::BoxFuture; @@ -257,6 +257,10 @@ impl P2pConnManager { ) .await?; } + NodeEvent::QueryConnections { callback } => { + let connections = self.connections.keys().cloned().collect(); + callback.send(QueryResult::Connections(connections)).await?; + } NodeEvent::Disconnect { cause } => { tracing::info!( "Disconnecting from network{}", diff --git a/crates/core/src/node/p2p_impl.rs b/crates/core/src/node/p2p_impl.rs index da677ab25..77b400e9b 100644 --- a/crates/core/src/node/p2p_impl.rs +++ b/crates/core/src/node/p2p_impl.rs @@ -3,13 +3,12 @@ use std::sync::Arc; use tracing::Instrument; use super::{ - client_event_handling, network_bridge::{ event_loop_notification_channel, p2p_protoc::P2pConnManager, EventLoopNotificationsReceiver, }, NetEventRegister, PeerId, }; -use crate::ring::ConnectionManager; +use crate::{client_events::client_event_handling, ring::ConnectionManager}; use crate::{ client_events::{combinator::ClientEventsCombinator, BoxedClient}, config::GlobalExecutor, diff --git a/crates/core/src/node/testing_impl.rs b/crates/core/src/node/testing_impl.rs index be8c9ad81..8e18804f3 100644 --- a/crates/core/src/node/testing_impl.rs +++ b/crates/core/src/node/testing_impl.rs @@ -17,7 +17,10 @@ use tracing::{info, Instrument}; #[cfg(feature = "trace-ot")] use crate::tracing::CombinedRegister; use crate::{ - client_events::test::{MemoryEventsGen, RandomEventGenerator}, + client_events::{ + client_event_handling, + test::{MemoryEventsGen, RandomEventGenerator}, + }, config::{ConfigArgs, GlobalExecutor}, contract::{ self, ContractHandlerChannel, ExecutorToEventLoopChannel, NetworkEventListenerHalve, @@ -786,7 +789,7 @@ where }; let (node_controller_tx, node_controller_rx) = tokio::sync::mpsc::channel(1); GlobalExecutor::spawn( - super::client_event_handling( + client_event_handling( config.op_manager.clone(), config.user_events.take().expect("should be set"), client_responses, @@ -888,6 +891,9 @@ where tracing::info!(peer = %peer_key, "Shutting down node"); return Ok(()); } + NodeEvent::QueryConnections { .. } => { + unimplemented!() + } }, Err(err) => { super::report_result( diff --git a/crates/core/src/operations.rs b/crates/core/src/operations.rs index 0987fa2bf..07bacb9f9 100644 --- a/crates/core/src/operations.rs +++ b/crates/core/src/operations.rs @@ -118,7 +118,7 @@ where let id = *msg.id(); tracing::debug!(%id, "updated op state"); if let Some(target) = msg.target() { - tracing::debug!(%id, "sending updated op state"); + tracing::debug!(%id, %target, "sending updated op state"); network_bridge.send(&target.peer, msg).await?; } op_manager.push(id, updated_state).await?; @@ -305,7 +305,12 @@ impl From> for OpError { } /// If the contract is not found, it will try to get it first if the `try_get` parameter is set. -async fn start_subscription_request(op_manager: &OpManager, key: ContractKey, try_get: bool) { +async fn start_subscription_request( + op_manager: &OpManager, + key: ContractKey, + try_get: bool, + skip_list: Vec, +) { let sub_op = subscribe::start_op(key); if let Err(error) = subscribe::request_subscribe(op_manager, sub_op).await { if !try_get { @@ -315,7 +320,7 @@ async fn start_subscription_request(op_manager: &OpManager, key: ContractKey, tr if let OpError::ContractError(ContractError::ContractNotFound(key)) = &error { tracing::debug!(%key, "Contract not found, trying to get it first"); let get_op = get::start_op(*key, true); - if let Err(error) = get::request_get(op_manager, get_op).await { + if let Err(error) = get::request_get(op_manager, get_op, skip_list).await { tracing::warn!(%error, "Error getting contract"); } } else { @@ -328,7 +333,7 @@ async fn has_contract(op_manager: &OpManager, key: ContractKey) -> Result Result Ok(true), - crate::contract::ContractHandlerEvent::GetResponse { - response: Ok(crate::contract::StoreResponse { state: None, .. }), - .. - } => Ok(false), - _ => Err(OpError::UnexpectedOpState), + _ => Ok(false), } } diff --git a/crates/core/src/operations/connect.rs b/crates/core/src/operations/connect.rs index 4a7140fa3..27079e859 100644 --- a/crates/core/src/operations/connect.rs +++ b/crates/core/src/operations/connect.rs @@ -172,7 +172,7 @@ impl Operation for ConnectOp { return Err(OpError::RingError(crate::ring::RingError::NoLocation)); }; let mut skip_list = skip_list.clone(); - skip_list.push(query_target.peer.clone()); + skip_list.extend([this_peer.clone(), query_target.peer.clone()]); if this_peer == &query_target.peer { // this peer should be the original target queries tracing::debug!( diff --git a/crates/core/src/operations/get.rs b/crates/core/src/operations/get.rs index 2829fd5cc..af88085a2 100644 --- a/crates/core/src/operations/get.rs +++ b/crates/core/src/operations/get.rs @@ -1,3 +1,4 @@ +use std::fmt::Display; use std::pin::Pin; use std::{future::Future, time::Instant}; @@ -43,16 +44,19 @@ pub(crate) fn start_op(key: ContractKey, fetch_contract: bool) -> GetOp { } /// Request to get the current value from a contract. -pub(crate) async fn request_get(op_manager: &OpManager, get_op: GetOp) -> Result<(), OpError> { +pub(crate) async fn request_get( + op_manager: &OpManager, + get_op: GetOp, + skip_list: Vec, +) -> Result<(), OpError> { let (target, id) = if let Some(GetState::PrepareRequest { key, id, .. }) = &get_op.state { - const EMPTY: &[PeerId] = &[]; // the initial request must provide: // - a location in the network where the contract resides // - and the key of the contract value to get ( op_manager .ring - .closest_potentially_caching(key, EMPTY) + .closest_potentially_caching(key, skip_list.as_slice()) .into_iter() .next() .ok_or(RingError::EmptyRing)?, @@ -86,6 +90,7 @@ pub(crate) async fn request_get(op_manager: &OpManager, get_op: GetOp) -> Result key, target: target.clone(), fetch_contract, + skip_list, }; let op = GetOp { @@ -127,6 +132,33 @@ enum GetState { }, } +impl Display for GetState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + GetState::ReceivedRequest => write!(f, "ReceivedRequest"), + GetState::PrepareRequest { + key, + id, + fetch_contract, + } => { + write!( + f, + "PrepareRequest(key: {}, id: {}, fetch_contract: {})", + key, id, fetch_contract + ) + } + GetState::AwaitingResponse { + requester, + fetch_contract, + retries, + current_hop, + } => { + write!(f, "AwaitingResponse(requester: {:?}, fetch_contract: {}, retries: {}, current_hop: {})", requester, fetch_contract, retries, current_hop) + } + } + } +} + struct GetStats { /// Next peer in get path to be targeted next_peer: Option, @@ -279,6 +311,7 @@ impl Operation for GetOp { id, target, fetch_contract, + skip_list, } => { // fast tracked from the request_get func debug_assert!(matches!( @@ -294,6 +327,8 @@ impl Operation for GetOp { first_response_time: None, })); let own_loc = op_manager.ring.connection_manager.own_location(); + let mut new_skip_list = skip_list.clone(); + new_skip_list.push(own_loc.peer.clone()); return_msg = Some(GetMsg::SeekNode { key: *key, id: *id, @@ -301,7 +336,7 @@ impl Operation for GetOp { sender: own_loc.clone(), fetch_contract: *fetch_contract, htl: op_manager.ring.max_hops_to_live, - skip_list: vec![own_loc.peer], + skip_list: new_skip_list, }); } GetMsg::SeekNode { @@ -323,10 +358,13 @@ impl Operation for GetOp { s.next_peer = Some(this_peer.clone()); } + let mut new_skip_list = skip_list.clone(); + new_skip_list.push(this_peer.clone().peer); + let get_result = op_manager .notify_contract_handler(ContractHandlerEvent::GetQuery { key, - fetch_contract, + return_contract_code: fetch_contract, }) .await; @@ -340,6 +378,13 @@ impl Operation for GetOp { }), }) => (key, contract, state), _ => { + tracing::debug!( + tx = %id, + %key, + %this_peer, + "Contract not found @ peer {}, retrying with other peers", + sender.peer + ); return try_forward_or_return( id, key, @@ -444,8 +489,37 @@ impl Operation for GetOp { htl: current_hop, skip_list: new_skip_list.clone(), }); + } else if let Some(requester_peer) = requester.clone() { + tracing::warn!( + tx = %id, + %key, + %this_peer, + target = %requester_peer, + "No other peers found while trying to get the contract, returning response to requester" + ); + return_msg = Some(GetMsg::ReturnGet { + id: *id, + key: *key, + value: StoreResponse { + state: None, + contract: None, + }, + sender: this_peer.clone(), + target: requester_peer, + skip_list: new_skip_list.clone(), + }); } else { - return Err(RingError::NoCachingPeers(*key).into()); + tracing::error!( + tx = %id, + "Failed getting a value for contract {}, reached max retries", + key + ); + return_msg = None; + result = Some(GetResult { + key: *key, + state: WrappedState::new(vec![]), + contract: None, + }); } new_state = Some(GetState::AwaitingResponse { retries: retries + 1, @@ -459,10 +533,45 @@ impl Operation for GetOp { "Failed getting a value for contract {}, reached max retries", key ); - return Err(OpError::MaxRetriesExceeded( - *id, - id.transaction_type(), - )); + if let Some(requester_peer) = requester.clone() { + tracing::warn!( + tx = %id, + %key, + %this_peer, + target = %requester_peer, + "No other peers found while trying to get the contract, returning response to requester" + ); + return_msg = Some(GetMsg::ReturnGet { + id: *id, + key: *key, + value: StoreResponse { + state: None, + contract: None, + }, + sender: this_peer.clone(), + target: requester_peer, + skip_list: skip_list.clone(), + }); + new_state = None; + } else { + tracing::error!( + tx = %id, + "Failed getting a value for contract {}, reached max retries", + key + ); + return_msg = None; + new_state = Some(GetState::AwaitingResponse { + retries: retries + 1, + fetch_contract, + requester, + current_hop, + }); + result = Some(GetResult { + key: *key, + state: WrappedState::new(vec![]), + contract: None, + }); + } } } Some(GetState::ReceivedRequest) => { @@ -475,8 +584,8 @@ impl Operation for GetOp { state: None, contract: None, }, - sender: sender.clone(), - target: target.clone(), + sender: this_peer.clone(), + target: sender.clone(), skip_list: skip_list.clone(), }); } @@ -497,6 +606,8 @@ impl Operation for GetOp { } => { let id = *id; let key = *key; + + tracing::info!(tx = %id, %key, "Received get response with state: {:?}", self.state.as_ref().unwrap()); let require_contract = matches!( self.state, Some(GetState::AwaitingResponse { @@ -505,8 +616,16 @@ impl Operation for GetOp { }) ); + let requester = if let Some(GetState::AwaitingResponse { requester, .. }) = + self.state.as_ref() + { + requester.clone() + } else { + return Err(OpError::UnexpectedOpState); + }; + // received a response with a contract value - if require_contract && contract.is_none() { + if require_contract && contract.is_none() && requester.is_some() { // no contract, consider this like an error ignoring the incoming update value tracing::warn!( tx = %id, @@ -516,6 +635,17 @@ impl Operation for GetOp { let mut new_skip_list = skip_list.clone(); new_skip_list.push(sender.peer.clone()); + + let requester = requester.unwrap(); + + tracing::warn!( + tx = %id, + %key, + at = %sender.peer, + target = %requester, + "Contract not received while required, returning response to requester", + ); + op_manager .notify_op_change( NetMessage::from(GetMsg::ReturnGet { @@ -526,7 +656,7 @@ impl Operation for GetOp { contract: None, }, sender: sender.clone(), - target: target.clone(), + target: requester.clone(), skip_list: new_skip_list, }), OpEnum::Get(GetOp { @@ -565,7 +695,15 @@ impl Operation for GetOp { op_manager.ring.is_seeding_contract(&key); if !is_subscribed_contract && should_subscribe { tracing::debug!(tx = %id, %key, peer = %op_manager.ring.connection_manager.get_peer_key().unwrap(), "Contract not cached @ peer, caching"); - super::start_subscription_request(op_manager, key, false).await; + let mut new_skip_list = skip_list.clone(); + new_skip_list.push(sender.peer.clone()); + super::start_subscription_request( + op_manager, + key, + false, + new_skip_list, + ) + .await; } } ContractHandlerEvent::PutResponse { @@ -578,6 +716,16 @@ impl Operation for GetOp { let mut new_skip_list = skip_list.clone(); new_skip_list.push(sender.peer.clone()); + let requester = requester.unwrap(); + + tracing::warn!( + tx = %id, + %key, + %sender.peer, + target = %requester, + "Failed put at executor, returning response to requester", + ); + op_manager .notify_op_change( NetMessage::from(GetMsg::ReturnGet { @@ -588,7 +736,7 @@ impl Operation for GetOp { contract: None, }, sender: sender.clone(), - target: target.clone(), + target: requester.clone(), skip_list: new_skip_list, }), OpEnum::Get(GetOp { @@ -633,9 +781,10 @@ impl Operation for GetOp { contract: contract.clone(), }, sender: target.clone(), - target: requester, + target: requester.clone(), skip_list: skip_list.clone(), }); + tracing::debug!(tx = %id, %key, target = %requester, "Returning contract to requester"); result = Some(GetResult { key, state: value.clone(), @@ -712,15 +861,66 @@ async fn try_forward_or_return( new_skip_list.push(this_peer.peer.clone()); let new_htl = htl - 1; - if new_htl == 0 { + + let new_target = if new_htl == 0 { tracing::warn!( tx = %id, sender = %sender.peer, - "The maximum hops has been exceeded, sending error \ - back to the node", + "The maximum hops have been exceeded, sending response back to the node", + ); + None + } else { + match op_manager + .ring + .closest_potentially_caching(&key, new_skip_list.as_slice()) + { + Some(target) => Some(target), + None => { + tracing::warn!( + tx = %id, + %key, + this_peer = %this_peer.peer, + "No other peers found while trying to get the contract", + ); + None + } + } + }; + + if let Some(target) = new_target { + tracing::debug!( + tx = %id, + "Forwarding get request to {}", + target.peer + ); + build_op_result( + id, + Some(GetState::AwaitingResponse { + requester: Some(sender), + retries: 0, + fetch_contract, + current_hop: new_htl, + }), + Some(GetMsg::SeekNode { + id, + key, + fetch_contract, + sender: this_peer, + target, + htl: new_htl, + skip_list: new_skip_list, + }), + None, + stats, + ) + } else { + tracing::debug!( + tx = %id, + "Cannot find any other peers to forward the get request to, returning get response to {}", + sender.peer ); - return build_op_result( + build_op_result( id, None, Some(GetMsg::ReturnGet { @@ -731,52 +931,13 @@ async fn try_forward_or_return( contract: None, }, sender: op_manager.ring.connection_manager.own_location(), - target: sender, // return to requester + target: sender, skip_list: new_skip_list, }), None, stats, - ); + ) } - - let Some(new_target) = op_manager - .ring - .closest_potentially_caching(&key, new_skip_list.as_slice()) - else { - tracing::warn!( - tx = %id, - %key, - this_peer = %this_peer.peer, - "No other peers found while trying getting contract", - ); - return Err(OpError::RingError(RingError::NoCachingPeers(key))); - }; - - tracing::debug!( - tx = %id, - "Forwarding get request to {}", - new_target.peer - ); - build_op_result( - id, - Some(GetState::AwaitingResponse { - requester: Some(sender), - retries: 0, - fetch_contract, - current_hop: new_htl, - }), - Some(GetMsg::SeekNode { - id, - key, - fetch_contract, - sender: this_peer, - target: new_target, - htl: new_htl, - skip_list: new_skip_list, - }), - None, - stats, - ) } mod messages { @@ -793,6 +954,7 @@ mod messages { target: PeerKeyLocation, key: ContractKey, fetch_contract: bool, + skip_list: Vec, }, SeekNode { id: Transaction, diff --git a/crates/core/src/operations/put.rs b/crates/core/src/operations/put.rs index 6c15140ab..a60cc9f18 100644 --- a/crates/core/src/operations/put.rs +++ b/crates/core/src/operations/put.rs @@ -57,7 +57,7 @@ impl PutOp { } pub(super) fn finalized(&self) -> bool { - self.state.is_none() + self.state.is_none() || matches!(self.state, Some(PutState::Finished { .. })) } pub(super) fn to_host_result(&self) -> HostResult { @@ -176,7 +176,8 @@ impl Operation for PutOp { sender, } => { let key = contract.key(); - let is_subscribed_contract = op_manager.ring.is_seeding_contract(&key); + let mut is_subscribed_contract = op_manager.ring.is_seeding_contract(&key); + let should_seed = op_manager.ring.should_seed(&key); tracing::debug!( tx = %id, @@ -186,8 +187,18 @@ impl Operation for PutOp { "Puttting contract at target peer", ); - if is_subscribed_contract || op_manager.ring.should_seed(&key) { + let mut already_put = false; + if is_subscribed_contract || should_seed { + if !is_subscribed_contract { + let skip_list = vec![sender.peer.clone(), target.peer.clone()]; + super::start_subscription_request(op_manager, key, true, skip_list) + .await; + // FIXME: we start subscription request, but that does not mean we are already seeding + op_manager.ring.seed_contract(key); + is_subscribed_contract = true; + } tracing::debug!(tx = %id, "Attempting contract value update"); + put_contract( op_manager, key, @@ -196,9 +207,11 @@ impl Operation for PutOp { contract, ) .await?; + already_put = true; + tracing::debug!( tx = %id, - "Successfully updated a value for contract {} @ {:?}", + "Successfully updated value for contract {} @ {:?}", key, target.location ); @@ -206,7 +219,7 @@ impl Operation for PutOp { let last_hop = if let Some(new_htl) = htl.checked_sub(1) { // forward changes in the contract to nodes closer to the contract location, if possible - let put_here = forward_put( + forward_put( op_manager, conn_manager, contract, @@ -215,31 +228,39 @@ impl Operation for PutOp { new_htl, vec![sender.peer.clone()], ) - .await; - if put_here && !is_subscribed_contract { - // if already subscribed the value was already put and merging succeeded + .await + } else { + // should put in this location, no hops left + true + }; + + if last_hop && !is_subscribed_contract { + tracing::debug!( + tx = %id, + %key, + peer = %op_manager.ring.connection_manager.get_peer_key().unwrap(), + "Caching contract locally as it's not already seeded" + ); + + if should_seed && !is_subscribed_contract { + let skip_list = vec![sender.peer.clone()]; + super::start_subscription_request(op_manager, key, true, skip_list) + .await; + // FIXME: we start subscription request, but that does not mean we are already seeding + op_manager.ring.seed_contract(key); + } + + if !already_put { put_contract( op_manager, key, value.clone(), - RelatedContracts::default(), + related_contracts.clone(), contract, ) .await?; } - put_here - } else { - // should put in this location, no hops left - put_contract( - op_manager, - key, - value.clone(), - RelatedContracts::default(), - contract, - ) - .await?; - true - }; + } let broadcast_to = op_manager.get_broadcast_targets(&key, &sender.peer); @@ -379,7 +400,8 @@ impl Operation for PutOp { let is_subscribed_contract = op_manager.ring.is_seeding_contract(&key); if !is_subscribed_contract && op_manager.ring.should_seed(&key) { tracing::debug!(tx = %id, %key, peer = %op_manager.ring.connection_manager.get_peer_key().unwrap(), "Contract not cached @ peer, caching"); - super::start_subscription_request(op_manager, key, true).await; + super::start_subscription_request(op_manager, key, true, vec![]) + .await; } tracing::info!( tx = %id, @@ -422,7 +444,9 @@ impl Operation for PutOp { ); let should_seed = op_manager.ring.should_seed(&key); + let mut already_put = false; if should_seed { + tracing::debug!(%key, "Seeding contracting"); // after the contract has been cached, push the update query put_contract( op_manager, @@ -432,6 +456,7 @@ impl Operation for PutOp { contract, ) .await?; + already_put = true; } // if successful, forward to the next closest peers (if any) @@ -446,22 +471,33 @@ impl Operation for PutOp { new_value.clone(), *id, new_htl, - new_skip_list, + new_skip_list.clone(), ) .await; let is_seeding_contract = op_manager.ring.is_seeding_contract(&key); if put_here && !is_seeding_contract && should_seed { - // if already subscribed the value was already put and merging succeeded - put_contract( - op_manager, - key, - new_value.clone(), - RelatedContracts::default(), - contract, - ) - .await?; - let (dropped_contract, old_subscribers) = - op_manager.ring.seed_contract(key); + if !already_put { + // if already subscribed the value was already put and merging succeeded + put_contract( + op_manager, + key, + new_value.clone(), + RelatedContracts::default(), + contract, + ) + .await?; + } + let (dropped_contract, old_subscribers) = { + super::start_subscription_request( + op_manager, + key, + true, + new_skip_list, + ) + .await; + // FIXME: we start subscription request, but that does not mean we are already seeding + op_manager.ring.seed_contract(key) + }; if let Some(key) = dropped_contract { for subscriber in old_subscribers { conn_manager @@ -482,7 +518,7 @@ impl Operation for PutOp { } } put_here - } else { + } else if !already_put { // should put in this location, no hops left put_contract( op_manager, @@ -493,6 +529,8 @@ impl Operation for PutOp { ) .await?; true + } else { + true }; let broadcast_to = op_manager.get_broadcast_targets(&key, &sender.peer); @@ -744,10 +782,11 @@ async fn put_contract( new_value: Ok(new_val), }) => Ok(new_val), Ok(ContractHandlerEvent::PutResponse { - new_value: Err(_err), + new_value: Err(err), }) => { - // return Err(OpError::from(ContractError::StorageError(err))); - todo!("not a valid value update, notify back to requester") + tracing::error!(%key, "Failed to update contract value: {}", err); + Err(OpError::from(err)) + // TODO: not a valid value update, notify back to requester } Err(err) => Err(err.into()), Ok(_) => Err(OpError::UnexpectedOpState), @@ -933,7 +972,7 @@ mod messages { Self::SeekNode { .. } => write!(f, "SeekNode(id: {id})"), Self::RequestPut { .. } => write!(f, "RequestPut(id: {id})"), Self::Broadcasting { .. } => write!(f, "Broadcasting(id: {id})"), - Self::SuccessfulPut { .. } => write!(f, "SusscessfulUpdate(id: {id})"), + Self::SuccessfulPut { .. } => write!(f, "SuccessfulPut(id: {id})"), Self::PutForward { .. } => write!(f, "PutForward(id: {id})"), Self::AwaitPut { .. } => write!(f, "AwaitPut(id: {id})"), Self::BroadcastTo { .. } => write!(f, "BroadcastTo(id: {id})"), diff --git a/crates/core/src/operations/subscribe.rs b/crates/core/src/operations/subscribe.rs index 6a87860e6..9f9d69725 100644 --- a/crates/core/src/operations/subscribe.rs +++ b/crates/core/src/operations/subscribe.rs @@ -2,7 +2,7 @@ use std::future::Future; use std::pin::Pin; use freenet_stdlib::{ - client_api::{ErrorKind, HostResponse}, + client_api::{ContractResponse, ErrorKind, HostResponse}, prelude::*, }; use serde::{Deserialize, Serialize}; @@ -36,7 +36,9 @@ enum SubscribeState { upstream_subscriber: Option, current_hop: usize, }, - Completed {}, + Completed { + key: ContractKey, + }, } pub(crate) struct SubscribeResult {} @@ -45,7 +47,7 @@ impl TryFrom for SubscribeResult { type Error = OpError; fn try_from(value: SubscribeOp) -> Result { - if let Some(SubscribeState::Completed {}) = value.state { + if let Some(SubscribeState::Completed { .. }) = value.state { Ok(SubscribeResult {}) } else { Err(OpError::UnexpectedOpState) @@ -66,6 +68,7 @@ pub(crate) async fn request_subscribe( ) -> Result<(), OpError> { let (target, _id) = if let Some(SubscribeState::PrepareRequest { id, key }) = &sub_op.state { if !super::has_contract(op_manager, *key).await? { + tracing::debug!(%key, "Contract not found, trying other peer"); return Err(OpError::ContractError(ContractError::ContractNotFound( *key, ))); @@ -122,8 +125,13 @@ impl SubscribeOp { } pub(super) fn to_host_result(&self) -> HostResult { - if let Some(SubscribeState::Completed {}) = self.state { - Ok(HostResponse::Ok) + if let Some(SubscribeState::Completed { key }) = self.state { + Ok(HostResponse::ContractResponse( + ContractResponse::SubscribeResponse { + key, + subscribed: true, + }, + )) } else { Err(ErrorKind::OperationError { cause: "subscribe didn't finish successfully".into(), @@ -380,9 +388,12 @@ impl Operation for SubscribeOp { provider = %sender.peer, "Subscribed to contract" ); - op_manager.ring.register_subscription(key, sender.clone()); + if op_manager.ring.add_subscriber(key, sender.clone()).is_err() { + // concurrently it reached max number of subscribers for this contract + return Err(OpError::UnexpectedOpState); + } - new_state = Some(SubscribeState::Completed {}); + new_state = Some(SubscribeState::Completed { key: *key }); if let Some(upstream_subscriber) = upstream_subscriber { return_msg = Some(SubscribeMsg::ReturnSub { id: *id, diff --git a/crates/core/src/operations/update.rs b/crates/core/src/operations/update.rs index 8d38a67a4..16fd1bd95 100644 --- a/crates/core/src/operations/update.rs +++ b/crates/core/src/operations/update.rs @@ -499,10 +499,11 @@ async fn update_contract( state: WrappedState, related_contracts: RelatedContracts<'static>, ) -> Result { + let update_data = UpdateData::State(State::from(state)); match op_manager .notify_contract_handler(ContractHandlerEvent::UpdateQuery { key, - state, + data: update_data, related_contracts, }) .await diff --git a/crates/core/src/ring.rs b/crates/core/src/ring.rs index b4e62e057..367dfcdf6 100644 --- a/crates/core/src/ring.rs +++ b/crates/core/src/ring.rs @@ -371,21 +371,24 @@ impl Ring { let seed_score = self.calculate_seed_score(&key); let mut old_subscribers = vec![]; let mut contract_to_drop = None; - if self.seeding_contract.len() < Self::MAX_SEEDING_CONTRACTS { - let dropped_contract = *self + + if self.seeding_contract.len() <= Self::MAX_SEEDING_CONTRACTS { + if let Some(dropped_contract) = self .seeding_contract .iter() .min_by_key(|v| *v.value()) - .unwrap() - .key(); - self.seeding_contract.remove(&dropped_contract); - if let Some((_, mut subscribers_of_contract)) = - self.subscribers.remove(&dropped_contract) + .map(|entry| *entry.key()) { - std::mem::swap(&mut subscribers_of_contract, &mut old_subscribers); + self.seeding_contract.remove(&dropped_contract); + if let Some((_, mut subscribers_of_contract)) = + self.subscribers.remove(&dropped_contract) + { + std::mem::swap(&mut subscribers_of_contract, &mut old_subscribers); + } + contract_to_drop = Some(dropped_contract); } - contract_to_drop = Some(dropped_contract); } + self.seeding_contract.insert(key, seed_score); (contract_to_drop, old_subscribers) } @@ -471,14 +474,6 @@ impl Ring { self.router.write().add_event(event); } - pub fn register_subscription(&self, contract: &ContractKey, subscriber: PeerKeyLocation) { - self.subscribers - .entry(*contract) - .or_insert(Vec::with_capacity(Self::TOTAL_MAX_SUBSCRIPTIONS)) - .value_mut() - .push(subscriber); - } - /// Will return an error in case the max number of subscribers has been added. pub fn add_subscriber( &self, @@ -611,13 +606,14 @@ impl Ring { // remove all missing candidates which have been retried missing.split_off(&Reverse(retry_missing_candidates_until)); + // avoid connecting to the same peer multiple times + let mut skip_list = missing.values().collect::>(); + let this_peer = self.connection_manager.get_peer_key().unwrap(); + skip_list.push(&this_peer); + if let Some(ideal_location) = pending_conn_adds.pop_front() { live_tx = self - .acquire_new( - ideal_location, - &missing.values().collect::>(), - ¬ifier, - ) + .acquire_new(ideal_location, &skip_list, ¬ifier) .await .map_err(|error| { tracing::debug!(?error, "Shutting down connection maintenance task"); diff --git a/crates/core/src/server/path_handlers.rs b/crates/core/src/server/path_handlers.rs index be9e451ee..f8f94d83e 100644 --- a/crates/core/src/server/path_handlers.rs +++ b/crates/core/src/server/path_handlers.rs @@ -56,7 +56,7 @@ pub(super) async fn contract_home( req: Box::new( ContractRequest::Get { key, - fetch_contract: true, + return_contract_code: true, } .into(), ), diff --git a/crates/core/src/transport/connection_handler.rs b/crates/core/src/transport/connection_handler.rs index d395a1c0e..b53e7270f 100644 --- a/crates/core/src/transport/connection_handler.rs +++ b/crates/core/src/transport/connection_handler.rs @@ -1,4 +1,24 @@ use std::collections::BTreeMap; + +type GatewayConnectionFuture = Box< + dyn Future< + Output = Result< + ( + RemoteConnection, + InboundRemoteConnection, + PacketData, + ), + TransportError, + >, + > + Send + + 'static, +>; + +type TraverseNatFuture = Box< + dyn Future> + + Send + + 'static, +>; use std::net::{IpAddr, SocketAddr}; use std::pin::Pin; use std::sync::atomic::AtomicU32; @@ -9,15 +29,16 @@ use crate::transport::crypto::TransportSecretKey; use crate::transport::packet_data::{AssymetricRSA, UnknownEncryption}; use crate::transport::symmetric_message::OutboundConnection; use aes_gcm::{Aes128Gcm, KeyInit}; +use futures::FutureExt; use futures::{ stream::{FuturesUnordered, StreamExt}, Future, }; -use futures::{FutureExt, TryFutureExt}; use tokio::net::UdpSocket; use tokio::sync::{mpsc, oneshot}; use tokio::task; -use tracing::{span, Instrument}; +use tracing::span; +use tracing::Instrument; use super::{ crypto::{TransportKeypair, TransportPublicKey}, @@ -266,11 +287,15 @@ impl UdpPacketsListener { } let packet_data = PacketData::from_buf(&buf[..size]); let (gw_ongoing_connection, packets_sender) = self.gateway_connection(packet_data, remote_addr); - let task = tokio::spawn(gw_ongoing_connection - .instrument(tracing::span!(tracing::Level::DEBUG, "gateway_connection")) - .map_err(move |error| { - (error, remote_addr) - })); + let task = tokio::spawn({ + let span = tracing::span!(tracing::Level::DEBUG, "gateway_connection"); + async move { + match Pin::from(gw_ongoing_connection).await { + Ok(result) => Ok(result), + Err(error) => Err((error, remote_addr)) + } + }.instrument(span) + }); ongoing_gw_connections.insert(remote_addr, packets_sender); gw_connection_tasks.push(task); } @@ -351,9 +376,15 @@ impl UdpPacketsListener { let (ongoing_connection, packets_sender) = self.traverse_nat( remote_addr, remote_public_key, ); - let task = tokio::spawn(ongoing_connection.map_err(move |error| { - (error, remote_addr) - }).instrument(span!(tracing::Level::DEBUG, "traverse_nat"))); + let task = tokio::spawn({ + let span = span!(tracing::Level::DEBUG, "traverse_nat"); + async move { + match Pin::from(ongoing_connection).await { + Ok(result) => Ok(result), + Err(error) => Err((error, remote_addr)) + } + }.instrument(span) + }); connection_tasks.push(task); ongoing_connections.insert(remote_addr, (packets_sender, open_connection)); }, @@ -366,17 +397,7 @@ impl UdpPacketsListener { remote_intro_packet: PacketData, remote_addr: SocketAddr, ) -> ( - impl Future< - Output = Result< - ( - RemoteConnection, - InboundRemoteConnection, - PacketData, - ), - TransportError, - >, - > + Send - + 'static, + GatewayConnectionFuture, mpsc::Sender>, ) { let secret = self.this_peer_keypair.secret.clone(); @@ -483,7 +504,7 @@ impl UdpPacketsListener { tracing::debug!("returning connection at gw"); Ok((remote_conn, inbound_conn, outbound_ack_packet)) }; - (f, inbound_from_remote) + (Box::new(f), inbound_from_remote) } // TODO: this value should be set given exponential backoff and max timeout @@ -498,9 +519,7 @@ impl UdpPacketsListener { remote_addr: SocketAddr, remote_public_key: TransportPublicKey, ) -> ( - impl Future> - + Send - + 'static, + TraverseNatFuture, mpsc::Sender>, ) { // Constants for exponential backoff @@ -786,7 +805,7 @@ impl UdpPacketsListener { cause: "max connection attempts reached".into(), }) }; - (f, inbound_from_remote) + (Box::new(f), inbound_from_remote) } } diff --git a/crates/core/src/transport/rate_limiter.rs b/crates/core/src/transport/rate_limiter.rs index fce9635ca..f7d92d8aa 100644 --- a/crates/core/src/transport/rate_limiter.rs +++ b/crates/core/src/transport/rate_limiter.rs @@ -66,7 +66,7 @@ impl PacketRateLimiter { while self .packets .front() - .map_or(false, |&(_, time)| now - time > self.window_size) + .is_some_and(|&(_, time)| now - time > self.window_size) { let expired = self.packets.pop_front(); if let Some((size, _)) = expired { diff --git a/crates/core/src/transport/received_packet_tracker.rs b/crates/core/src/transport/received_packet_tracker.rs index fa3443703..21cecd4a2 100644 --- a/crates/core/src/transport/received_packet_tracker.rs +++ b/crates/core/src/transport/received_packet_tracker.rs @@ -88,7 +88,7 @@ impl ReceivedPacketTracker { while self .packet_id_time .front() - .map_or(false, |&(_, time)| time < remove_before) + .is_some_and(|&(_, time)| time < remove_before) { let expired = self.packet_id_time.pop_front(); if let Some((packet_id, _)) = expired { diff --git a/crates/core/src/util.rs b/crates/core/src/util.rs index 1e0908bd3..5405ebfd2 100644 --- a/crates/core/src/util.rs +++ b/crates/core/src/util.rs @@ -262,19 +262,19 @@ pub(crate) trait Contains { fn has_element(&self, target: &T) -> bool; } -impl<'x> Contains for &'x [PeerId] { +impl Contains for &[PeerId] { fn has_element(&self, target: &PeerId) -> bool { self.contains(target) } } -impl<'x> Contains for &'x [&PeerId] { +impl Contains for &[&PeerId] { fn has_element(&self, target: &PeerId) -> bool { self.contains(&target) } } -impl<'x> Contains for &'x Vec<&PeerId> { +impl Contains for &Vec<&PeerId> { fn has_element(&self, target: &PeerId) -> bool { self.contains(&target) } diff --git a/crates/core/src/wasm_runtime/contract.rs b/crates/core/src/wasm_runtime/contract.rs index c65aa05c6..074067727 100644 --- a/crates/core/src/wasm_runtime/contract.rs +++ b/crates/core/src/wasm_runtime/contract.rs @@ -19,16 +19,6 @@ pub(crate) trait ContractRuntimeInterface { related: &RelatedContracts<'_>, ) -> RuntimeResult; - /// Verify that a delta is valid - at least as much as possible. The goal is to prevent DDoS of - /// a contract by sending a large number of invalid delta updates. This allows peers - /// to verify a delta before forwarding it. - fn validate_delta( - &mut self, - key: &ContractKey, - parameters: &Parameters<'_>, - delta: &StateDelta<'_>, - ) -> RuntimeResult; - /// Determine whether this delta is a valid update for this contract. If it is, return the modified state, /// else return error. /// @@ -116,47 +106,6 @@ impl ContractRuntimeInterface for super::Runtime { Ok(is_valid) } - fn validate_delta<'a>( - &mut self, - key: &ContractKey, - parameters: &Parameters<'a>, - delta: &StateDelta<'a>, - ) -> RuntimeResult { - // todo: if we keep this hot in memory on next calls overwrite the buffer with new delta - let req_bytes = parameters.size() + delta.size(); - let running = self.prepare_contract_call(key, parameters, req_bytes)?; - let linear_mem = self.linear_mem(&running.instance)?; - - let param_buf_ptr = { - let mut param_buf = self.init_buf(&running.instance, parameters)?; - param_buf.write(parameters)?; - param_buf.ptr() - }; - let delta_buf_ptr = { - let mut delta_buf = self.init_buf(&running.instance, delta)?; - delta_buf.write(delta)?; - delta_buf.ptr() - }; - - let validate_func: TypedFunction<(i64, i64), FfiReturnTy> = running - .instance - .exports - .get_typed_function(&self.wasm_store, "validate_delta")?; - let is_valid = unsafe { - ContractInterfaceResult::from_raw( - validate_func.call( - &mut self.wasm_store, - param_buf_ptr as i64, - delta_buf_ptr as i64, - )?, - &linear_mem, - ) - .unwrap_validate_delta_res(linear_mem) - .map_err(Into::::into)? - }; - Ok(is_valid) - } - fn update_state( &mut self, key: &ContractKey, diff --git a/crates/core/src/wasm_runtime/delegate.rs b/crates/core/src/wasm_runtime/delegate.rs index 0ecf91b0a..2408387b4 100644 --- a/crates/core/src/wasm_runtime/delegate.rs +++ b/crates/core/src/wasm_runtime/delegate.rs @@ -203,7 +203,6 @@ impl Runtime { break; } OutboundDelegateMsg::RequestUserInput(req) => { - //results.push(OutboundDelegateMsg::RequestUserInput(req)); // Simulate user response changes after receiving the RequestUserInput let user_response = ClientResponse::new(serde_json::to_vec(&Response::Allowed).unwrap()); diff --git a/crates/core/src/wasm_runtime/runtime.rs b/crates/core/src/wasm_runtime/runtime.rs index 0015c0bff..df25dfb32 100644 --- a/crates/core/src/wasm_runtime/runtime.rs +++ b/crates/core/src/wasm_runtime/runtime.rs @@ -230,7 +230,7 @@ impl Runtime { .as_ref() .map(Ok) .unwrap_or_else(|| instance.exports.get_memory("memory"))?; - let req_pages = Bytes::from(req_bytes).try_into().unwrap(); + let req_pages: wasmer::Pages = Bytes::from(req_bytes).try_into().unwrap(); if memory.view(&self.wasm_store).size() < req_pages { if let Err(err) = memory.grow(&mut self.wasm_store, req_pages) { tracing::error!("wasm runtime failed with memory error: {err}"); diff --git a/crates/core/src/wasm_runtime/tests/contract.rs b/crates/core/src/wasm_runtime/tests/contract.rs index 70ebaf898..6b4266f2c 100644 --- a/crates/core/src/wasm_runtime/tests/contract.rs +++ b/crates/core/src/wasm_runtime/tests/contract.rs @@ -37,34 +37,6 @@ fn validate_state() -> Result<(), Box> { Ok(()) } -#[test] -fn validate_delta() -> Result<(), Box> { - let TestSetup { - contract_store, - delegate_store, - secrets_store, - contract_key, - temp_dir, - } = super::setup_test_contract(TEST_CONTRACT_1)?; - let mut runtime = Runtime::build(contract_store, delegate_store, secrets_store, false).unwrap(); - - let is_valid = runtime.validate_delta( - &contract_key, - &Parameters::from([].as_ref()), - &StateDelta::from([1, 2, 3, 4].as_ref()), - )?; - assert!(is_valid); - - let not_valid = !runtime.validate_delta( - &contract_key, - &Parameters::from([].as_ref()), - &StateDelta::from([1, 0, 0, 1].as_ref()), - )?; - assert!(not_valid); - std::mem::drop(temp_dir); - Ok(()) -} - #[test] fn update_state() -> Result<(), Box> { let TestSetup { diff --git a/crates/fdev/Cargo.toml b/crates/fdev/Cargo.toml index 2cedf1029..18bce56f6 100644 --- a/crates/fdev/Cargo.toml +++ b/crates/fdev/Cargo.toml @@ -20,6 +20,7 @@ either = { workspace = true } futures = { workspace = true } glob = "0.3" pico-args = "0.5" +prettytable-rs = "0.10" rand = { workspace = true } serde = "1" serde_json = "1" diff --git a/crates/fdev/src/commands.rs b/crates/fdev/src/commands.rs index e906aa3fd..9b8133d49 100644 --- a/crates/fdev/src/commands.rs +++ b/crates/fdev/src/commands.rs @@ -1,9 +1,4 @@ -use std::{ - fs::File, - io::Read, - net::{IpAddr, SocketAddr}, - path::PathBuf, -}; +use std::{fs::File, io::Read, net::SocketAddr, path::PathBuf}; use freenet::dev_tool::OperationMode; use freenet_stdlib::{ @@ -88,7 +83,8 @@ async fn put_contract( related_contracts, } .into(); - execute_command(request, other, config.address, config.port).await + let mut client = start_api_client(other).await?; + execute_command(request, &mut client).await } async fn put_delegate( @@ -127,7 +123,8 @@ For additional hardening is recommended to use a different cipher and nonce to e nonce, } .into(); - execute_command(request, other, config.address, config.port).await + let mut client = start_api_client(other).await?; + execute_command(request, &mut client).await } pub async fn update(config: UpdateConfig, other: BaseConfig) -> anyhow::Result<()> { @@ -142,14 +139,17 @@ pub async fn update(config: UpdateConfig, other: BaseConfig) -> anyhow::Result<( StateDelta::from(buf).into() }; let request = ContractRequest::Update { key, data }.into(); - execute_command(request, other, config.address, config.port).await + let mut client = start_api_client(other).await?; + execute_command(request, &mut client).await +} + +pub(crate) async fn start_api_client(cfg: BaseConfig) -> anyhow::Result { + v1::start_api_client(cfg).await } -async fn execute_command( +pub(crate) async fn execute_command( request: ClientRequest<'static>, - other: BaseConfig, - address: IpAddr, - port: u16, + api_client: &mut WebApi, ) -> anyhow::Result<()> { - v1::execute_command(request, other, address, port).await + v1::execute_command(request, api_client).await } diff --git a/crates/fdev/src/commands/v1.rs b/crates/fdev/src/commands/v1.rs index 73eec9c05..f9970718f 100644 --- a/crates/fdev/src/commands/v1.rs +++ b/crates/fdev/src/commands/v1.rs @@ -1,13 +1,8 @@ use super::*; -pub(super) async fn execute_command( - request: ClientRequest<'static>, - other: BaseConfig, - address: IpAddr, - port: u16, -) -> anyhow::Result<()> { - let mode = other.mode; - +pub(super) async fn start_api_client(cfg: BaseConfig) -> anyhow::Result { + let mode = cfg.mode; + let address = cfg.address; let target = match mode { OperationMode::Local => { if !address.is_loopback() { @@ -15,9 +10,9 @@ pub(super) async fn execute_command( "invalid ip: {address}, expecting a loopback ip address in local mode" )); } - SocketAddr::new(address, port) + SocketAddr::new(address, cfg.port) } - OperationMode::Network => SocketAddr::new(address, port), + OperationMode::Network => SocketAddr::new(address, cfg.port), }; let (stream, _) = tokio_tungstenite::connect_async(&format!( @@ -30,8 +25,13 @@ pub(super) async fn execute_command( anyhow::anyhow!(format!("fail to connect to the host({target}): {e}")) })?; - WebApi::start(stream) - .send(request) - .await - .map_err(Into::into) + Ok(WebApi::start(stream)) +} + +pub(super) async fn execute_command( + request: ClientRequest<'static>, + api_client: &mut WebApi, +) -> anyhow::Result<()> { + api_client.send(request).await?; + Ok(()) } diff --git a/crates/fdev/src/config.rs b/crates/fdev/src/config.rs index 9eb107815..10e06bc84 100644 --- a/crates/fdev/src/config.rs +++ b/crates/fdev/src/config.rs @@ -27,6 +27,13 @@ pub struct BaseConfig { /// Node operation mode. #[arg(value_enum, default_value_t=OperationMode::Local, env = "MODE")] pub mode: OperationMode, + /// The port of the running local freenet node websocket API. + #[arg(short, long, default_value = "50509", env = "WS_API_PORT")] + pub(crate) port: u16, + /// The ip address of freenet node to publish the contract to. If the node is running in local mode, + /// The default value is `127.0.0.1`. + #[arg(short, long, default_value_t = IpAddr::V4(Ipv4Addr::LOCALHOST))] + pub(crate) address: IpAddr, } #[derive(clap::Subcommand, Clone)] @@ -35,6 +42,8 @@ pub enum SubCommand { Build(BuildToolConfig), Inspect(crate::inspect::InspectConfig), Publish(PutConfig), + /// Query the local node for information. Currently only shows open connections. + Query {}, WasmRuntime(ExecutorConfig), Execute(RunCliConfig), Test(crate::testing::TestConfig), @@ -78,9 +87,6 @@ pub struct UpdateConfig { /// The default value is `127.0.0.1` #[arg(short, long, default_value_t = IpAddr::V4(Ipv4Addr::LOCALHOST))] pub(crate) address: IpAddr, - /// The port of the running local freenet node. - #[arg(short, long, default_value = "50509")] - pub(crate) port: u16, /// A path to the update/delta being pushed to the contract. pub(crate) delta: PathBuf, /// Whether this contract will be updated in the network or is just a dry run @@ -97,15 +103,6 @@ pub struct PutConfig { #[arg(long)] pub(crate) code: PathBuf, - /// The ip address of freenet node to publish the contract to. If the node is running in local mode, - /// The default value is `127.0.0.1`. - #[arg(short, long, default_value_t = IpAddr::V4(Ipv4Addr::LOCALHOST))] - pub(crate) address: IpAddr, - - /// The port of the running local freenet node. - #[arg(short, long, default_value = "50509")] - pub(crate) port: u16, - /// A path to the file parameters for the contract/delegate. If not specified, will be published /// with empty parameters. #[arg(long)] diff --git a/crates/fdev/src/main.rs b/crates/fdev/src/main.rs index 3d24f4c06..aa8107c8c 100644 --- a/crates/fdev/src/main.rs +++ b/crates/fdev/src/main.rs @@ -9,6 +9,7 @@ mod config; mod inspect; pub(crate) mod network_metrics_server; mod new_package; +mod query; mod testing; mod util; mod wasm_runtime; @@ -66,6 +67,10 @@ fn main() -> anyhow::Result<()> { } Ok(()) } + SubCommand::Query {} => { + query::query(config.additional).await?; + Ok(()) + } }; // todo: make all commands return concrete `thiserror` compatible errors so we can use anyhow r.map_err(|e| anyhow::format_err!(e)) diff --git a/crates/fdev/src/query.rs b/crates/fdev/src/query.rs new file mode 100644 index 000000000..d1f946126 --- /dev/null +++ b/crates/fdev/src/query.rs @@ -0,0 +1,40 @@ +use freenet_stdlib::client_api::{ConnectedPeers, HostResponse, QueryResponse}; +use prettytable::{Cell, Row, Table}; + +use crate::{ + commands::{execute_command, start_api_client}, + config::BaseConfig, +}; + +pub async fn query(base_cfg: BaseConfig) -> anyhow::Result<()> { + let mut client = start_api_client(base_cfg).await?; + tracing::info!("Querying for connected peers"); + execute_command( + freenet_stdlib::client_api::ClientRequest::NodeQueries(ConnectedPeers {}), + &mut client, + ) + .await?; + let HostResponse::QueryResponse(QueryResponse::ConnectedPeers { peers }) = + client.recv().await? + else { + anyhow::bail!("Unexpected response from the host"); + }; + + let mut table = Table::new(); + + table.add_row(Row::new(vec![ + Cell::new("Identifier"), + Cell::new("SocketAddress"), + ])); + + for (identifier, socketaddress) in peers { + table.add_row(Row::new(vec![ + Cell::new(&identifier.to_string()), + Cell::new(&socketaddress.to_string()), + ])); + } + + table.printstd(); + + Ok(()) +} diff --git a/crates/fdev/src/wasm_runtime/user_events.rs b/crates/fdev/src/wasm_runtime/user_events.rs index 68c63b3e9..6b15dc36c 100644 --- a/crates/fdev/src/wasm_runtime/user_events.rs +++ b/crates/fdev/src/wasm_runtime/user_events.rs @@ -217,7 +217,7 @@ impl From for OpenRequest<'static> { let req = match cmd.cmd { Command::Get => ContractRequest::Get { key, - fetch_contract: false, + return_contract_code: false, } .into(), Command::Put => { @@ -305,7 +305,7 @@ impl ClientEventsProxy for StdInput { let key = self.contract.key(); node.send(ClientRequest::ContractOp(ContractRequest::Get { key, - fetch_contract: true, + return_contract_code: true, })) .await .map_err(|e| { diff --git a/modules/antiflood-tokens/contracts/token-allocation-record/src/lib.rs b/modules/antiflood-tokens/contracts/token-allocation-record/src/lib.rs index a5260cb81..fcfa6eff2 100644 --- a/modules/antiflood-tokens/contracts/token-allocation-record/src/lib.rs +++ b/modules/antiflood-tokens/contracts/token-allocation-record/src/lib.rs @@ -28,25 +28,6 @@ impl ContractInterface for TokenAllocContract { Ok(ValidateResult::Valid) } - /// The contract verifies that the release times for a tier matches the tier. - /// - /// For example, a 15:30 UTC release time isn't permitted for hour_1 tier, but 15:00 UTC is permitted. - fn validate_delta( - parameters: Parameters<'static>, - delta: StateDelta<'static>, - ) -> Result { - let assigned_token = TokenAssignment::try_from(delta)?; - let params = TokenDelegateParameters::try_from(parameters)?; - #[allow(clippy::redundant_clone)] - let verifying_key = VerifyingKey::::new(params.generator_public_key.clone()); - let verification = assigned_token.is_valid(&verifying_key); - if verification.is_err() { - log_verification_err(¶ms.generator_public_key, "validate delta"); - } - log_succesful_ver(¶ms.generator_public_key, "validate delta"); - Ok(verification.is_ok()) - } - fn update_state( parameters: Parameters<'static>, state: State<'static>, @@ -142,9 +123,8 @@ impl ContractInterface for TokenAllocContract { state: State<'static>, _summary: StateSummary<'static>, ) -> Result, ContractError> { - // FIXME: this will be broken because problem with node let assigned_tokens = TokenAllocationRecord::try_from(state)?; - //let summary = TokenAllocationSummary::try_from(summary)?; + // FIXME: this will be broken because problem with node //let delta = assigned_tokens.delta(&summary); assigned_tokens.try_into() } diff --git a/network-monitor/src/home-page-ring.tsx b/network-monitor/src/home-page-ring.tsx index 670df95dd..5ded4d151 100644 --- a/network-monitor/src/home-page-ring.tsx +++ b/network-monitor/src/home-page-ring.tsx @@ -50,7 +50,7 @@ export const HomePageRing = () => { console.log("all_contracts_locations", all_contracts_locations); - let key_index = all_contracts.keys().next().value; + let key_index = all_contracts.keys().next().value as string; let one_contract = all_contracts.get(key_index); console.log("key_index", key_index); diff --git a/stdlib b/stdlib index f3d68931c..8095aac4b 160000 --- a/stdlib +++ b/stdlib @@ -1 +1 @@ -Subproject commit f3d68931ccbc9df37038b0246baf8ffeb7d693d2 +Subproject commit 8095aac4b9a5888d308ef26a3cef7a407e221334 diff --git a/tests/test-app-1/Cargo.lock b/tests/test-app-1/Cargo.lock index af7571b91..3624742f0 100644 --- a/tests/test-app-1/Cargo.lock +++ b/tests/test-app-1/Cargo.lock @@ -37,9 +37,6 @@ name = "arrayvec" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" -dependencies = [ - "serde", -] [[package]] name = "autocfg" @@ -256,9 +253,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "flatbuffers" -version = "23.5.26" +version = "24.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dac53e22462d78c16d64a1cd22371b54cc3fe94aa15e7886a2fa6e5d1ab8640" +checksum = "8add37afff2d4ffa83bc748a70b4b1370984f6980768554182424ef71447c35f" dependencies = [ "bitflags", "rustc_version", @@ -272,9 +269,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "freenet-macros" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e9fac5b43bee97b6ee49a376ca9e26eedf8bd581efcb176e4a534304f8b4279" +version = "0.0.5" dependencies = [ "proc-macro2", "quote", @@ -283,11 +278,8 @@ dependencies = [ [[package]] name = "freenet-stdlib" -version = "0.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a408ccb448697333c9e3f8a935976c223db980d2fb6a076ae7c89851c6ed17a" +version = "0.0.8" dependencies = [ - "arrayvec", "bincode", "blake3", "bs58", diff --git a/tests/test-app-1/Cargo.toml b/tests/test-app-1/Cargo.toml index 8f7076981..23921efc2 100644 --- a/tests/test-app-1/Cargo.toml +++ b/tests/test-app-1/Cargo.toml @@ -14,4 +14,4 @@ panic = 'abort' strip = true [workspace.dependencies] -freenet-stdlib = { version = "0.0.7", default-features = false } +freenet-stdlib = { path = "../../stdlib/rust", default-features = false } diff --git a/tests/test-app-1/container/src/lib.rs b/tests/test-app-1/container/src/lib.rs index 897ec084f..8761c769c 100644 --- a/tests/test-app-1/container/src/lib.rs +++ b/tests/test-app-1/container/src/lib.rs @@ -12,13 +12,6 @@ impl ContractInterface for Contract { Ok(ValidateResult::Valid) } - fn validate_delta( - _parameters: Parameters<'static>, - _delta: StateDelta<'static>, - ) -> Result { - Ok(true) - } - fn update_state( _parameters: Parameters<'static>, state: State<'static>, diff --git a/tests/test-app-1/deps/src/lib.rs b/tests/test-app-1/deps/src/lib.rs index 897ec084f..8761c769c 100644 --- a/tests/test-app-1/deps/src/lib.rs +++ b/tests/test-app-1/deps/src/lib.rs @@ -12,13 +12,6 @@ impl ContractInterface for Contract { Ok(ValidateResult::Valid) } - fn validate_delta( - _parameters: Parameters<'static>, - _delta: StateDelta<'static>, - ) -> Result { - Ok(true) - } - fn update_state( _parameters: Parameters<'static>, state: State<'static>, diff --git a/tests/test-contract-1/src/lib.rs b/tests/test-contract-1/src/lib.rs index a41afc2ef..af1b232cd 100644 --- a/tests/test-contract-1/src/lib.rs +++ b/tests/test-contract-1/src/lib.rs @@ -23,20 +23,6 @@ impl ContractInterface for Contract { } } - fn validate_delta( - _parameters: Parameters<'static>, - delta: StateDelta<'static>, - ) -> Result { - let bytes = delta.as_ref(); - if bytes.len() == 4 && bytes == [1, 2, 3, 4] { - Ok(true) - } else if bytes.len() != 4 { - Err(ContractError::InvalidDelta) - } else { - Ok(false) - } - } - fn update_state( _parameters: Parameters<'static>, state: State<'static>,