feat(protocol): share native protocol types and dependencies with WASM clients - #29
Conversation
b5a74a7 to
c6bbf17
Compare
… fix) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
f6e86dd to
1764950
Compare
grumbach
left a comment
There was a problem hiding this comment.
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, andcommitment_signed_payloadonly 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:
- This is semver-major for
default-features = falseusers. On main, turning off default features only removed logging. Nowchunk_protocol(and its re-exports),evm::{Wallet, PayForQuotesError},evm::testnet,evm::contract,evm::utils,transport::{P2PNode, P2PEvent, NodeMode, CoreNodeConfig, IPDiversityConfig, PeerRouteKind}andSingleNodePayment::{pay, verify}all neednativeorrpc. The PR is marked breaking, which is right, but README still says Rust 1.75 and its feature table lists onlylogging, and CHANGELOG has no entry. 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 oneFindNodewith an optional signed-record trailer.- The RPC-only test job compiles
SingleNodePayment::payandverifybut never runs them: every test that calls them is gated onnative. 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.
Testnet evidence — DEV-03 run 591 (2026-09-17),
|
| 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 429from 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.
|
Merging with two CI checks red. Both are known and neither reflects a problem with the change.
|
| 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).
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, andevmre-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 fromsaorsa_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
Proposed for human review.
Compatibility
Semver impact
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:
29a2b272cf424c046f0b1f620df7853ecdae7d1926f5fb79f1303e1aEVM 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-featuresandcargo 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-featurespasses 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-featurespassed. Full stack measurements and validation.