Skip to content

SDK: nym-swizzle - #6984

Open
mmsinclair wants to merge 7 commits into
developfrom
feature/nym-swizzle
Open

SDK: nym-swizzle#6984
mmsinclair wants to merge 7 commits into
developfrom
feature/nym-swizzle

Conversation

@mmsinclair

@mmsinclair mmsinclair commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Adds nym-swizzle, an SDK utility crate for application-layer traffic-shape obfuscation, aimed at wallets and any client that fetches sequential, index-addressed data or broadcasts at meaningful moments.

Why

A mixnet hides who is talking. It does not hide what an application's query pattern says about it, and two leaks survive perfect transport anonymity:

  • Timing. The destination sees wall-clock arrival. A wallet that broadcasts the moment it reaches chain tip is trivially correlatable with its own sync activity.
  • Index / start height. A light client asking for exactly blocks 4_120_000..4_120_010 states its resume point, and because today's start is yesterday's end, the start height acts as a linking key that chains otherwise-unlinkable sessions together.

Every nym-powered wallet currently has to reinvent these mitigations. They are pure application-layer transforms with no dependency on nym internals, so they belong in one small reusable crate.

What's in it

Two composable primitives plus a shared, seedable randomness configuration:

delay — schedules an async action after a sampled delay (uniform, Poisson-process, or normal).

let mut s = Delay::uniform(Duration::ZERO, Duration::from_secs(10));
let result = s.run(async move { broadcast_tx(tx).await }).await;

range — decomposes an index range into randomly sized, deliberately overlapping, randomly ordered chunks that cover it exactly, with start-edge obfuscation.

Range::new(resume_height, tip)
    .snap_start(Snap::Spacing(1000))   // anonymity by collision
    .start_jitter(2500)                // anonymity by noise
    .plan()
    .for_each_concurrent(4, |start, end| get_blocks(start, end))
    .await;

Design decisions worth reviewing

These are the choices a reviewer should push back on if they disagree — each is recorded with its rationale in openspec/changes/add-nym-swizzle/design.md.

  1. Rejection-resampling, never clamping. Clamping an out-of-bounds sample to max piles probability mass into a spike at exactly max — itself a fingerprint. Out-of-bounds samples are redrawn instead, with a bounded-retry fallback so a pathological config still terminates.
  2. "Poisson" means exponential inter-arrival times, matching sample_poisson_duration in common/nymsphinx. Deliberate: delays drawn from the same family as mixnet cover traffic blend into traffic the adversary already observes.
  3. Snapping is deterministic and consumes no randomness. Its entire value is that independent clients collide on an identical start; any RNG involvement destroys that. There is a test asserting the RNG sample stream is byte-identical with and without snapping enabled.
  4. Jitter composes with snapping in whole checkpoint intervals, so the emitted start stays on-grid. Naive composition (jitter in raw indexes) would smear starts off-grid and quietly destroy the collision property.
  5. The end of a range is never extended. The crate cannot know which indexes exist (chain tip, array bounds), so callers widen ranges themselves. The downward start edge is the one sanctioned exception, because earlier indexes always exist.
  6. No VRF machinery. A VRF is keyed and produces a proof; nothing here needs a third party to verify the schedule. Seeded ChaCha20 gives the reproducibility that tests and replay actually want, and a caller who needs verifiable randomness feeds a VRF output in as opaque seed material.

Wasm is a hard constraint, not a goal

The crate is designed to be wrapped, unmodified, by a wasm-pack wrapper with JS conveniences — wallets are the target and wallets are often wasm. Every non-dev dependency compiles to wasm32-unknown-unknown (tokio time gated to non-wasm, wasmtimer to wasm32, following common/http-api-client). This is enforced in CI rather than trusted: nym-swizzle is added to WASM_CRATES so sdk-wasm-lint clippies it for wasm32 with -Dwarnings.

It also has zero nym dependencies, so it stays independently publishable and honest about what it is.

Evidence

Profiling harness (--example profiling, dev-deps only) streams 10M samples per distribution through fixed-size accumulators and renders each against its theoretical density as SVG. The plots are evidence, not the gate — every one is paired with a moment check that fails the run on drift:

check measured expected
uniform mean / std dev 3.0005 / 1.1547 3.0 / 1.1547
truncated-exponential mean 1.9320 1.9322
normal mean / std dev 5.0000 / 0.9997 5.0 / 1.0
chunk size / overlap mean 199.97 / 27.504 200 / 27.5
start-jitter mean 500.28 500

It also asserts the absence of a spike at the max bound — the artefact a clamping implementation would produce.

Live Zcash example (nym-swizzle-zcash-example) fetches real compact blocks from the public zec.rocks lightwalletd over gRPC and measures what the obfuscation actually costs, median of 3 trials over 1000 blocks:

strategy requests wall clock wastage
direct fetch 1 0.08s (baseline) 0.0%
swizzled, sequential 8 0.35s (4.26x) 25.0%
swizzled, 4 concurrent 8 0.11s (1.33x) 25.0%

All three verify they received every block in the range (an incomplete fetch fails the run rather than printing a nice number). Headline: obfuscation costs ~25% extra bandwidth, and concurrency buys back nearly all of the wall-clock.

Both swizzled runs share a seed so they execute a byte-identical plan, making concurrency the only variable — without that they draw different plans and the numbers aren't comparable. A discarded warm-up pass equalises server-side caching, and timings are medians because single samples swung the sequential ratio between 1.9x and 12.8x across runs.

This lives in its own crate rather than under examples/ because a gRPC/TLS stack does not compile to wasm and would break the dependency guarantee above. Its lightwalletd messages are hand-written prost structs, so there is no build script and no protoc requirement.

Testing

39 tests (unit + integration) and 4 doctests, covering the coverage/overlap/permutation invariants across many seeds, the delay laziness guarantee (poll-counting future under paused time), rejection-resampling bounds, floor and on-grid behaviour, determinism, and the concurrency limit. Clippy clean with -Dwarnings on native and wasm32. tokio is asserted absent from the resolved wasm dependency graph.

Not in scope

Transport and destination splitting (never broadcast over the sync session; sync from one server and broadcast through another) and deduplication of overlapping results stay with the application — both documented in the crate docs.

Tuning numbers are deliberately unvalidated. Wider overlaps and checkpoint spacing buy a larger anonymity set at the cost of re-downloaded data, and there are no settled values for that trade-off. Defaults are conservative percentage-of-range derivations, exposed as knobs and documented as such, not presented as validated anonymity parameters. Quantifying anonymity-set size against overlap distribution is left as open research in design.md.

This change is Reviewable

Summary by CodeRabbit

  • New Features
    • Added the nym-swizzle SDK utility for randomized delays and obfuscated, overlapping range chunking with shuffled execution and configurable concurrency.
    • Added WASM-compatible support plus examples for sampling, fetching overlapping chunks, and profiling.
    • Added a nym-swizzle-backed Zcash lightwalletd example to compare direct vs swizzled fetching strategies.
  • Documentation
    • Added new design/spec/proposal documentation and an integration checklist for nym-swizzle.
  • Tests
    • Added invariants and determinism coverage for delays, chunk plans, snapping/jitter behavior, and concurrency.
  • Chores
    • Expanded CI and build targets to include the new nym-swizzle components.

Proposes `nym-swizzle`, an SDK utility crate for application-layer
traffic-shape obfuscation aimed at wallets and other clients that fetch
sequential, index-addressed data or broadcast at meaningful moments.

A mixnet hides who is talking but not what a query pattern says about the
client: broadcast timing correlates with sync milestones, and a light
client's start height acts as a linking key across sessions (today's start
is yesterday's end). The proposal captures two primitives (randomized delay
scheduling, overlapping randomized range chunking with start-edge
obfuscation), a shared seedable randomness configuration, and a
development-time profiling harness that proves the statistical claims.

design.md records the load-bearing decisions: rejection-resampling rather
than truncation-clamping (a clamp leaves a probability spike at the bound),
Poisson-as-exponential-inter-arrival to match mixnet cover traffic,
deterministic RNG-free checkpoint snapping so independent clients actually
collide, jitter expressed in whole checkpoint intervals when snapping is
enabled, and VRF support via seed injection rather than built-in VRF
machinery.
Two composable primitives for clients whose traffic shape leaks what the
transport layer hides:

- `delay` schedules an async action after a sampled delay (uniform,
  Poisson-process, or normal). The wrapped future is not polled before its
  scheduled time, so the observable event is what moves — a "delay the
  result, start the work now" semantic would obfuscate nothing.
- `range` decomposes an index range into randomly sized, deliberately
  overlapping chunks covering it exactly, executable as a shuffled iterator
  or via async push drivers. `start_jitter` and `snap_start` obfuscate the
  start edge, which is what links a light client's sync sessions together.

Bounded sampling rejects and redraws rather than clamping: clamping piles
probability mass at the bound, which is itself a fingerprint. Snapping is
deterministic and consumes no randomness, because its whole value is that
independent clients collide on an identical start; when jitter and snapping
are combined the jitter moves the start in whole checkpoints so it stays
on-grid.

The crate has no nym dependencies and every dependency compiles to
wasm32-unknown-unknown (tokio time is gated to non-wasm, wasmtimer to
wasm32), so a wasm-pack wrapper can distribute it unmodified.
One per capability, each runnable:

- delay_broadcast: defers a broadcast by a sampled duration, with the
  app-level caveats noted in comments (never broadcast over the sync
  session; consider destination splitting).
- fetch_blocks_overlapping: resumes a sync at a realistic height and
  obfuscates the start with checkpoint snapping plus jitter, then fetches
  the overlapping chunks concurrently and reports the deliberate waste.
- poisson_sampling: shows why the empirical mean sits below the configured
  mean once a maximum bound truncates the exponential tail.
- seeded_vrf: seeds from fixed bytes standing in for a VRF output and shows
  two runs producing identical plans, plus a third seed diverging. The crate
  stays VRF-agnostic: a VRF output is just opaque seed material.
The crate makes distributional claims — delays follow their configured
distribution, chunk geometry follows its bounds, seeded runs reproduce
exactly — and claims like that should be demonstrated rather than asserted
in a doc comment.

The harness streams 10M samples per delay distribution (plus 50k chunk plans
and 500k jitter observations) through fixed-size accumulators, so memory
stays flat, and renders each against its theoretical density as SVG. The
plots are evidence, not the gate: every one is paired with a moment check
that fails the run if the sample mean or variance drifts beyond tolerance,
and the exponential suite explicitly asserts the absence of a spike at the
max bound, which is what a clamping implementation would produce.

Dev-dependencies only, so nothing here reaches downstream consumers.
The crate promises to be wrappable by wasm-pack unmodified, which is a
promise about its dependency tree and so is easy to break accidentally with
an innocuous-looking dependency. Add it to WASM_CRATES so sdk-wasm-lint
clippies it for wasm32 with -Dwarnings, and trigger the workflow on changes
under sdk/rust/nym-swizzle so a regression fails here rather than surfacing
later when someone builds the wrapper.
Measures the real cost of the obfuscation against a public lightwalletd
(zec.rocks) over gRPC, since the synthetic examples can show the shape of
the traffic but not what it costs. Three runs over one block range: a direct
fetch, overlapping chunks sequentially, and the same chunks concurrently.
Each verifies it received every block in the range, and reports the
redundancy as wastage. Typical result: ~25% extra blocks, with concurrency
recovering nearly all of the wall-clock.

Two details that make the comparison mean something. Both swizzled runs
share a seed, so they execute a byte-identical plan and concurrency is the
only variable — without this they draw different plans and the numbers are
not comparable. And because network timings are noisy enough that a single
sample swung the sequential ratio between 1.9x and 12.8x across runs, each
strategy is measured as a median of several trials after a discarded warm-up
pass equalises server-side caching.

This is a separate crate rather than an example under nym-swizzle because a
gRPC/TLS stack does not compile to wasm; keeping it out preserves that
crate's dependency guarantee and keeps its test suite fast. The lightwalletd
messages are hand-written prost structs rather than generated code, so there
is no build script and no protoc requirement; prost skips unknown fields, so
declaring a subset stays forward-compatible. Note lightwalletd's BlockRange
is inclusive at both ends, converted from the half-open chunks at the wire
boundary.
@vercel

vercel Bot commented Jul 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
nym-explorer-v2 Ready Ready Preview, Comment Jul 25, 2026 10:30am
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs-nextra Ignored Ignored Preview Jul 25, 2026 10:30am
nym-node-status Ignored Ignored Preview Jul 25, 2026 10:30am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds the nym-swizzle Rust SDK crate with randomized delay and overlapping range-planning primitives, seeded cryptographic randomness, wasm support, tests, examples, profiling validation, workspace integration, and a Zcash lightwalletd benchmark.

Changes

nym-swizzle SDK

Layer / File(s) Summary
Contracts and workspace wiring
.github/workflows/ci-sdk-wasm.yml, Cargo.toml, Makefile, openspec/changes/add-nym-swizzle/*, sdk/rust/nym-swizzle/Cargo.toml, sdk/rust/nym-swizzle/src/lib.rs, sdk/rust/nym-swizzle/src/timer.rs, sdk/rust/nym-swizzle/README.md
Defines crate requirements, public exports, wasm timer selection, workspace membership, WASM lint coverage, documentation, and implementation tasks.
Randomness and delayed execution
sdk/rust/nym-swizzle/src/rng.rs, sdk/rust/nym-swizzle/src/delay.rs, sdk/rust/nym-swizzle/tests/invariants.rs
Adds crypto-grade, seeded, and caller-supplied randomness; bounded uniform, Poisson, and normal sampling; delayed future execution; and delay tests.
Range planning and execution
sdk/rust/nym-swizzle/src/range.rs, sdk/rust/nym-swizzle/tests/invariants.rs
Adds overlapping chunk planning, downward jitter, checkpoint snapping, shuffled iteration, sequential execution, bounded concurrency, optional delays, and range invariants.
Examples and statistical validation
sdk/rust/nym-swizzle/examples/*
Adds examples for delayed broadcasts, overlapping fetches, Poisson sampling, seeded plans, and profiling-based distribution and geometry checks.
Zcash lightwalletd benchmark
sdk/rust/nym-swizzle-zcash/*
Adds a TLS gRPC client and benchmark comparing direct and swizzled block retrieval with coverage, timing, concurrency, and wastage reporting.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Benchmark
  participant Range
  participant Lightwalletd
  participant ChunkWorkers
  Benchmark->>Lightwalletd: request chain tip
  Benchmark->>Range: create deterministic overlapping plan
  Range-->>Benchmark: shuffled chunk ranges
  Benchmark->>ChunkWorkers: execute chunks with configured concurrency
  ChunkWorkers->>Lightwalletd: request compact block ranges
  Lightwalletd-->>ChunkWorkers: stream compact blocks
  ChunkWorkers-->>Benchmark: aggregate coverage and timing
Loading

Suggested reviewers: jstuczyn, mfahampshire, simonwicky

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is related to the new SDK crate, but it is too generic to explain the main change. Use a more specific title that names the added nym-swizzle crate and its purpose, such as traffic-shape obfuscation utilities.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/nym-swizzle

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mmsinclair
mmsinclair marked this pull request as ready for review July 25, 2026 10:22
@mmsinclair mmsinclair changed the title Feature/nym swizzle SDK: nym-swizzle Jul 25, 2026
Documents what the example measures and, more importantly, why its numbers
can be trusted: both swizzled runs share a seed so concurrency is the only
variable between them, and timings are medians after a warm-up pass because
single samples swung the sequential ratio between 1.9x and 12.8x.

Also records the two things that surprise readers of the source — the
messages are hand-written prost structs so there is no protoc requirement,
and lightwalletd's BlockRange is inclusive at both ends while the chunks are
half-open — plus the leaks this example deliberately does not address.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@sdk/rust/nym-swizzle/src/delay.rs`:
- Around line 152-159: Update Delay::sample and the rejection-resampling path
used by Poisson/normal sampling so an infinite max cannot cause indefinite
retries when a draw is below min: after MAX_REJECTION_RETRIES, return the
unbounded fallback sample or reject the unbounded configuration before sampling.
Add a regression test covering Delay::poisson(...).min(...) and verify sampling
terminates.
- Around line 131-133: Update the bounds method to validate the supplied min and
max together before mutating the delay state, then assign both values
atomically. Do not chain self.max and self.min, since each validates against the
existing counterpart; preserve the method’s panic behavior for invalid bounds
and return the updated Self.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a0ae3955-8583-4993-b363-1409af1af486

📥 Commits

Reviewing files that changed from the base of the PR and between eacc890 and e79c6b5.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (23)
  • .github/workflows/ci-sdk-wasm.yml
  • Cargo.toml
  • Makefile
  • openspec/changes/add-nym-swizzle/design.md
  • openspec/changes/add-nym-swizzle/proposal.md
  • openspec/changes/add-nym-swizzle/specs/nym-swizzle/spec.md
  • openspec/changes/add-nym-swizzle/tasks.md
  • sdk/rust/nym-swizzle-zcash/Cargo.toml
  • sdk/rust/nym-swizzle-zcash/src/lightwalletd.rs
  • sdk/rust/nym-swizzle-zcash/src/main.rs
  • sdk/rust/nym-swizzle/Cargo.toml
  • sdk/rust/nym-swizzle/README.md
  • sdk/rust/nym-swizzle/examples/delay_broadcast.rs
  • sdk/rust/nym-swizzle/examples/fetch_blocks_overlapping.rs
  • sdk/rust/nym-swizzle/examples/poisson_sampling.rs
  • sdk/rust/nym-swizzle/examples/profiling.rs
  • sdk/rust/nym-swizzle/examples/seeded_vrf.rs
  • sdk/rust/nym-swizzle/src/delay.rs
  • sdk/rust/nym-swizzle/src/lib.rs
  • sdk/rust/nym-swizzle/src/range.rs
  • sdk/rust/nym-swizzle/src/rng.rs
  • sdk/rust/nym-swizzle/src/timer.rs
  • sdk/rust/nym-swizzle/tests/invariants.rs

Comment on lines +131 to +133
pub fn bounds(self, min: Duration, max: Duration) -> Self {
self.max(max).min(min)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make bounds validate and assign both values atomically.

self.max(max).min(min) validates the new maximum against the old minimum first. For example, changing an existing min = 100s to valid bounds 0s..20s panics even though the supplied pair is valid.

Proposed fix
-    pub fn bounds(self, min: Duration, max: Duration) -> Self {
-        self.max(max).min(min)
+    pub fn bounds(mut self, min: Duration, max: Duration) -> Self {
+        assert!(
+            min <= max,
+            "delay bounds inverted: min {min:?} > max {max:?}"
+        );
+        self.min = min;
+        self.max = Some(max);
+        self
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pub fn bounds(self, min: Duration, max: Duration) -> Self {
self.max(max).min(min)
}
pub fn bounds(mut self, min: Duration, max: Duration) -> Self {
assert!(
min <= max,
"delay bounds inverted: min {min:?} > max {max:?}"
);
self.min = min;
self.max = Some(max);
self
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdk/rust/nym-swizzle/src/delay.rs` around lines 131 - 133, Update the bounds
method to validate the supplied min and max together before mutating the delay
state, then assign both values atomically. Do not chain self.max and self.min,
since each validates against the existing counterpart; preserve the method’s
panic behavior for invalid bounds and return the updated Self.

Comment on lines +152 to +159
pub fn sample(&mut self) -> Duration {
let min = self.min.as_nanos() as f64;
let max = self
.max
.map(|m| m.as_nanos() as f64)
.unwrap_or(f64::INFINITY);
let nanos = sample_bounded(&mut self.rng, &self.sampling, min, max);
Duration::from_nanos(nanos.round() as u64)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate file and outline =="
if [ -f sdk/rust/nym-swizzle/src/delay.rs ]; then
  wc -l sdk/rust/nym-swizzle/src/delay.rs
  ast-grep outline sdk/rust/nym-swizzle/src/delay.rs || true
else
  echo "file not found"
fi

echo "== relevant delay.rs section =="
sed -n '1,240p' sdk/rust/nym-swizzle/src/delay.rs

echo "== search sample_bounded and related bounds =="
rg -n "sample_bounded|pub struct|struct Sampling|fn new|unbounded|bounded|Poisson|Uniform|RejectionSampling|Rejection|infinite|INFINITY" sdk/rust/nym-swizzle/src/delay.rs sdk/rust/nym-swizzle/src || true

Repository: nymtech/nym

Length of output: 12783


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
p = Path('sdk/rust/nym-swizzle/src/delay.rs')
print(p.exists(), p.is_file())
if p.exists():
    lines = p.read_text().splitlines()
    for i,l in enumerate(lines,1):
        if 'sample(' in l or 'sample_bounded' in l or 'f64::INFINITY' in l:
            print(f"{i}: {l}")
PY

Repository: nymtech/nym

Length of output: 761


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== rng.rs outline =="
ast-grep outline sdk/rust/nym-swizzle/src/rng.rs || true

echo "== rng.rs relevant implementation =="
sed -n '1,330p' sdk/rust/nym-swizzle/src/rng.rs

echo "== deterministic Rust semantics probe by inspecting loop/branch condition math =="
python3 - <<'PY'
from pathlib import Path
text = Path('sdk/rust/nym-swizzle/src/rng.rs').read_text()
print("contains rejection loop:", "while true" in text or "loop" in text)
for needle in ["sample_bounded", "sample_poisson", "fn sample_normal", "infinite", "INFINITY", "try"]:
    print(f"{needle}: {text.find(needle)}")
PY

Repository: nymtech/nym

Length of output: 10904


Handle the infinite upper bound in rejection-resampling.

reject_into_bounds rejects until sample <= max; with max == f64::INFINITY, an undershoot below min is redrawn indefinitely instead of falling back after MAX_REJECTION_RETRIES. Return an unbounded fallback sample, or reject configurations that leave Poisson/normal rejection-resampling unbounded before sampling, and add a regression test for Delay::poisson(...).min(...).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdk/rust/nym-swizzle/src/delay.rs` around lines 152 - 159, Update
Delay::sample and the rejection-resampling path used by Poisson/normal sampling
so an infinite max cannot cause indefinite retries when a draw is below min:
after MAX_REJECTION_RETRIES, return the unbounded fallback sample or reject the
unbounded configuration before sampling. Add a regression test covering
Delay::poisson(...).min(...) and verify sampling terminates.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@sdk/rust/nym-swizzle-zcash/README.md`:
- Line 56: Update the fenced code block in the README sample output section to
specify the text language by changing its opening fence to use text, satisfying
markdownlint MD040.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b00828fa-5ca4-4d74-94bc-72969f2538de

📥 Commits

Reviewing files that changed from the base of the PR and between e79c6b5 and c25ec1a.

📒 Files selected for processing (2)
  • sdk/rust/nym-swizzle-zcash/Cargo.toml
  • sdk/rust/nym-swizzle-zcash/README.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • sdk/rust/nym-swizzle-zcash/Cargo.toml


### Sample output

```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Specify the sample-output fence language.

Add text to this fenced block so it satisfies markdownlint MD040.

Proposed fix
-```
+```text
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```
🧰 Tools
🪛 markdownlint-cli2 (0.23.0)

[warning] 56-56: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdk/rust/nym-swizzle-zcash/README.md` at line 56, Update the fenced code
block in the README sample output section to specify the text language by
changing its opening fence to use text, satisfying markdownlint MD040.

Source: Linters/SAST tools

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.

2 participants