Stage 5d-next-5 housekeeping: HTTP errors + CI cyclic tests + doc fold (closes #28) - #31
Merged
TaprootFreak merged 8 commits intoMay 18, 2026
Conversation
The `usernames`-feature-gated test `balance_unknown_address_with_claimed_username_returns_username` in server_tests.rs passed `[u8; 32]` to `UsernameStore::claim`, whose signature expects `Address = HashDigest = HashOut<F>` (the Plonky2-era type after the migration). `cargo check --all-targets` without `--all-features` skipped the test gate and missed the breakage; `cargo clippy --all-targets --all-features -- -D warnings` fails it. Round-trip the address through `digest_from_bytes` so the test compiles on both feature configurations. Unblocks the local pre-flight sanity gate the housekeeping work in #28 takes as its baseline.
…7.22 The standalone aggregator architecture write-up was created during the Stage 5d-next-5 work as a tracking artifact. Now that all four phases (1 + 2a + 2b + 3) have landed, the content belongs in the §7.x lessons-learned series alongside the other empirical findings from the Plonky2 migration. §7.22 captures the canonical content: - Architecture diagram (aggregator non-cyclic + outer cyclic). - The two empirical insights pinned by recursion_shape_probe: ConstantGate::new(2) injection in pass 3 of the helper, and helper_degree = pad_bits + 1 (INNER_PAD_BITS_STAGE_5D_NEXT_5=15 for the Phase 2b outer at degree 16). - Per-slot Phase 2b constraints (8 items, masked by active bit). - Public-API extensions: InCoinSourceWitness + the two _and_sources prove entries. - Multi-leaf MMR test-fixture caveat (consumer-at-0 + source-at-1 with shared bootstrap leaf). - Test coverage matrix (5 positives + 3 §13 negatives). - Per-test benchmark on M3 and the verification runbook. - Rule-of-thumb capstone for future multi-verify outer circuits. §7.21 status updated from "deferred" to "resolved in §7.22". Cross-references updated in ROADMAP.md row 5 + row 7 + §5, account_server.rs defense-in-depth comment, program-plonky2/ SESSION_STATE.md (3 locations), recursion_shape_probe.rs doc comment, source_aggregator.rs doc comment (2 locations), and main.rs doc comments (2 locations). All now point at the §7.22 anchor. Verified post-fold: - `git grep STAGE_5D_NEXT_5_AGGREGATOR.md` returns no hits. - `cargo doc -p zkcoins-program-plonky2 --no-deps` succeeds with no broken intra-doc links.
…body
The /api/send + /api/mint failure path previously surfaced every
send_coins error as 200 OK with an empty SendCoinResponse {
success: false }. Clients consuming the API had no way to
distinguish "user error" (insufficient funds, bad inclusion proof)
from "server error" (prover failure, broadcast failure), and the
specific error string from send_coins was logged via eprintln!
but never reached the caller.
Changes:
- Add `error: Option<String>` to SendCoinResponse. Present on every
failure (success: false), absent on success.
- Introduce `map_send_coins_error(&str) -> (StatusCode, &'static str)`
as the single source of truth for the API contract:
- 404 NOT_FOUND → "Unknown account address"
- 400 BAD_REQUEST → "prev_commitment_pubkey required for account
update"
- 422 UNPROCESSABLE_ENTITY → Insufficient funds, defense-in-depth
shim rejections (in-coin not in source OCR /
source not in history MMR), malformed witness
(coin missing commitment, missing inclusion
proof, coin already in coin-history /
output-coins SMT), slot-count violations
- 500 INTERNAL_SERVER_ERROR → "prove failed" (collapsed; full error
string is still logged via eprintln!)
- 500 INTERNAL_SERVER_ERROR → "internal error" (catch-all for
newly-added error strings we haven't mapped
yet — wallet treats it as a server problem,
operator finds the unmapped string in logs)
- Introduce `handler_error_response(StatusCode, &str)` for
request-level failures (signature verification, hex decode,
address length mismatch, broadcast failure, etc.) so every failure
carries a body.error string instead of an opaque empty body.
- Wire send_coin_handler + mint_handler + commit_handler through
the two helpers. The defense-in-depth shim from PR #26 (Stage
5d-next-5 Phase 2b) now surfaces as a specific 422 in microseconds
before the prove cost is paid; full latency profile preserved.
Tests:
- 14 unit tests pin every documented send_coins error string to its
mapped (status, body) pair. Adding a new error string in
account_server::send_coins that isn't mapped will surface in the
catch-all 500 arm — and the unknown-string test ensures that
surfaces as 500 internal error rather than leaking through as
2xx.
- New handler-level test `send_with_unknown_account_returns_404_
with_error_string` exercises the 404 path end-to-end.
- Updated `send_with_insufficient_funds_returns_422_with_error_
string` (renamed from `_returns_ok_with_success_false`) to assert
the new contract.
- All five malformed-input HTTP tests still green; their assertions
are unchanged (4xx already), so the body.error addition is
forward-compatible at the deserialization boundary.
Migration note: wallet clients that hard-code the `if status ==
200 { success } else { fail }` check will see 4xx where they
previously saw 200 + success:false. The body shape is otherwise
forward-compatible (error: Option<String> is skip_serializing_if =
"Option::is_none", so success responses still serialize exactly as
before).
…0 min The `tests` job was previously skipping the long-running cyclic positives via `--skip stage_5b --skip stage_5c --skip stage_5d --skip stage_5e` with the comment "they are exercised explicitly in the coverage job below". That comment was stale: the `coverage` job runs `cargo llvm-cov -p server`, which only covers the `server` crate and does NOT touch `program-plonky2`'s cyclic-recursion tests. Result: 29 stage_5d (incl. 5d-next-3 + 5d-next-5) tests, 5 stage_5e SPEC §13 negatives, 10 stage_5c_plus tests — 44 cyclic positives and negatives total — were running locally only. A regression that broke a cyclic constraint while compiling cleanly would land on develop without anyone noticing. This change: - Drops every `--skip stage_5*` flag from the program-plonky2 test step. - Renames the step from "off-circuit + non-cyclic gadgets" to "full cyclic-recursion sweep" to match what it now does. - Bumps `tests` job `timeout-minutes` from 75 → 180. Local single-threaded wall on the `program-plonky2` lib sweep is ~42 min on M3; `ubuntu-latest` is ~2.5× slower without GPU acceleration so the budget is ~80–120 min. Combined with the ~30–45 min server suite, worst-case wall is ~125–165 min. 180 min cap leaves headroom for a one-off cache miss without resorting to a larger-tier runner. - Documents the OOM mitigation path in-line (exit code 143 → larger runner / `--ignored` heavies / shard) so the next person hitting it doesn't have to rediscover the trade-offs. Verification caveat: this workflow only triggers on PRs targeting `develop`, so the in-PR CI cycle for this branch (target `feat/plonky2-migration`) does NOT exercise the change. The change takes effect once `feat/plonky2-migration` merges into `develop` via the release PR.
A consistency audit of the new HTTP error mapping surfaced three reachable `account_server` error strings that were falling through to the generic 500 "internal error" arm. All three originate in `get_merkle_proofs` (called from `send_coins` on the prev_commitment_pubkey path). - "Unable to get merkle proofs for provided public key" (account_server.rs:224) → 422. Caller supplied a public_key the server has no commitment proof for. - "Unable to get mmr inclusion proof for the previous root" (account_server.rs:236) → 422. Caller's previous_proof references a history root the server's MMR hasn't observed yet — stale client-side snapshot. - "Proof public_inputs too short" (account_server.rs:232) → 500. Truncated proof bytes — server-side data corruption or version mismatch with the prover. Not caller-fixable, hence 500 + the full string preserved in the body (no prove-internal information to leak, unlike `*_failed` which is the actual prover output). Three new unit tests pin each mapping. Test count for the `map_send_coins_error_*` block is now 17. Also refresh program-plonky2/SESSION_STATE.md "Active parallel work" to mark all four Issue #28 housekeeping items as ✅ done. The previous text listed coverage-exclusions and cyclic-tests as pending — both land in PR #31.
TaprootFreak
marked this pull request as ready for review
May 18, 2026 16:29
commit_handler delegates to server_runtime::broadcast_commit_and_ deliver for the broadcast step. Its 503 SERVICE_UNAVAILABLE arm still returned an empty SendCoinResponse::default(), missing the body.error string the rest of the post-Item-1 handlers now carry. Mirror of the mint_handler analogue: use handler_error_response so 503 responses surface the failure mode to the client (the operator can already see the underlying Esplora error in the eprintln! log).
TaprootFreak
marked this pull request as draft
May 18, 2026 16:31
The 18 new tests added in PR #31 (14 + 3 `map_send_coins_error_*` unit tests + 1 new handler-level 404 test) bring the `--all-features` server sweep count from 120 to 138. Update the breakdown line at the top of the file and the "Test confirmation status" reference so the next reader of SESSION_STATE.md sees the post-merge state.
…ines) The previous text overclaimed "passes at 100% lines / 100% functions". The actual local result with the production exclusion list is exit 0 (acceptance criterion met) with 100% functions (96/96) but 99.44% lines (1067/1073). The 6 uncovered lines are all `?` error-propagation sites in account_server::send_coins (323, 358, 400, 412, 415, 478) — reachable Err paths not exercised by the current test set but not blocking the gate. No tactical #[coverage(off)] added.
TaprootFreak
marked this pull request as ready for review
May 18, 2026 19:08
This was referenced May 18, 2026
Closed
TaprootFreak
added a commit
that referenced
this pull request
May 18, 2026
) PR #31's local `cargo llvm-cov` run returned exit 0 at 99.44% line coverage with 6 uncovered `?` error-propagation sites in account_server::send_coins. The gate accepts exit 0 as authoritative, but the 99.44% leaves a real (if narrow) regression window: if cargo-llvm-cov 0.8.x changes its `--fail-under-lines` semantics on a future toolchain bump, CI could go red unexpectedly. This change closes 5 of the 6 gaps via a small targeted refactor + 4 new negative tests, with the off-circuit defense-in-depth shim already covered by the existing `test_send_coins_rejects_tampered_source_proof_inclusion`. ## Refactor - `Account::create_coins` previously returned `Result<Vec<Coin>, &'static str>` but never produced an Err (the upstream balance/slot-count guards in `send_coins` are total). Drops the dead `Result` so the call site has no dead `?` path. - `send_coins`'s `in_coins.len() > MAX_IN_COINS` and `out_coins.len() > MAX_OUT_COINS` checks moved to the top of the function — before the heavy `get_merkle_proofs` loop and prove cost. Callers violating the per-transition slot budget now fail in microseconds instead of paying state-mutation cost first. The new guards use `account.coin_queue.len()` and `invoices.len()` directly, which the test-suite can hit without constructing 9 real CoinProofs. ## New tests - `test_send_coins_rejects_too_many_invoices` — `invoices.len() > MAX_OUT_COINS`. Empty account, no prove cost. - `test_send_coins_rejects_too_many_coins_in_queue` — clones one honest CoinProof MAX_IN_COINS+1 times into the recipient's queue; guard fires before the in-coin loop reads any of the entries. - `test_send_coins_errors_when_state_lacks_commitment_for_in_coin` — mint+receive WITHOUT calling `state.update`, so the recipient's queued in-coin references a commitment public_key the state never indexed; `get_merkle_proofs` returns "Unable to get merkle proofs for provided public key" which PR #31's `map_send_coins_error` maps to 422. - `test_send_coins_errors_when_state_lacks_commitment_for_prev_account_proof` — same surface but for the AccountUpdate branch: forge `account.proof = Some(...)`, pass a never-indexed `prev_commitment_pubkey`. The AccountUpdate-branch `get_merkle_proofs` call surfaces the same error string. All 4 tests green: 295 s wall single-threaded in release on M3. ## Verification - `cargo check --workspace --all-targets` ✅ - `cargo clippy --workspace --all-features --tests --all-targets -- -D warnings` ✅ - `cargo fmt --all --check` ✅ - 4 new tests green (295 s) - Existing 139 tests in the server crate unchanged Lines previously uncovered (323/358/400/412/415/478) are now either covered by these 4 tests (323+478 via the merkle-proofs failure tests; 412+415 via the new top-of-function guards) or remain a single tracker line in the off-circuit defense-in-depth shim (line 400 — Source-not-in-MMR — covered by the in-circuit Phase 2b gate and the existing test_send_coins_rejects_tampered_source_proof_inclusion).
TaprootFreak
added a commit
that referenced
this pull request
May 18, 2026
closes #28) (#31) * fix(tests): convert byte address to HashDigest in username claim test The `usernames`-feature-gated test `balance_unknown_address_with_claimed_username_returns_username` in server_tests.rs passed `[u8; 32]` to `UsernameStore::claim`, whose signature expects `Address = HashDigest = HashOut<F>` (the Plonky2-era type after the migration). `cargo check --all-targets` without `--all-features` skipped the test gate and missed the breakage; `cargo clippy --all-targets --all-features -- -D warnings` fails it. Round-trip the address through `digest_from_bytes` so the test compiles on both feature configurations. Unblocks the local pre-flight sanity gate the housekeeping work in #28 takes as its baseline. * docs: fold STAGE_5D_NEXT_5_AGGREGATOR.md into MIGRATION_RESEARCH.md §7.22 The standalone aggregator architecture write-up was created during the Stage 5d-next-5 work as a tracking artifact. Now that all four phases (1 + 2a + 2b + 3) have landed, the content belongs in the §7.x lessons-learned series alongside the other empirical findings from the Plonky2 migration. §7.22 captures the canonical content: - Architecture diagram (aggregator non-cyclic + outer cyclic). - The two empirical insights pinned by recursion_shape_probe: ConstantGate::new(2) injection in pass 3 of the helper, and helper_degree = pad_bits + 1 (INNER_PAD_BITS_STAGE_5D_NEXT_5=15 for the Phase 2b outer at degree 16). - Per-slot Phase 2b constraints (8 items, masked by active bit). - Public-API extensions: InCoinSourceWitness + the two _and_sources prove entries. - Multi-leaf MMR test-fixture caveat (consumer-at-0 + source-at-1 with shared bootstrap leaf). - Test coverage matrix (5 positives + 3 §13 negatives). - Per-test benchmark on M3 and the verification runbook. - Rule-of-thumb capstone for future multi-verify outer circuits. §7.21 status updated from "deferred" to "resolved in §7.22". Cross-references updated in ROADMAP.md row 5 + row 7 + §5, account_server.rs defense-in-depth comment, program-plonky2/ SESSION_STATE.md (3 locations), recursion_shape_probe.rs doc comment, source_aggregator.rs doc comment (2 locations), and main.rs doc comments (2 locations). All now point at the §7.22 anchor. Verified post-fold: - `git grep STAGE_5D_NEXT_5_AGGREGATOR.md` returns no hits. - `cargo doc -p zkcoins-program-plonky2 --no-deps` succeeds with no broken intra-doc links. * feat(api): replace 200+success:false with 4xx/5xx + structured error body The /api/send + /api/mint failure path previously surfaced every send_coins error as 200 OK with an empty SendCoinResponse { success: false }. Clients consuming the API had no way to distinguish "user error" (insufficient funds, bad inclusion proof) from "server error" (prover failure, broadcast failure), and the specific error string from send_coins was logged via eprintln! but never reached the caller. Changes: - Add `error: Option<String>` to SendCoinResponse. Present on every failure (success: false), absent on success. - Introduce `map_send_coins_error(&str) -> (StatusCode, &'static str)` as the single source of truth for the API contract: - 404 NOT_FOUND → "Unknown account address" - 400 BAD_REQUEST → "prev_commitment_pubkey required for account update" - 422 UNPROCESSABLE_ENTITY → Insufficient funds, defense-in-depth shim rejections (in-coin not in source OCR / source not in history MMR), malformed witness (coin missing commitment, missing inclusion proof, coin already in coin-history / output-coins SMT), slot-count violations - 500 INTERNAL_SERVER_ERROR → "prove failed" (collapsed; full error string is still logged via eprintln!) - 500 INTERNAL_SERVER_ERROR → "internal error" (catch-all for newly-added error strings we haven't mapped yet — wallet treats it as a server problem, operator finds the unmapped string in logs) - Introduce `handler_error_response(StatusCode, &str)` for request-level failures (signature verification, hex decode, address length mismatch, broadcast failure, etc.) so every failure carries a body.error string instead of an opaque empty body. - Wire send_coin_handler + mint_handler + commit_handler through the two helpers. The defense-in-depth shim from PR #26 (Stage 5d-next-5 Phase 2b) now surfaces as a specific 422 in microseconds before the prove cost is paid; full latency profile preserved. Tests: - 14 unit tests pin every documented send_coins error string to its mapped (status, body) pair. Adding a new error string in account_server::send_coins that isn't mapped will surface in the catch-all 500 arm — and the unknown-string test ensures that surfaces as 500 internal error rather than leaking through as 2xx. - New handler-level test `send_with_unknown_account_returns_404_ with_error_string` exercises the 404 path end-to-end. - Updated `send_with_insufficient_funds_returns_422_with_error_ string` (renamed from `_returns_ok_with_success_false`) to assert the new contract. - All five malformed-input HTTP tests still green; their assertions are unchanged (4xx already), so the body.error addition is forward-compatible at the deserialization boundary. Migration note: wallet clients that hard-code the `if status == 200 { success } else { fail }` check will see 4xx where they previously saw 200 + success:false. The body shape is otherwise forward-compatible (error: Option<String> is skip_serializing_if = "Option::is_none", so success responses still serialize exactly as before). * ci: enable full program-plonky2 cyclic test sweep, bump timeout to 180 min The `tests` job was previously skipping the long-running cyclic positives via `--skip stage_5b --skip stage_5c --skip stage_5d --skip stage_5e` with the comment "they are exercised explicitly in the coverage job below". That comment was stale: the `coverage` job runs `cargo llvm-cov -p server`, which only covers the `server` crate and does NOT touch `program-plonky2`'s cyclic-recursion tests. Result: 29 stage_5d (incl. 5d-next-3 + 5d-next-5) tests, 5 stage_5e SPEC §13 negatives, 10 stage_5c_plus tests — 44 cyclic positives and negatives total — were running locally only. A regression that broke a cyclic constraint while compiling cleanly would land on develop without anyone noticing. This change: - Drops every `--skip stage_5*` flag from the program-plonky2 test step. - Renames the step from "off-circuit + non-cyclic gadgets" to "full cyclic-recursion sweep" to match what it now does. - Bumps `tests` job `timeout-minutes` from 75 → 180. Local single-threaded wall on the `program-plonky2` lib sweep is ~42 min on M3; `ubuntu-latest` is ~2.5× slower without GPU acceleration so the budget is ~80–120 min. Combined with the ~30–45 min server suite, worst-case wall is ~125–165 min. 180 min cap leaves headroom for a one-off cache miss without resorting to a larger-tier runner. - Documents the OOM mitigation path in-line (exit code 143 → larger runner / `--ignored` heavies / shard) so the next person hitting it doesn't have to rediscover the trade-offs. Verification caveat: this workflow only triggers on PRs targeting `develop`, so the in-PR CI cycle for this branch (target `feat/plonky2-migration`) does NOT exercise the change. The change takes effect once `feat/plonky2-migration` merges into `develop` via the release PR. * feat(api): map three more send_coins errors + refresh SESSION_STATE A consistency audit of the new HTTP error mapping surfaced three reachable `account_server` error strings that were falling through to the generic 500 "internal error" arm. All three originate in `get_merkle_proofs` (called from `send_coins` on the prev_commitment_pubkey path). - "Unable to get merkle proofs for provided public key" (account_server.rs:224) → 422. Caller supplied a public_key the server has no commitment proof for. - "Unable to get mmr inclusion proof for the previous root" (account_server.rs:236) → 422. Caller's previous_proof references a history root the server's MMR hasn't observed yet — stale client-side snapshot. - "Proof public_inputs too short" (account_server.rs:232) → 500. Truncated proof bytes — server-side data corruption or version mismatch with the prover. Not caller-fixable, hence 500 + the full string preserved in the body (no prove-internal information to leak, unlike `*_failed` which is the actual prover output). Three new unit tests pin each mapping. Test count for the `map_send_coins_error_*` block is now 17. Also refresh program-plonky2/SESSION_STATE.md "Active parallel work" to mark all four Issue #28 housekeeping items as ✅ done. The previous text listed coverage-exclusions and cyclic-tests as pending — both land in PR #31. * fix(api): plumb body.error through commit broadcast 503 path commit_handler delegates to server_runtime::broadcast_commit_and_ deliver for the broadcast step. Its 503 SERVICE_UNAVAILABLE arm still returned an empty SendCoinResponse::default(), missing the body.error string the rest of the post-Item-1 handlers now carry. Mirror of the mint_handler analogue: use handler_error_response so 503 responses surface the failure mode to the client (the operator can already see the underlying Esplora error in the eprintln! log). * docs(session-state): bump server test count 120 → 138 post-PR-#31 The 18 new tests added in PR #31 (14 + 3 `map_send_coins_error_*` unit tests + 1 new handler-level 404 test) bring the `--all-features` server sweep count from 120 to 138. Update the breakdown line at the top of the file and the "Test confirmation status" reference so the next reader of SESSION_STATE.md sees the post-merge state. * docs(session-state): clarify Item 2 llvm-cov result (exit 0, 99.44% lines) The previous text overclaimed "passes at 100% lines / 100% functions". The actual local result with the production exclusion list is exit 0 (acceptance criterion met) with 100% functions (96/96) but 99.44% lines (1067/1073). The 6 uncovered lines are all `?` error-propagation sites in account_server::send_coins (323, 358, 400, 412, 415, 478) — reachable Err paths not exercised by the current test set but not blocking the gate. No tactical #[coverage(off)] added.
TaprootFreak
added a commit
that referenced
this pull request
May 18, 2026
) PR #31's local `cargo llvm-cov` run returned exit 0 at 99.44% line coverage with 6 uncovered `?` error-propagation sites in account_server::send_coins. The gate accepts exit 0 as authoritative, but the 99.44% leaves a real (if narrow) regression window: if cargo-llvm-cov 0.8.x changes its `--fail-under-lines` semantics on a future toolchain bump, CI could go red unexpectedly. This change closes 5 of the 6 gaps via a small targeted refactor + 4 new negative tests, with the off-circuit defense-in-depth shim already covered by the existing `test_send_coins_rejects_tampered_source_proof_inclusion`. ## Refactor - `Account::create_coins` previously returned `Result<Vec<Coin>, &'static str>` but never produced an Err (the upstream balance/slot-count guards in `send_coins` are total). Drops the dead `Result` so the call site has no dead `?` path. - `send_coins`'s `in_coins.len() > MAX_IN_COINS` and `out_coins.len() > MAX_OUT_COINS` checks moved to the top of the function — before the heavy `get_merkle_proofs` loop and prove cost. Callers violating the per-transition slot budget now fail in microseconds instead of paying state-mutation cost first. The new guards use `account.coin_queue.len()` and `invoices.len()` directly, which the test-suite can hit without constructing 9 real CoinProofs. ## New tests - `test_send_coins_rejects_too_many_invoices` — `invoices.len() > MAX_OUT_COINS`. Empty account, no prove cost. - `test_send_coins_rejects_too_many_coins_in_queue` — clones one honest CoinProof MAX_IN_COINS+1 times into the recipient's queue; guard fires before the in-coin loop reads any of the entries. - `test_send_coins_errors_when_state_lacks_commitment_for_in_coin` — mint+receive WITHOUT calling `state.update`, so the recipient's queued in-coin references a commitment public_key the state never indexed; `get_merkle_proofs` returns "Unable to get merkle proofs for provided public key" which PR #31's `map_send_coins_error` maps to 422. - `test_send_coins_errors_when_state_lacks_commitment_for_prev_account_proof` — same surface but for the AccountUpdate branch: forge `account.proof = Some(...)`, pass a never-indexed `prev_commitment_pubkey`. The AccountUpdate-branch `get_merkle_proofs` call surfaces the same error string. All 4 tests green: 295 s wall single-threaded in release on M3. ## Verification - `cargo check --workspace --all-targets` ✅ - `cargo clippy --workspace --all-features --tests --all-targets -- -D warnings` ✅ - `cargo fmt --all --check` ✅ - 4 new tests green (295 s) - Existing 139 tests in the server crate unchanged Lines previously uncovered (323/358/400/412/415/478) are now either covered by these 4 tests (323+478 via the merkle-proofs failure tests; 412+415 via the new top-of-function guards) or remain a single tracker line in the off-circuit defense-in-depth shim (line 400 — Source-not-in-MMR — covered by the in-circuit Phase 2b gate and the existing test_send_coins_rejects_tampered_source_proof_inclusion).
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.
Implements the four housekeeping items deferred from PR #23 + PR #26, bundled into one PR per user request. Closes #28.
Items
Item 4 — Doc fold (low risk; pure doc movement) —
530b7acFolded
program-plonky2/STAGE_5D_NEXT_5_AGGREGATOR.md(612 lines) intoMIGRATION_RESEARCH.md§7.22 as the canonical lessons-learned entry, alongside §7.21 ("Stage 5d-next-4 source-side verification blocked on Plonky2 1.1.0") which is now updated to status "resolved in §7.22".ConstantGate::new(2)injection +helper_degree = pad_bits + 1), per-slot Phase 2b constraints, public-API extensions, multi-leaf MMR fixture caveat, test coverage matrix, benchmark, and verification runbook.ROADMAP.md(3 spots),account_server.rs(defense-in-depth comment),program-plonky2/SESSION_STATE.md(3 spots),recursion_shape_probe.rs,source_aggregator.rs(2 spots),main.rs(2 spots).git grep STAGE_5D_NEXT_5_AGGREGATOR.mdreturns 0 hits.cargo doc -p zkcoins-program-plonky2 --no-depsgreen.Item 1 — HTTP error mapping for /api/send + /api/mint (medium-high risk; API-contract change) —
3f0b9b4The previous failure path returned
200 OK + {success: false}with no error string. Clients couldn't distinguish user error (insufficient funds, bad inclusion proof) from server error (prover failure, broadcast failure), and thesend_coinserror string was logged viaeprintln!but never reached the caller.SendCoinResponsegainserror: Option<String>(skip-serializing-if-none, forward-compatible at the deserialization boundary on success responses).map_send_coins_error(&str) -> (StatusCode, &'static str)is the single source of truth for the API contract:eprintln!so attacker can't probe prover internals).handler_error_response(StatusCode, &str)for request-level failures (signature verification, hex decode, address length mismatch, broadcast failure, etc.) so every failure carries a body.error string instead of an opaque empty body.send_coin_handler+mint_handler+commit_handler+server_runtime::broadcast_commit_and_deliver.Tests: 14 unit tests pin every documented error string to its
(StatusCode, body)pair. New handler testsend_with_unknown_account_returns_404_with_error_stringexercises 404 end-to-end. Existingsend_with_insufficient_fundstest updated to assert the new contract (renamed_returns_ok_with_success_false→_returns_422_with_error_string).Migration note for wallet maintainer: Clients that hard-code
if status == 200 { success } else { fail }will see 4xx where they previously saw 200 + success:false. The body shape is otherwise additive (success responses serialize exactly as before).Item 3 — CI cyclic-tests inclusion + timeout bump (high risk; only verifiable via CI) —
ebe7b10The
testsjob was skipping the long-running cyclic positives via--skip stage_5b --skip stage_5c --skip stage_5d --skip stage_5ewith a stale comment claiming "they are exercised explicitly in the coverage job below". The coverage job only covers theservercrate — 44 cyclic positives/negatives inprogram-plonky2were silently running locally only.--skip stage_5*flags.testsjobtimeout-minutesfrom 75 → 180. Worst-case wall ≈ 125–165 min (≈ 30–45 min server + ≈ 80–120 min program-plonky2 single-threaded onubuntu-latest).--ignoredheavies / shard).Verification caveat: the workflow only triggers on PRs targeting
develop. PRs targetingfeat/plonky2-migrationdo NOT exercise the change. The change takes effect oncefeat/plonky2-migrationmerges todevelopvia PR #18 (release PR).Item 2 — Drop CI coverage exclusions for
account_server.rs+server.rs(medium risk) — pending llvm-cov verificationCurrent state: the workflow's
--ignore-filename-regexalready does NOT containaccount_server.rsorserver.rs(the exclusions were dropped earlier; Issue #28's snapshot was stale at the file level). The remaining work is to verify locally that the existing gate runs green with the current exclusions, and close any gaps that surface.A local
cargo llvm-cov --release -p server --show-missing-lines --ignore-filename-regex 'main\.rs|publisher\.rs|server_runtime\.rs|scanner_runtime\.rs|_tests\.rs$' --fail-under-lines 100 --fail-under-functions 100 -- --test-threads=1is currently in flight (~30–60 min wall). I'll push any gap-closing commits to this branch as soon as it lands.If you want to merge before then, the gate is enforced in CI by the
coveragejob — if anything's actually broken, the release-PR CI run will catch it.Pre-flight stabilizer —
0a3bfb7A pre-existing bug in
server/src/server_tests.rs:124(introduced in PR #24) madecargo clippy --all-features --tests -- -D warningsfail at HEAD: theusernames-feature-gated test was passing[u8; 32]toUsernameStore::claim, which expectsAddress = HashDigestafter the Plonky2 migration. Fixed by round-tripping throughdigest_from_bytes. Unblocks the local pre-flight gate this PR's other items take as their baseline.Files changed
Verification
cargo check --workspace --all-targets✅cargo fmt --all --check✅cargo clippy --workspace --all-targets --all-features -- -D warnings✅cargo doc -p zkcoins-program-plonky2 --no-deps✅map_send_coins_error✅ (0.00s)cargo llvm-covwith relaxed exclusion list — in flight.House rules followed
feedback_pr_as_draft.Claude/AI/Generated withfooters per global rule.feat/plonky2-migration, NOTdevelopdirectly (perfeedback_zkcoins_direct_developexception: this branch is itself en route to develop via PR feat: Plonky2 / Poseidon proof-system migration #17).Out of scope
zk-coins/apprepo).feat/plonky2-migrationlands indevelop.Audit follow-up commits (after the initial PR open)
A consistency audit before final review surfaced three reachable
account_servererror strings that were falling through to the generic 500 catch-all, plus one missed broadcast-503 path:d63c9a8feat(api): map three more send_coins errors + refresh SESSION_STATE— addsUnable to get merkle proofs for provided public key(422),Unable to get mmr inclusion proof for the previous root(422), andProof public_inputs too short(500) tomap_send_coins_error. Three new unit tests pin each. Totalmap_send_coins_error_*unit-test count rises from 14 → 17. SESSION_STATE.md "Active parallel work" refreshed to mark all four Issue Stage 5d-next-5 / Step 7 housekeeping: HTTP errors + CI coverage + CI cyclic-tests + doc fold #28 items ✅ done.0f67cd8fix(api): plumb body.error through commit broadcast 503 path—server_runtime::broadcast_commit_and_deliver503 branch was still returningSendCoinResponse::default()with no error string. Now useshandler_error_responselike the mint-handler analogue.Test count delta: +18 tests in this PR total (14 + 3 unit tests + 1 new handler-level 404 test). Post-merge full server sweep is 138 tests (up from 120 in the previous
feat/plonky2-migrationstate).Final branch HEAD:
0f67cd8. PR is 6 commits.