Keyset final_expiry silently kills held balances — wallets must rotate off inactive keysets (NUT-02)
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 5/5
- 見積もり時間
- 1週間以上
- 初心者へのやさしさ
- 28/100
調査の方向性
Start with tests/cloud-lab/run-keyset-rotation.sh and the related issues #409, #413, and #416 to understand the existing rotation and error-classification work. The issue proposes several separable directions, so confirm which one is in scope before starting; for the lifecycle monitor, inspect the keyset refresh and held-proof handling entry points. Done means the selected behavior is covered by tests, including the expired-keyset case.
索引モデルが issue の本文から書いたものです。
説明
Wallet hygiene: keyset final_expiry silently kills held balances — wallets must rotate off inactive keysets (NUT-02 SHOULD). Proven in the cloud lab.
What happened (lab evidence, #416): paying with proofs whose keyset was expired fails with "could not swap proofs: Keyset has expired" — the proofs are permanently unspendable at that mint. This is spec-correct mint behavior, not a bug: NUT-02 Keyset Final Expiry says that after final_expiry the mint "is no longer obliged to fulfill promises signed with the keys from that keyset" and may irrevocably delete the keyset's nullifiers. The spec even standardized the refusal: error code 12003 "Keyset has expired" (nuts PR #367). Implementations differ — cdk enforces expiry, nutshell never expires (nutshell #7, tracked in the cashu-audit divergence database) — and the spec's active flag conflates mintable vs redeemable, but none of that is ours to fix upstream.
Why it matters to TollGate: any wallet balance held across a keyset rotation+expiry window can be wiped:
- Reseller mode: the reseller holds balances at upstream mints (
min_balanceinaccepted_mints) between purchases — an upstream that rotates with expiry (cdk mints do) silently destroys that float. - Operator payouts: the merchant wallet holds received funds between payout cycles (
min_payout_amount,payout_interval_seconds); a fee-charging cdk-style mint rotating keysets mid-cycle destroys receivables. - Customers: a customer holding a token from an expired keyset gets a refusal — currently misclassified as
payment-error-mint-unreachable(see the note on #409), telling them to retry a permanently dead token.
Suggested work items (split-able):
- Keyset lifecycle monitor: on keyset refresh, detect
active=falsefor keysets with held proofs and proactively swap them to the active keyset (NUT-02: wallets SHOULD prioritize this; NUT-03 swap). React tofinal_expiryin the keyset response with an operator warning when held value is affected. - Classifier fix (already noted on #409): match "Keyset has expired" / code 12003 to a dedicated
payment-error-keyset-expirednotice instead of mint-unreachable, keeping the cause in the message. The pin intest_keyset_rotation.pyupdates consciously with it. - Advertisement/trust surface (product question): consider surfacing keyset-expiry policy (e.g. keysets with
final_expiryset) in mint metadata evaluation, so routers can prefer mints without expiry time bombs for float.
Repro: tests/cloud-lab/run-keyset-rotation.sh (phase A mints on a 100-ppk keyset; phase B expires it; the stashed token dies with 12003-class refusal).
Refs: #416 (rotation lane + pins), #409 (classifier), #413 (fee mint), cashu-audit issues/keyset-active-flag-semantics.md + divergences/CDK-VS-NUTSHELL-ALIGNMENT-2026-07-29.md ("#7 old keysets never expire"), nuts #367 (error 12003), NUT-02 §Keyset Final Expiry.
- 主要言語
- Go
- スター
- 12
- フォーク
- 14
- 平均マージ
- 3日 20時間
- マージ済み PR(30日)
- 261
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートなし
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
OpenTollGate/tollgate-module-basic-go のほかの issue
-
難易度 1/5 1時間未満 初心者へのやさしさ 85/100
OpenTollGate/tollgate-module-basic-go#1159 ·
メンテナーはふだん 1 日以内に返信
-
Degraded-mode hygiene follow-ups: dead walletNotInitializedError() + main.go's startup log still over-promises mint recovery対応中かも このイシューにリンクされたプルリクエストがオープン中、またはマージ済みです。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
OpenTollGate/tollgate-module-basic-go#1135 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
setup_uhttpd_portal hardcodes the GUEST portal docroot to the TollGate path — the brand token cannot select a re-brand guest portal対応中かも このイシューにリンクされたプルリクエストがオープン中、またはマージ済みです。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
OpenTollGate/tollgate-module-basic-go#1122 ·
メンテナーはふだん 1 日以内に返信
-
changelog: dependency-security sweep missing from [Unreleased] [Amperstrand fork #105 review debt]オープン
難易度 1/5 1時間未満 初心者へのやさしさ 72/100
OpenTollGate/tollgate-module-basic-go#1055 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
OpenTollGate/tollgate-module-basic-go#1027 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
OpenTollGate/tollgate-module-basic-go の issue をすべて見る
似ている issue
-
date_time, url_encode, html_escape, to_unix_time, contains_all and contains_any panic when called without arguments対応中かも @antcybersec が今日担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
projectdiscovery/dsl#372 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
JuliusBrussee/caveman#1449 ·
メンテナーはふだん 1 日以内に返信
-
bot-found bug priority: P3
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
madenvel/KalinkaPlayer#498 ·
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
メンテナーはふだん 1 日以内に返信
-
clawsweeper:fix-shape-clear clawsweeper:needs-maintainer-review clawsweeper:needs-product-decision clawsweeper:no-new-fix-pr clawsweeper:source-repro impact:ux-friction issue-rating: 🦞 diamond lobster P2
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
openclaw/gogcli#1291 · コメント 1 件 · リアクション 1 件 ·
メンテナーはふだん 1 日以内に返信