Skip to content

feat(protocol): share native protocol types and dependencies with WASM clients - #29

Merged
jacderida merged 37 commits into
mainfrom
web-support
Sep 22, 2026
Merged

jacderida merged 37 commits into
mainfrom
web-support

Conversation

@mickvandijke

@mickvandijke mickvandijke commented Sep 1, 2026

Copy link
Copy Markdown
Member

Summary

Exposes the native chunk wire types, content addressing, quote and commitment verification, pricing, and payment proofs to browser clients through one Rust protocol crate. Default features retain native behavior; portable consumers disable defaults and enable RPC only when needed.

The transport, pqc, and evm re-exports keep client and node on the same underlying types and immutable dependency revisions. Native consumers also receive the signed-payment journal API. Authenticated browser sessions and framing come from saorsa_transport::webrtc; application chunk messages and payment policy remain shared protocol types.

The native EVM re-export now includes finality-aware payment recovery. RPC-only tests exclude native Anvil fixtures, run in CI, and the lockfile retains patched h2 and ruint versions.

Linear issue

Closes V2-803 — Linear issue

Risk tier

  • T0 — docs / tooling / CI / pure UX-output. Repo CI only.
  • T1 — client-only, no network-facing behavior change. CI + prod compat smoke.
  • T2 — node/client logic with behavioral surface, no protocol/format/economics change. Dev testnet + ADR.
  • T3 — protocol / storage format / payments / routing. T2 evidence + adversarial testing.

Proposed for human review.

Compatibility

  • Wire: Existing native chunk, quote, and payment formats are retained. Browser v5 framing/session behavior comes from the coordinated transport dependency.
  • Storage: No stored chunk or DataMap format change.
  • API: Adds portable feature selection and shared re-exports. Default native APIs remain available; dependency types and the raised Rust 1.91 MSRV require compatibility review.

Semver impact

  • breaking
  • feature
  • fix

Test evidence

Validated on the current revision during this refresh:

  • cargo test --locked --lib: 87 passed.
  • cargo test --locked --no-default-features --features rpc -- --test-threads=1: 83 passed.
  • cargo check --locked --target wasm32-unknown-unknown --no-default-features --features rpc: passed.
  • cargo clippy --locked --all-targets --all-features -- -D warnings: passed.

Native, portable RPC, and WASM consumers resolve the same finalized-journal evmlib revision. The RPC-only test command is also part of CI.

New dependency

Immutable Git revisions of saorsa-core, saorsa-pqc, and evmlib; Tokio and native dependency features are gated. No separate browser protocol or crypto implementation.

ADR

Shared native/browser core ADR-019 and browser transport ADR-015.

Mitigation / rollback

Keep consumers on the default native feature and revert coordinated dependency revisions together.

Coordinated dependencies

Exact immutable revisions are recorded in Cargo.toml and Cargo.lock. Compatible published releases remain a release gate.

Current stack revisions

All PRs link to V2-803. Immutable Git revisions used by the validated stack:

PR Revision
saorsa-pqc#7 29a2b272
evmlib#17 cf424c04
saorsa-transport#160 6f0b1f62
saorsa-core#158 0df7853e
ant-protocol#29 cdae7d19
ant-node#220 26f5fb79
ant-client#186 f1303e1a

EVM recovery now retains ambiguous journals until finalized failure/replacement evidence is available. The protocol and native client include the matching consumer changes. Existing browser wire and payment formats are retained.

Security dependency refresh (2026-09-15)

The shared transport now requires rustls >=0.23.45, fixing RUSTSEC-2026-0285. Affected lockfiles and downstream immutable Git pins are updated. This branch passes cargo check --all-targets --all-features and cargo audit (zero vulnerabilities; existing warning advisories remain).

ICE admission interoperability fix (2026-09-16)

Admission challenges now carry a random nonzero 64-bit ICE-CONTROLLED tie-breaker, stable for the listener lifetime. This removes the zero value that libjuice treats as a missing role attribute. Downstream Git revisions are aligned.

Validation: cargo check --all-targets --all-features passes with the updated pins.

Browser performance dependency refresh (2026-09-16)

Pins now include transport 6f0b1f62, which forwards the admission-completing Binding request to ICE instead of waiting for another client retransmission. The coordinated client caches exact verified signed publications while retaining shared ownership, replacement and witness checks. No additional API, payment or wire changes are introduced by this refresh.

cargo check --no-default-features passed. Full stack measurements and validation.

@grumbach grumbach left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review at 1764950d

Verdict: fine to merge once its dependencies merge. A few docs fixes.

Verified:

  • This crate's chunk and payment-proof wire encodings and its pricing are unchanged: chunk*.rs, proof encoding and pricing have no diff, and commitment_signed_payload only became public (same bytes). (The DHT wire changes come from the pinned saorsa-core, reviewed on #158.)
  • The evmlib changes this crate re-exports keep the same bytes as main for quote hashes and Merkle payment calldata.

To fix:

  1. This is semver-major for default-features = false users. On main, turning off default features only removed logging. Now chunk_protocol (and its re-exports), evm::{Wallet, PayForQuotesError}, evm::testnet, evm::contract, evm::utils, transport::{P2PNode, P2PEvent, NodeMode, CoreNodeConfig, IPDiversityConfig, PeerRouteKind} and SingleNodePayment::{pay, verify} all need native or rpc. The PR is marked breaking, which is right, but README still says Rust 1.75 and its feature table lists only logging, and CHANGELOG has no entry.
  2. ADDRESS_V2_CAPABILITY's doc comment is out of date (src/lib.rs:148-150): it says native peers send both versions, but saorsa-core #158 now sends one FindNode with an optional signed-record trailer.
  3. The RPC-only test job compiles SingleNodePayment::pay and verify but never runs them: every test that calls them is gated on native. A few mocked JSON-RPC tests (valid payment, underpayment, RPC error) would give the portable path real coverage.

The red Publish dry-run needs coordinated registry releases, not just merged branches: this manifest asks for saorsa-core/native and evmlib's native and rpc features, which only exist in the pinned revisions so far.

Tested locally: cargo test --locked --lib 87 passed; cargo test --locked --no-default-features --features rpc 83 passed.

@jacderida

Copy link
Copy Markdown
Member

Testnet evidence — DEV-03 run 591 (2026-09-17), web-support rebased on main

Four-hour staging-scale run of the web-support branches — 990 nodes + 7 bootstraps (nat_percent: 0, one node VM clock-skewed +3 h), a native client tier (7 uploaders, 1 downloader) and a WASM/browser-path client tier (3 uploaders, 1 downloader; Node.js + node-datachannel driving ant-core compiled to WASM over WebRTC-Direct, bootstrapping from the 7 bootstrap multiaddrs and nothing else). Builds: ant-node 41acee71 (crate 0.19.0), ant-client 9b85491b (crate 0.3.7; native ant and the WASM bundle from the same head); evmlib ccd65f18, saorsa-transport 3ed66a9c. Full write-up with all 21 evidence items: V2-1270.

This is the first run of this series where the node fleet stayed the build it was deployed as (the previous run's 0.18.1-identified nodes auto-upgraded mid-run to a stable without the WebRTC listener): 997/997 services logged No upgrade available at every check, 0 New version detected, 0 service restarts, 0 replaced binaries, 7/7 bootstraps live at the end.

The result that matters: sustained, flat throughput over the whole run, on both transports

Successful / attempted transfers per 30-minute slot from T0 (measurement window = slots 3–8):

30-min slot from T0 (UTC) native uploads WASM uploads native downloads WASM downloads
T0 → +30m (19:16) 356/356 (29.8 GiB) 12/13 (1.4 GiB) 42/42 (9.8 GiB) 13/13 (0.5 GiB)
+30m → +60m 371/371 (30.9 GiB) 16/16 (2.6 GiB) 48/48 (8.9 GiB) 15/15 (0.5 GiB)
+60m → +90m window 385/385 (32.3 GiB) 16/16 (2.0 GiB) 42/42 (12.8 GiB) 7/7 (0.3 GiB)
+90m → +120m window 392/392 (33.4 GiB) 16/16 (2.6 GiB) 50/50 (10.3 GiB) 11/11 (1.1 GiB)
+120m → +150m window 391/391 (32.4 GiB) 16/16 (2.6 GiB) 48/48 (10.7 GiB) 14/14 (0.5 GiB)
+150m → +180m window 388/388 (32.3 GiB) 18/18 (2.6 GiB) 49/49 (11.2 GiB) 11/11 (0.6 GiB)
+180m → +210m window 388/389 (32.7 GiB) 16/16 (1.7 GiB) 53/53 (9.3 GiB) 11/11 (0.6 GiB)
+210m → +240m window 392/393 (33.8 GiB) 17/17 (2.6 GiB) 50/50 (9.9 GiB) 15/15 (0.6 GiB)

No decay in rate or success on either transport from the first half-hour to the last. Measurement-window totals: native uploads 2336/2338 (99.91%), native downloads 292/292, WASM uploads 99/99, WASM downloads 69/69. Whole run (incl. warm-up): native 3085/3087 up / 386/386 down; WASM 127/128 up / 98/98 down. WASM covered 20 MB, 300 MB (single payment) and 900 MB (merkle) uploads, and downloads up to 500 MB, all paid through the JS callback (approve + payForQuotes) with the payment network taken from the node's HELLO.

Durations (ok ops, window): native upload p50 24.4 s / p95 126.5 s, WASM upload p50 146.1 s / p95 1021.8 s; native download p50 17.2 s / p95 72.2 s, WASM download p50 110.8 s / p95 236.3 s. Size-matched 20 MB: upload 16.8 s native vs 137.5 s WASM, download 15.5 s vs 96.6 s — the browser path is slower, as expected, but consistently so.

Failures, all three of them (out of 3,699 transfers)

  • WASM upload chore(deps): track saorsa-core mick/remove-bootstrap-cache branch #1 at T0 on the 20 MB uploader: Witnessed close group inconclusive before payment: got 0/1 quorum-recognised peers — first-op-after-start transient, pre-payment, nothing spent; chore(release): promote rc-2026.4.3 #2 succeeded three minutes later and nothing recurred in 127 further uploads.
  • Native sgp/50 MB #336 (22:19Z): witnessed close group initial lookup found 6 peers, need 7 — DHT transient, pre-payment.
  • Native sjc/100 MB #418 (23:06Z): payment error: Could not get current gas price: HTTP error 429 from the public Arbitrum Sepolia RPC, pre-payment, nothing spent, next upload fine.

That last one (plus one identical -32000 context deadline exceeded on the same call five minutes after the window) technically fails the run's zero-tolerance "no RPC-transport payment failure" criterion, so the issue is scored 10/11. It is not a significant finding: 2 in 3,087 native uploads (0.065%), never post-payment, never money lost, and the cause is narrow — get_gas_price is the one read in the payment prepare path that evmlib ccd65f18 did not put behind retry() (estimate_gas, get_transaction_count, get_chain_id, get_transaction_receipt, get_block_by_number are). Zero evmlib::retry warnings appeared anywhere in the run, i.e. the public RPC was otherwise quiet. A one-line retry wrap closes it; it says nothing about the transport, the node, or the WASM path.

Also clean: 0 clock-skew signatures on either tier against the +3 h node (unsubmitted payment quotes expired / paid proof expired), 0 awaiting chain finality, 0 partial uploads, 0 not within its local closest peers, 0 invalid public file size (the WASM downloader skipped 111 over-cap entries by design), 0 OOM (WASM downloader peak 2.45 GB on the 500 MB pick), and a fleet-wide sweep of 1,009 ant-* units on 85 hosts found no panic, restart or replaced binary.

Node tier under this load: CPU mean 23.5% per host, per-service RSS p50 262 MB / p95 283 MB; steady-state 35.4 GB egress per GB uploaded, 8.7 GB disk per GB stored — in line with the previous (contaminated) run and not split by transport, since one fleet served both.

@jacderida
jacderida marked this pull request as ready for review September 22, 2026 13:38
@jacderida

Copy link
Copy Markdown
Member

Merging with two CI checks red. Both are known and neither reflects a problem with the change.

Publish dry-run

This cannot pass while the branch is unreleased, and it is expected to stay red until release.

cargo publish --dry-run ignores the git pin and resolves evmlib from crates.io. The published evmlib 0.9.1 exposes only external-signer, while this crate's native feature requires evmlib/native, which exists only on the pinned revision:

package `ant-protocol` depends on `evmlib` with feature `native` but `evmlib` does not have that feature.
help: available features: external-signer

The evmlib reference is deliberately a git revision and will be resolved to a published version at release time, at which point this check passes on its own. The pin is not being changed here.

Three checks cancelled by the draft transition

linear-link, pr-template and self-test show as failed because their runs were cancelled when the PR was taken out of draft (run 35734844086, all three cancelled at 13:47:22Z). They do not restart on their own.

All three passed on this same head before the draft status changed:

Check Last passing run
linear-link 2026-09-22 13:32:19Z
pr-template 2026-09-22 13:37:18Z
self-test 2026-09-22 13:37:17Z

The pr-template pass at 13:37 also covers the earlier failure on this PR, which was a - Api: / - API: mismatch in the Compatibility section of the description; the checker matches that axis case-sensitively. The description has been corrected and the check passed afterwards.

Everything else is green: Clippy, Format, Documentation, Security Audit, wasm, and the full test matrix (ubuntu, macOS, no-default-features).

@jacderida
jacderida merged commit 3ad7cf2 into main Sep 22, 2026
14 of 22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants