NUT-XX pubkey quote discovery: sweeper reconcile + cdk pin cba8469b #21

Closed
vnprc-agent wants to merge 27 commits from agent/nutxx-pubkey-lookup into master
Collaborator

The translator's wallet now discovers its mint quotes by locking pubkey — a 15s reconcile loop asking the mint for all still-mintable quotes locked to its key — instead of depending on per-quote notifications that can be dropped. A dropped notification used to mean that quote's ehash was silently never minted; now it costs a few seconds. Nothing changes for the pool or the mint protocol surface. This is the base of the epoch milestone stack (vnprc/hashpool#13): merging it unblocks the epoch-mechanics and multi-unit-sweeper work. What proves it: cargo check, cargo test (20/20) and clippy are green on the current pin, the four MSRV lock pins are intact, and a cold cross-family reviewer read the re-pin and found nothing. What is NOT re-proven: the original end-to-end live run, for the reason in the next paragraph. If this is wrong, revert the merge — master stays functional on 7011eebc.

The cdk pin changed after this PR was first validated, and that matters for how you read it. This branch was originally pinned to cdk cba8469b and live-run validated against it on 2026-08-10 (64 quotes / 662 ehash minted via pubkey reconcile, including 12 deliberately dropped notifications). That revision is now confirmed lost — it exists on no reachable remote and in no local object store (details in the comment below). The pin is now e5a85f810a91bd1d8882a4a60ed93fd60bb691c1 (vnprc/cdk branch nutxx-wallet-v2), workspace 0.17.0, carrying the same four APIs this branch calls. Because the old revision is gone, the new pin cannot be diffed against the one the live run was performed on, so that live-run result stands as a record of what was observed then and is not re-established by this change. Re-running it is cheap once the stack is up and is the honest next step before relying on the dropped-notification path in anger.

Refs vnprc/hashpool#7. Refs vnprc/hashpool#25.

What changed since the original review

  • bfc15a92 re-pins cdk from the unreachable cba8469b to e5a85f8 across protocols/Cargo.toml, roles/Cargo.toml and devenv.nix.
  • 30b82da7 merges master, which carries the devenv repair (e09a7381) without which devenv shell cannot enter on this branch at all, plus the dev-stack controls (735678e5).
  • 1d1b25d9 moves roles/Cargo.lock onto the new revision by incremental resolution.

Risk

R2. The cdk pin moves in all four places that must agree (protocols/Cargo.toml, roles/Cargo.toml, devenv.nix, roles/Cargo.lock), verified consistent including the short-rev form. roles/Cargo.lock changed 32 lines and every one of them is a source = line — no version, checksum or dependency list moved — so the MSRV-pinned transitive deps are untouched: time 0.3.41, serde_with 3.17.0, darling 0.21.3, simple_asn1 0.6.3 against devenv's rustc 1.86.0. A fresh re-resolve would pick time 0.3.55 (MSRV 1.88) and fail; see vnprc/hashpool#10. The old 0xC0 notification path is retained but its wallet-side fetch is disabled, so discovery is sweep-only.

Residual risk that re-pinning cannot remove: the replacement revision is a different commit from the one the live run exercised. Its API surface is verified present and the unit tests pass, but behavioural equivalence on the reconcile path is not proven by a diff.

Validation

Run on the current pin, in the devenv shell (rustc 1.86.0, gcc 14.2.1):

  • cargo check -p translator_sv2 — clean.
  • cargo test -p translator_sv2 — 20 passed, 0 failed. Re-run independently by the integrator, same result.
  • cargo clippy -p translator_sv2 --all-targets — no errors; two pre-existing style warnings remain in wallet.rs:15 and channel_manager.rs:900, both recorded nits, neither introduced here.
  • Pin consistency: no occurrence of cba8469b remains anywhere in the tree; all 16 cdk entries in roles/Cargo.lock are at e5a85f8.
  • MSRV pins asserted by direct read of roles/Cargo.lock, independently twice.
  • Cold cross-family review of the re-pin diff: no findings in either correctness or comment-bloat dimensions.

Not run: the 2026-08-10 end-to-end live run, as explained above. Not run: anything in protocols/, whose gitignored lock hits the known pre-existing time 0.3.55 MSRV failure of vnprc/hashpool#10 on any clean worktree — unrelated to this change.

The translator's wallet now discovers its mint quotes by locking pubkey — a 15s reconcile loop asking the mint for all still-mintable quotes locked to its key — instead of depending on per-quote notifications that can be dropped. A dropped notification used to mean that quote's ehash was silently never minted; now it costs a few seconds. Nothing changes for the pool or the mint protocol surface. This is the base of the epoch milestone stack (vnprc/hashpool#13): merging it unblocks the epoch-mechanics and multi-unit-sweeper work. What proves it: `cargo check`, `cargo test` (20/20) and `clippy` are green on the current pin, the four MSRV lock pins are intact, and a cold cross-family reviewer read the re-pin and found nothing. What is NOT re-proven: the original end-to-end live run, for the reason in the next paragraph. If this is wrong, revert the merge — master stays functional on `7011eebc`. **The cdk pin changed after this PR was first validated, and that matters for how you read it.** This branch was originally pinned to cdk `cba8469b` and live-run validated against it on 2026-08-10 (64 quotes / 662 ehash minted via pubkey reconcile, including 12 deliberately dropped notifications). That revision is now confirmed lost — it exists on no reachable remote and in no local object store (details in the comment below). The pin is now `e5a85f810a91bd1d8882a4a60ed93fd60bb691c1` (`vnprc/cdk` branch `nutxx-wallet-v2`), workspace 0.17.0, carrying the same four APIs this branch calls. Because the old revision is gone, the new pin **cannot be diffed against the one the live run was performed on**, so that live-run result stands as a record of what was observed then and is not re-established by this change. Re-running it is cheap once the stack is up and is the honest next step before relying on the dropped-notification path in anger. Refs vnprc/hashpool#7. Refs vnprc/hashpool#25. ## What changed since the original review - `bfc15a92` re-pins cdk from the unreachable `cba8469b` to `e5a85f8` across `protocols/Cargo.toml`, `roles/Cargo.toml` and `devenv.nix`. - `30b82da7` merges master, which carries the devenv repair (`e09a7381`) without which `devenv shell` cannot enter on this branch at all, plus the dev-stack controls (`735678e5`). - `1d1b25d9` moves `roles/Cargo.lock` onto the new revision by incremental resolution. ## Risk R2. The cdk pin moves in all four places that must agree (`protocols/Cargo.toml`, `roles/Cargo.toml`, `devenv.nix`, `roles/Cargo.lock`), verified consistent including the short-rev form. `roles/Cargo.lock` changed 32 lines and every one of them is a `source =` line — no version, checksum or dependency list moved — so the MSRV-pinned transitive deps are untouched: `time` 0.3.41, `serde_with` 3.17.0, `darling` 0.21.3, `simple_asn1` 0.6.3 against devenv's rustc 1.86.0. A fresh re-resolve would pick `time` 0.3.55 (MSRV 1.88) and fail; see vnprc/hashpool#10. The old 0xC0 notification path is retained but its wallet-side fetch is disabled, so discovery is sweep-only. Residual risk that re-pinning cannot remove: the replacement revision is a different commit from the one the live run exercised. Its API surface is verified present and the unit tests pass, but behavioural equivalence on the reconcile path is not proven by a diff. ## Validation Run on the current pin, in the devenv shell (rustc 1.86.0, gcc 14.2.1): - `cargo check -p translator_sv2` — clean. - `cargo test -p translator_sv2` — 20 passed, 0 failed. Re-run independently by the integrator, same result. - `cargo clippy -p translator_sv2 --all-targets` — no errors; two pre-existing style warnings remain in `wallet.rs:15` and `channel_manager.rs:900`, both recorded nits, neither introduced here. - Pin consistency: no occurrence of `cba8469b` remains anywhere in the tree; all 16 cdk entries in `roles/Cargo.lock` are at `e5a85f8`. - MSRV pins asserted by direct read of `roles/Cargo.lock`, independently twice. - Cold cross-family review of the re-pin diff: no findings in either correctness or comment-bloat dimensions. Not run: the 2026-08-10 end-to-end live run, as explained above. Not run: anything in `protocols/`, whose gitignored lock hits the known pre-existing `time` 0.3.55 MSRV failure of vnprc/hashpool#10 on any clean worktree — unrelated to this change.
Move all coordinated CDK pins from cashubtc/cdk 1572941d (0.16.0) to
vnprc/cdk 7011eebc, the tip of branch fix/nutxx-lookup: upstream main at
23fa905d plus the NUT-XX mint quote lookup endpoint. The mint now serves
POST /v1/mint/quote/pubkey (schnorr-signed, replay-protected lookup of
mint quotes by NUT-20 locking key); no hashpool role calls it yet.

- protocols/Cargo.toml, roles/Cargo.toml: [patch.crates-io] -> vnprc/cdk
  rev 7011eebc for all cdk crates
- protocols/ehash, roles/mint, roles/translator Cargo.toml: cdk version
  reqs 0.16.0 -> 0.17.0 to match the patched workspace version
- roles/mint/Cargo.toml: cdk-ehash rev 40fdebe -> 1328180 (branch
  agent/nutxx-cdk-017: the paired cdk-ehash bump to the same CDK rev)
- devenv.nix: cdkRepo/cdkCommit -> vnprc/cdk 7011eebc; build:cdk:cli now
  interpolates those variables instead of hardcoding a second copy of
  the URL and commit, so future bumps touch one place
- scripts/patch-cdk-path.sh: match github.com/(cashubtc|vnprc)/cdk so
  the local-path patcher keeps recognizing the pin lines
- protocols/ehash/src/quote.rs: MintQuoteCustomRequest.amount became
  Option<Amount> upstream; wrap the construction site and test assert
- roles/Cargo.lock: re-resolved; cdk crates to 0.17.0/7011eebc, new
  transitive deps bdk_electrum/electrum-client/bitreq from upstream's
  wallet work, no other pin movement

Upstream drift absorbed: 265 commits (1572941d..7011eebc). Mint DB gets
two additive migrations on first start (updated_at column backfill +
pubkey index); wallet DB gets one (updated_at). No destructive schema
changes.

Validation (rustc 1.97.1, cdk repo nix dev shell; devenv nixpkgs rustc
is 1.95.0, above cdk's 1.85 MSRV): cargo check --all-targets clean and
full test suites green in all three workspaces - protocols 55 tests,
roles 51 suites 0 failures, cdk-ehash 16 tests. Runtime devenv up
mining smoke test remains a human gate.
Move the vnprc/cdk patch pin from 7011eebc to ec97d741 on
agent/nutxx-wallet-client, the combined NUT-XX mint + wallet branch,
across protocols/Cargo.toml, roles/Cargo.toml, and devenv.nix
(cdkCommit). roles/mint/Cargo.toml keeps cdk-ehash at rev 1328180
since the MintPayment trait it consumes is unchanged.

The bump carries CurrencyUnit::Custom from a String payload to
Arc<str>. Update every call site constructing the "hash" custom unit
accordingly:
- protocols/ehash/src/keyset.rs
- protocols/ehash/src/quote.rs (impl + test)
- protocols/ehash/src/sv2.rs (test)
- roles/mint/src/lib/mint_manager/setup.rs
- roles/mint/src/lib/sv2_connection/quote_processing.rs
- roles/translator/src/lib/payment/wallet.rs

Regenerate roles/Cargo.lock against the new rev.
Add a reconcile step at the start of each quote sweeper pass: query
Wallet::mint_quotes_by_pubkey with the translator's locking key, and
for any returned quote not already in the wallet's local store, call
Wallet::fetch_mint_quote to store it through cdk's own accounting
logic (no hand-constructed MintQuote records).

This catches quotes the mint holds against our key that never arrived
through the SV2 notification path (dropped message, translator
restart). The existing get_unissued_mint_quotes -> batch_check ->
batch_mint flow is unchanged and now simply sees the reconciled
quotes alongside anything the notification path already delivered.

A failed pubkey lookup or a single bad quote is logged and skipped
rather than aborting the sweep pass.
The MintQuoteNotification (0xC0) handler's fetch_mint_quote(quote_id,
Custom("ehash")) call is now redundant: the quote sweeper's pubkey
reconcile (Wallet::mint_quotes_by_pubkey) is the discovery source of
record. Comment out the fetch, leaving the payload decode, the
quote_id/amount extraction, and the info! logging in place so the
notification is still observed and logged, just no longer acted on.

The rest of the SV2 plumbing is untouched: the pool poller, the
MintQuoteNotification/MintQuoteFailure message types, and the
mint-side send all remain as-is. Retained but now-unused import and
struct field are marked #[allow(...)] with a note rather than
removed, so re-enabling the fetch later is a one-line uncomment.
Bump the cdk pin to 693f4ec4 (NUT-XX wallet: reject method-less quote
lookup responses instead of mislabeling them Bolt11, and store/validate
pubkey-looked-up quotes in the wallet database).

reconcile_quotes_by_pubkey now makes one call to
Wallet::fetch_mint_quotes_by_pubkey and logs the result; cdk does the
per-quote known-locally check, accounting, and local storage itself, so
the old per-quote check-then-fetch loop is gone. Documents the known
accepted cost of this call (O(full quote history) per lookup until the
NUT grows a state filter - see cashubtc/nuts PR 341) directly above it.

The signing-key stamping loop in process_stored_quotes is unchanged:
quotes reconciled via pubkey lookup now arrive pre-stamped, but the loop
still covers quotes discovered by other means (SV2 notifications,
pre-existing DB rows), so it stays, with a comment noting why.
Bump the cdk pin to 6cf76d12 (NUT-XX: skip malformed quotes in the
pubkey lookup instead of failing the batch, a real regression test for
the fetch_mint_quotes_by_pubkey write-guard, and an opt-in
only_mintable filter on the pubkey lookup request).

reconcile_quotes_by_pubkey now calls fetch_mint_quotes_by_pubkey with
only_mintable: true, so the mint bounds its response to quotes still
mintable for this key. Replace the old O(full quote history) cost note
- which was pending a NUT-level filter - with a short comment that this
filter is implementation-side while the spec discussion at
cashubtc/nuts PR 341 is still open.
Pubkey-lookup reconcile results now log at info when quotes were
discovered (a validation run showed the debug-only line made the
discovery path invisible at the stack's default RUST_LOG=info); quiet
passes stay at debug.

cdk pin moves to 73bbef28: the wallet-side series rebased onto TheMhv's
rebased wallet PR branch (his real commits now underneath), plus a
created_time fix at the shared wallet store path and a mint-side debug
line for lookup observability. Tree-equivalent feature content to the
previous pin.
Tree-identical to the previously pinned local rev; the hash moves to the
commits actually pushed to the fork and under review, so devenv and any
checkout without local redirects fetch the real PR code from GitHub.
Author
Collaborator

Adversarial review findings.

Merge verification (all clean): 49edec22 has an empty combined diff (no evil-merge content); vs the validated branch tip it adds exactly master's three docs files; vs master only branch work + the declared pin supersession, with nothing from master lost (files the 0.17 bump touched that aren't in this diff are byte-identical to master). Single [patch.crates-io] per workspace, single cdkCommit, all 16 lock entries at cba8469b, MSRV pins intact (time 0.3.41 / serde_with 3.17.0 / darling 0.21.3 / simple_asn1 0.6.3), zero stale revs from all 12 superseded hashes, zero conflict markers; the quote.rs resolution (Custom(Arc<str>) form) is the only one that compiles at the kept pin.

should-fix (body text) — "the mint gains the NUT-XX lookup endpoint from the cdk bump" is false: the /mint/quote/pubkey route is identical at master's pin 7011eebc. What the bump actually delivers is the only_mintable filter + pubkey-encoding fixes mint-side and the lookup API wallet-side. Corrected in the body so a future bisect/revert doesn't misjudge master's capabilities.

Verdict: textbook merge, nothing lost; body corrected.

Adversarial review findings. **Merge verification (all clean):** `49edec22` has an empty combined diff (no evil-merge content); vs the validated branch tip it adds exactly master's three docs files; vs master only branch work + the declared pin supersession, with nothing from master lost (files the 0.17 bump touched that aren't in this diff are byte-identical to master). Single `[patch.crates-io]` per workspace, single `cdkCommit`, all 16 lock entries at `cba8469b`, MSRV pins intact (`time` 0.3.41 / `serde_with` 3.17.0 / `darling` 0.21.3 / `simple_asn1` 0.6.3), zero stale revs from all 12 superseded hashes, zero conflict markers; the quote.rs resolution (`Custom(Arc<str>)` form) is the only one that compiles at the kept pin. **should-fix (body text)** — "the mint gains the NUT-XX lookup endpoint from the cdk bump" is false: the `/mint/quote/pubkey` route is identical at master's pin `7011eebc`. What the bump actually delivers is the `only_mintable` filter + pubkey-encoding fixes mint-side and the lookup API wallet-side. Corrected in the body so a future bisect/revert doesn't misjudge master's capabilities. Verdict: textbook merge, nothing lost; body corrected.
Author
Collaborator

Owner request: re-pin cdk off the dead revision so this PR can build again.

The pinned revision cba8469be5b37c067f0cf594b267801afe357960 no longer exists anywhere reachable. Checked: all 45 branches of github.com/vnprc/cdk, all 18 of github.com/themhv/cdk, cashubtc/cdk, and the shared GitHub fork network by commit API (422 no such commit, against 200 for master's pin 7011eebc as a control); all 33 repositories on the forge, which hosts no cdk fork at all; every git object store on the rust-dev VM; and nexus:~/work/cdk, which has no matching branch, one worktree, no reflog trace, and no second cdk checkout. The remaining possibility is a private GitHub repository.

Replacement pin is vnprc/cdk e5a85f8 (branch nutxx-wallet-v2), chosen because it is workspace 0.17.0, matching this repo's crate version requirements, and carries all four cdk APIs this branch and vnprc/hashpool#23 call: batch_check_mint_quote_status, batch_mint, get_unissued_mint_quotes, and only_mintable — the last being the entire delta over master's pin, which lacks it. only_mintable is present on both the mint side (crates/cdk/src/mint/issue, crates/cdk-axum/src/custom_handlers.rs, crates/cdk-sql-common/src/mint/quotes.rs) and the wallet side (crates/cdk/src/wallet/issue, crates/cdk/src/wallet/mint_connector/http_client.rs).

Validation limit worth stating plainly: because cba8469b is gone, the new pin cannot be diffed against the revision this PR's live-run validation was performed on, so equivalence of intent is not provable. The witness for the re-pin is compilation plus this repo's test suites, not a diff. The 2026-08-10 live-run result in the body stands as a record of what was observed on the old pin and is not re-established by this change.

Commits follow on this branch; no force-push.

Owner request: re-pin cdk off the dead revision so this PR can build again. The pinned revision `cba8469be5b37c067f0cf594b267801afe357960` no longer exists anywhere reachable. Checked: all 45 branches of `github.com/vnprc/cdk`, all 18 of `github.com/themhv/cdk`, `cashubtc/cdk`, and the shared GitHub fork network by commit API (`422 no such commit`, against `200` for master's pin `7011eebc` as a control); all 33 repositories on the forge, which hosts no cdk fork at all; every git object store on the rust-dev VM; and `nexus:~/work/cdk`, which has no matching branch, one worktree, no reflog trace, and no second cdk checkout. The remaining possibility is a private GitHub repository. Replacement pin is `vnprc/cdk` `e5a85f8` (branch `nutxx-wallet-v2`), chosen because it is workspace `0.17.0`, matching this repo's crate version requirements, and carries all four cdk APIs this branch and vnprc/hashpool#23 call: `batch_check_mint_quote_status`, `batch_mint`, `get_unissued_mint_quotes`, and `only_mintable` — the last being the entire delta over master's pin, which lacks it. `only_mintable` is present on both the mint side (`crates/cdk/src/mint/issue`, `crates/cdk-axum/src/custom_handlers.rs`, `crates/cdk-sql-common/src/mint/quotes.rs`) and the wallet side (`crates/cdk/src/wallet/issue`, `crates/cdk/src/wallet/mint_connector/http_client.rs`). Validation limit worth stating plainly: because `cba8469b` is gone, the new pin cannot be diffed against the revision this PR's live-run validation was performed on, so equivalence of intent is not provable. The witness for the re-pin is compilation plus this repo's test suites, not a diff. The 2026-08-10 live-run result in the body stands as a record of what was observed on the old pin and is not re-established by this change. Commits follow on this branch; no force-push.
cba8469be5b37c067f0cf594b267801afe357960 no longer exists on any reachable
remote: absent from all 45 branches of github.com/vnprc/cdk, all 18 of
themhv/cdk, cashubtc/cdk and the shared fork network by commit API; absent
from all 33 forge repositories, which host no cdk fork; absent from every
git object store on this VM and from nexus:~/work/cdk.

e5a85f810a91bd1d8882a4a60ed93fd60bb691c1 (vnprc/cdk nutxx-wallet-v2) is
workspace 0.17.0, matching this repo's crate version requirements, and
carries the four cdk APIs this branch and the multi-unit sweeper call:
batch_check_mint_quote_status, batch_mint, get_unissued_mint_quotes and
only_mintable. The last is the whole delta over master's pin, present both
mint-side and wallet-side.

Refs vnprc/hashpool#25
Incremental re-resolution after the master merge; MSRV pins (time
0.3.41, serde_with 3.17.0, darling 0.21.3, simple_asn1 0.6.3) verified
intact against devenv's rustc 1.86.0.
Only the git URL moves (github.com/vnprc/cdk -> ssh://git@forge.anarch.diy:2222/vnprc/cdk); rev stays e5a85f8. protocols/Cargo.toml and roles/Cargo.toml [patch.crates-io] entries, devenv.nix cdkRepo, and roles/Cargo.lock's resolved source URLs all move together.
Add .cargo/config.toml with net.git-fetch-with-cli=true: cargo's bundled
libgit2 cannot authenticate to the forge over SSH (ssh-agent-only, no
fallback to default key files), so fetches must shell out to system git.
Without this a committed forge pin is unbuildable.

Also move the justfile's CDK_REPO/CDK_COMMIT pin (a fifth pin site, not
previously tracked) to the forge URL and this branch's e5a85f8 rev, and
repoint docs/AGENTS.md's live CDK-dependency note at the forge. docs/archive
is left untouched as history.
Author
Collaborator

In plain English

The pool's wallet has to know which mining rewards it is owed before it can collect them. It used to find out by being told: the mint sent a message for each reward, one at a time. If a message went missing, the wallet never learned that reward existed, and the ecash for it was silently never collected. Nobody noticed, because nothing was obviously broken — the money just quietly did not arrive.

This changes the wallet to ask instead of wait. Every fifteen seconds it asks the mint "what do I still have outstanding?" and works from that answer. A lost message now costs a few seconds instead of the reward.

This is the base the other two epoch pull requests build on, so it merges first.

Status

Verified on a running dev stack: shares were mined, rewards were quoted and collected, and the balance grew as expected. The dependency this branch pins now lives on the forge rather than only on GitHub, so it cannot go missing again.

## In plain English The pool's wallet has to know which mining rewards it is owed before it can collect them. It used to find out by being told: the mint sent a message for each reward, one at a time. If a message went missing, the wallet never learned that reward existed, and the ecash for it was silently never collected. Nobody noticed, because nothing was obviously broken — the money just quietly did not arrive. This changes the wallet to ask instead of wait. Every fifteen seconds it asks the mint "what do I still have outstanding?" and works from that answer. A lost message now costs a few seconds instead of the reward. This is the base the other two epoch pull requests build on, so it merges first. ## Status Verified on a running dev stack: shares were mined, rewards were quoted and collected, and the balance grew as expected. The dependency this branch pins now lives on the forge rather than only on GitHub, so it cannot go missing again.
The sweeper needs `Wallet::cleanup_orphaned_quote_reservations`, which clears
only orphaned quote reservations and never resumes a saga. The previously
pinned revision exposed no such entry point: the only public recovery method
also resumed Swap, Send and Receive sagas, which cannot safely run on a wallet
handle the faucet is also driving.

Pin moves in the four declared sites and resolves in roles/Cargo.lock. The
MSRV-pinned transitive deps are unchanged: time 0.3.41, serde_with 3.17.0,
darling 0.21.3, simple_asn1 0.6.3.
The mint issues a distinct currency unit per mining epoch, but the sweeper only
minted the one unit its wallet was built with. After the first rotation it
fetched the new unit's quotes, stored them, and silently never minted them, so
every epoch after the first lost all of its mining rewards.

The sweeper now mints every unit it sees. A per-unit wallet handle is built on
first sight from one shared localstore, seed and mint metadata cache, and each
unit runs the same sweep: discover, status-check, then P2PK-locked batch mint.
Discovery unions the unit-agnostic pubkey lookup with a unit-blind read of the
local store's unissued quotes, so a failing lookup endpoint cannot hide a
backlog. Work is bounded per unit per pass -- at most 200 quotes selected,
locally-mintable first, processed in chunks of 50 -- so one bad quote id wedges
at most its own chunk and a rotation backlog drains over successive passes.

Two faults found by running the full dev stack are fixed here, because the
sweeper cannot mint a new epoch's unit without them:

The shared mint metadata cache is keyed by mint, not by unit, and the default
load policy treats "populated and within TTL" as fresh. A brand-new epoch unit
therefore read as cached while its keyset was absent, and every mint attempt
failed with "Unknown Keyset" forever. The first sight of a unit now forces one
refresh, and a unit whose refresh fails is skipped for the pass rather than
having quotes reserved behind a keyset that could not be looked up.

A failed batch mint leaves the quote reserved in the local store with nothing
to release it, wedging a paid quote behind "Quote already in use by another
operation". Each pass now clears orphaned reservations for the unit first, via
cdk's narrow cleanup, which never resumes or compensates a saga and so is safe
on the base unit's handle even though the faucet drives its own Send saga over
it. The count of cleared reservations is logged when non-zero.

The faucet, web UI and quote notification path keep using the single base-unit
wallet exactly as before. The wallet DB schema is untouched.

Closes vnprc/hashpool#17. Closes vnprc/hashpool#29. Refs vnprc/hashpool#13.
Author
Collaborator

Superseded by a squashed branch with the same tree. Closing and reopening because the forge refuses force-push on an open pull request branch. Note this branch now also carries the multi-unit sweeper, which merged into it as vnprc/hashpool#31, so the replacement covers both and its title and body are rewritten to say so.

Superseded by a squashed branch with the same tree. Closing and reopening because the forge refuses force-push on an open pull request branch. Note this branch now also carries the multi-unit sweeper, which merged into it as vnprc/hashpool#31, so the replacement covers both and its title and body are rewritten to say so.
vnprc-agent closed this pull request 2026-10-09 06:14:02 +01:00

Pull request closed

Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
vnprc/hashpool!21
No description provided.