feat(cli): let clearnet-test-mode go online, so the mixnet's cost is … - #2717
Draft
zancas wants to merge 8 commits into
Draft
feat(cli): let clearnet-test-mode go online, so the mixnet's cost is …#2717zancas wants to merge 8 commits into
zancas wants to merge 8 commits into
Conversation
…sion An in-process harness measures the sync engine alone, which answers nothing about a session where four proven exits bootstrap beside the scan. So each run here drives the CLI: `makers run-cli --online` where that task exists, and a direct `cargo build -p zingo-cli` where it does not, which is the only way to reach the era this comparison looks back into. An arm is a commit and a build kind, written `<commit>[:clearnet]`. Sync rides clearnet in both kinds, because the sync engine takes a plain gRPC indexer and never a SOCKS5 route. So one commit against itself in the two kinds measures what the mixnet's presence costs a clearnet sync, which is a different question from what a mixnet carrier would cost, and the one worth asking of a wallet that bundles a proxy it must launch. The reading comes from the engine's own clock, and the tool grafts that span into each arm's worktree rather than requiring the arm to carry one. A fixed patch could not do this: the call's argument list grew across the range, so the graft anchors on the spawn both eras share, walks to its matching brace, and surrounds whatever lies between without reading it. It refuses unless the anchor appears exactly once and the block it found calls the sync engine, so a step that would instrument the wrong work fails loudly rather than producing a number. Tests pin both eras' shapes, both refusals, and a truncated reading. A benchmark that varies more than one thing measures nothing, so everything but the arm is held fixed: the indexer, the seed, the window, the performance level, and the release profile. Each arm gets its own worktree, so neither meets the other's build artifacts. A warm-up per arm pays for the build and is discarded. The measured rounds alternate. The window is the caller's, since `--birthday` is required and every arm receives the one value, so nothing here asks a moving tip where to start. The task takes an optional reserved first word naming the session kind, following the idiom that gives `makers test` its `packages` and `live`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… them Measuring a long range meant scanning it to the end, which put a real question out of reach. A wallet restored from a birthday hundreds of thousands of blocks back scans millions of outputs, so a single run takes minutes and twelve rounds across two arms takes hours. The window a user actually complains about is the first minute, not the last. `--seconds <n>` stops each scan that long after its span opens. The engine answers a stop by returning the session's own counts, so a bounded run reports the same quantity a finished one does, and the rate means what it meant before. A range too long to finish becomes affordable to compare, and the comparison lands on the phase the user waits through. Nothing else changes. The stop rides the same stdin the tool already uses to launch a scan on an arm that cannot consent to going online. Omitting the option runs to completion as before. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The wallet is a covariate, and the tool pinned it to one fundless mnemonic. That is the right default, because a wallet holding nothing measures pure scanning: every output is trial decrypted and discarded, and none is decrypted twice for its note. It is the wrong fixture for reproducing somebody else's measurement, since a wallet with history does more work per output and the difference belongs to the wallet rather than to the code. `--seed <mnemonic>` restores a named wallet in every arm. Both arms receive the one value, as they do the birthday, so the wallet stays fixed across the comparison while ceasing to be fixed across comparisons. The default is unchanged, so an invocation that names no seed measures what it measured before. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The comparability guard compared the arms' output counts, which was right while every run scanned a window to its end: the counts should then agree, and a disagreement means the arms did different work. Bounding a run broke that premise. A bounded run scans whatever its seconds reach, so its counts differ by design, and the guard fired on the tool's own arrangement. A real comparison of Oscar's window reported 68% drift and warned that its rates were not comparable, when nothing was wrong with them. The guard now checks durations when a run is bounded and counts when it is not, and it names which it checked. Bounded runs get their own tolerance, because a stop lands after the batch in flight rather than on the instant it is asked for, so a span overshoots by up to one batch and the spread a fixed window allows would fire on that alone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…measurable A sync measurement cannot currently separate the mixnet's cost from the scan's, because no build offers the arm that would. ADR 0026 makes the mixnet a default capability and ADR 0024 forces Mixnet Mode on at the go-online moment, so every online session carries both; and a build without `nym` refuses to go online at all, answering an online consent act with "Offline Mode is its only mode". Both halves are correct policy and neither is the problem. The problem is that the two of them together leave no configuration in which a scan runs with no mixnet beside it. That gap produced a false alarm worth recording. A comparison of the `zingolib_beta_ironwood` tag against dev showed roughly an eleven-fold sync slowdown, which read as a regression. It is not one: at the tag, `nym` was off by default, and on dev it is on and forced, so the two runs differed by an entire mixnet session — proxy spawn, quartet proof, Server-Selection Sweep, and continuous cover traffic beside the scan. A matched harness run over an identical seed, server, birthday, and window puts dev at 1966 outputs/s against the tag's 1874, so the scan engine is not slower. `clearnet-test-mode` now suspends that refusal. The feature already existed for exactly this kind of act — the quarantined clearnet server-selection sweep ADR 0029 deletes from ordinary builds, compiled back in as a deliberate, review-gated test act under the 2026-08-06 ruling — and the two capabilities share one rationale, so its comment widens rather than a second flag appearing beside it. The gate sits inside the existing `#[cfg(not(feature = "nym"))]` variant of `get_communications`, so enabling the feature alongside `nym` changes nothing and still only reaches the sweep. A session that takes this path announces itself at launch: it names the feature, states that every request travels clearnet, and says the indexer sees the machine's IP. `sync-bench` gains `--clearnet`, which forwards to `run-cli` so one arm of a pair boots no mixnet. Run against a build without the feature it will fail fast on the refusal, which is the correct outcome rather than a silent clearnet session. Verified across all six of this crate's feature combinations with `cargo hack check --feature-powerset`, including `clearnet-test-mode,nym`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
# Conflicts: # tools/workbench/src/bin/sync-ab.rs
The module header explained what the tool measures before it said how to run it, so a reader who wanted a number had to reconstruct the invocation from the argument parser or from the cargo-make description. A how-to now opens the header, and the former opening text follows under a heading that names it as the rationale it is. The how-to gives one command per scenario: the mixnet arm alone, which is the default because the mixnet is a default capability under ADR 0026; the clearnet arm alone, which runs the same session with the mixnet left out of the build; and both arms alternated round by round, which is what attributes a difference to the mixnet rather than to the hour the run fell in. A closing paragraph names the three constraints a first run trips over: the required birthday and the fifteen-minute budget a session fails against, the refusal of --clearnet together with --compare, and the two release builds that alternating arms leave under target/sync-bench/builds. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
…measurable
A sync measurement cannot currently separate the mixnet's cost from the scan's, because no build offers the arm that would. ADR 0026 makes the mixnet a default capability and ADR 0024 forces Mixnet Mode on at the go-online moment, so every online session carries both; and a build without
nymrefuses to go online at all, answering an online consent act with "Offline Mode is its only mode". Both halves are correct policy and neither is the problem. The problem is that the two of them together leave no configuration in which a scan runs with no mixnet beside it.That gap produced a false alarm worth recording. A comparison of the
zingolib_beta_ironwoodtag against dev showed roughly an eleven-fold sync slowdown, which read as a regression. It is not one: at the tag,nymwas off by default, and on dev it is on and forced, so the two runs differed by an entire mixnet session — proxy spawn, quartet proof, Server-Selection Sweep, and continuous cover traffic beside the scan. A matched harness run over an identical seed, server, birthday, and window puts dev at 1966 outputs/s against the tag's 1874, so the scan engine is not slower.clearnet-test-modenow suspends that refusal. The feature already existed for exactly this kind of act — the quarantined clearnet server-selection sweep ADR 0029 deletes from ordinary builds, compiled back in as a deliberate, review-gated test act under the 2026-08-06 ruling — and the two capabilities share one rationale, so its comment widens rather than a second flag appearing beside it.The gate sits inside the existing
#[cfg(not(feature = "nym"))]variant ofget_communications, so enabling the feature alongsidenymchanges nothing and still only reaches the sweep. A session that takes this path announces itself at launch: it names the feature, states that every request travels clearnet, and says the indexer sees the machine's IP.sync-benchgains--clearnet, which forwards torun-cliso one arm of a pair boots no mixnet. Run against a build without the feature it will fail fast on the refusal, which is the correct outcome rather than a silent clearnet session.Verified across all six of this crate's feature combinations with
cargo hack check --feature-powerset, includingclearnet-test-mode,nym.Fixes: