CDK: dynamic payment processor registration on a running mint #15

Closed
opened 2026-08-12 18:07:55 +01:00 by vnprc-agent · 1 comment
Collaborator

Teach CDK to register and retire payment processors on a running mint, so the hashpool mint can open a new currency unit per mining epoch without restarting. Designed for upstream: the API and implementation sketch live in docs/pr/cdk-dynamic-payment-processors.md, written to be filed on cashubtc/cdk for discussion before the PR.

Primary goals:

  • Mint::register_payment_processor(unit, method, limits, processor) — inserts into the processor map, starts the processor if not already running under another key (Arc::ptr_eq dedup preserved), spawns its payment-event consumer, and updates stored mint info (the NUT-04/NUT-05 settings that actually gate quote creation).
  • Mint::deregister_payment_processor(unit, method) — removes the settings entries so no new quotes can be created for the pair; existing quotes are unaffected (their lifecycle never consults those settings); the processor stops only when no key references it. This is what keeps the hashpool mint bounded as epochs accumulate.
  • Upstream lifecycle — fork commit on vnprc/cdk first so hashpool work proceeds; file the design doc as a cashubtc/cdk issue (owner action — the agent has no GitHub access by design); PR after discussion; pin forward when merged.

Current state and specifics

All verified at the pinned revision and byte-identical to upstream main: the processor map is a frozen Arc<HashMap<PaymentProcessorKey, DynMintPayment>>; wait_for_paid_invoices fans out consumer tasks once at Mint::start and never re-reads the map (the substantive change — its select! loop gains an mpsc arm that spawns consumers for late registrations); stored MintInfo is the real quote gate (nut04.get_settings(unit, method) → UnsupportedUnit), so registration is a two-phase update ordered map-first, mint-info-last (fail-closed); the pubsub spec captures a map snapshot at construction and needs the swappable handle. The container change follows the in-file house pattern (Mint.keysets is already ArcSwap + companion mutex); roughly a dozen read sites adapt mechanically. Extract the per-method settings construction out of MintBuilder::add_payment_processor into a shared helper so builder and runtime paths cannot drift.

Witness

Tests from the design doc: register on a started mint → quote → pay → mint in the new unit; deregister → new quote creation fails UnsupportedUnit while an existing unpaid quote still pays and mints; same instance under two keys → one start(), one consumer, independent deregistration; interrupted registration leaves the unit unquotable (fail-closed).

Scope

In: the CDK fork commit, the upstream filing, and the hashpool repin (the pin moves in four places together: protocols/Cargo.toml, roles/Cargo.toml, cdk-ehash's patch section, and devenv.nix cdkRepo/cdkCommit). Out: NUT-17 websocket advertisement for late-added units (documented gap), hot-swapping a live key's processor, caller authorization (embedder's concern), and all hashpool-side epoch logic (#16).

Part of #13.

Teach CDK to register and retire payment processors on a running mint, so the hashpool mint can open a new currency unit per mining epoch without restarting. Designed for upstream: the API and implementation sketch live in `docs/pr/cdk-dynamic-payment-processors.md`, written to be filed on cashubtc/cdk for discussion before the PR. Primary goals: - **`Mint::register_payment_processor(unit, method, limits, processor)`** — inserts into the processor map, starts the processor if not already running under another key (`Arc::ptr_eq` dedup preserved), spawns its payment-event consumer, and updates stored mint info (the NUT-04/NUT-05 settings that actually gate quote creation). - **`Mint::deregister_payment_processor(unit, method)`** — removes the settings entries so no new quotes can be created for the pair; existing quotes are unaffected (their lifecycle never consults those settings); the processor stops only when no key references it. This is what keeps the hashpool mint bounded as epochs accumulate. - **Upstream lifecycle** — fork commit on vnprc/cdk first so hashpool work proceeds; file the design doc as a cashubtc/cdk issue (owner action — the agent has no GitHub access by design); PR after discussion; pin forward when merged. ### Current state and specifics All verified at the pinned revision and byte-identical to upstream main: the processor map is a frozen `Arc<HashMap<PaymentProcessorKey, DynMintPayment>>`; `wait_for_paid_invoices` fans out consumer tasks once at `Mint::start` and never re-reads the map (the substantive change — its `select!` loop gains an mpsc arm that spawns consumers for late registrations); stored `MintInfo` is the real quote gate (`nut04.get_settings(unit, method)` → `UnsupportedUnit`), so registration is a two-phase update ordered map-first, mint-info-last (fail-closed); the pubsub spec captures a map snapshot at construction and needs the swappable handle. The container change follows the in-file house pattern (`Mint.keysets` is already `ArcSwap` + companion mutex); roughly a dozen read sites adapt mechanically. Extract the per-method settings construction out of `MintBuilder::add_payment_processor` into a shared helper so builder and runtime paths cannot drift. ### Witness Tests from the design doc: register on a started mint → quote → pay → mint in the new unit; deregister → new quote creation fails `UnsupportedUnit` while an existing unpaid quote still pays and mints; same instance under two keys → one `start()`, one consumer, independent deregistration; interrupted registration leaves the unit unquotable (fail-closed). ### Scope In: the CDK fork commit, the upstream filing, and the hashpool repin (the pin moves in four places together: `protocols/Cargo.toml`, `roles/Cargo.toml`, cdk-ehash's patch section, and `devenv.nix` cdkRepo/cdkCommit). Out: NUT-17 websocket advertisement for late-added units (documented gap), hot-swapping a live key's processor, caller authorization (embedder's concern), and all hashpool-side epoch logic (#16). Part of #13.
Author
Collaborator

Done. The feature exists and hashpool builds against it.

The original implementation was lost before it was ever published, so it was rebuilt from docs/pr/cdk-dynamic-payment-processors.md and is now three operations rather than the two the design named: register_payment_processor, deregister_payment_processor, and retire_payment_processor. Retire was added because deregister strands money — it drops the processor map entry, and since ehash is a custom payment method cdk re-consults that map even for an already-paid quote, so a deregistered unit returns UnsupportedUnit forever and the holder can never issue. Epoch retirement uses retire; a cdk test pins the guarantee by mutation.

Lives on the forge at vnprc/cdk branch agent/dynamic-payment-processors (874a1bf3), which is what hashpool pins. Upstream pull request is open at https://github.com/cashubtc/cdk/pull/2686, built separately on upstream main with the NUT-XX fork work excluded.

Two further cdk improvements came out of this and are recorded in vnprc/notes/hashpool/cdk-upstream-pr-handoff.md rather than done here: MintSaga::prepare_batch drops its registered compensations on an early failure, and NUT-17 websocket advertisement does not cover units registered after startup.

Done. The feature exists and hashpool builds against it. The original implementation was lost before it was ever published, so it was rebuilt from `docs/pr/cdk-dynamic-payment-processors.md` and is now three operations rather than the two the design named: `register_payment_processor`, `deregister_payment_processor`, and `retire_payment_processor`. Retire was added because deregister strands money — it drops the processor map entry, and since ehash is a custom payment method cdk re-consults that map even for an already-paid quote, so a deregistered unit returns `UnsupportedUnit` forever and the holder can never issue. Epoch retirement uses retire; a cdk test pins the guarantee by mutation. Lives on the forge at `vnprc/cdk` branch `agent/dynamic-payment-processors` (`874a1bf3`), which is what hashpool pins. Upstream pull request is open at <https://github.com/cashubtc/cdk/pull/2686>, built separately on upstream main with the NUT-XX fork work excluded. Two further cdk improvements came out of this and are recorded in `vnprc/notes/hashpool/cdk-upstream-pr-handoff.md` rather than done here: `MintSaga::prepare_batch` drops its registered compensations on an early failure, and NUT-17 websocket advertisement does not cover units registered after startup.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
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#15
No description provided.