lnurlcash-kit 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ## 0.6.0 - 2026-08-31
6
+
7
+ - `namesMintOutput()` now requires `commentAllowed >= 64`; the additive
8
+ `mintToHash` advertisement alone no longer authorizes minting.
9
+ - `fetchPayRequest()` rejects a minting payRequest that cannot carry the
10
+ mandatory 64-character commitment, so a caller cannot proceed into an
11
+ invoice flow that has no conforming output name.
12
+ - Mint requests carrying an output hash continue to send identical
13
+ `comment` and `h` fields. Documentation now treats the former as mandatory
14
+ LUD-25 and the latter as the Moneyer/ForgeSworn receipt extension.
15
+
3
16
  Semantic versioning. While the LUD-25 draft is unmerged, `0.x` minor bumps
4
17
  may carry breaking changes; pin an exact version.
5
18
 
package/README.md CHANGED
@@ -154,18 +154,10 @@ callback — it is only observable as the note becoming spendable again. Other
154
154
  operations on that `k1` raise `PendingNoteError` meanwhile; retry, never
155
155
  read it as spent.
156
156
 
157
- **5. Rotate the instant you claim a minted note.** The preimage that mints a
158
- note is generated by the service, and if it serves
159
- [LUD-21](https://github.com/lnurl/luds/blob/luds/21.md) `verify`, *anyone*
160
- who saw the unpaid invoice can poll for it — the payment hash travels inside
161
- the invoice. First rotater wins. A wallet that rotates on settlement wins by
162
- construction; a human copying a preimage by hand does not.
163
-
164
- That is a race, and the way to win a race is not to enter it. Where a mint
165
- advertises `mintToHash`, name the note you are buying and the preimage is
166
- not its secret at all: see
167
- [Minting a note you named yourself](#minting-a-note-you-named-yourself).
168
- The rule above stands for every mint that does not offer it.
157
+ **5. Persist the mint secret before requesting the invoice.** Current
158
+ LUD-25 requires `comment=hex(sha256(secret))`; there is no preimage-backed
159
+ creation fallback. If the payRequest cannot carry that 64-character comment,
160
+ do not mint. Existing notes still redeem through ordinary LUD-03.
169
161
 
170
162
  ## Offline verification
171
163
 
@@ -297,18 +289,19 @@ can poll LUD-21 `verify` with the payment hash inside it and take the
297
289
  preimage the moment it settles, which is what a QR code on a desktop screen
298
290
  hands out.
299
291
 
300
- A mint can instead bind the note to a hash you supply, the same `h` you
301
- already send on every rotate, split and merge. Then you chose the secret,
302
- nobody else ever had it, and the preimage is an ordinary payment proof.
292
+ A current-draft mint binds the note to the hash supplied in the mandatory
293
+ LUD-12 comment. The kit repeats the same value as `h` for the additive
294
+ Moneyer/ForgeSworn receipt extension. You chose the secret, nobody else ever
295
+ had it, and the preimage is ordinary payment proof.
303
296
 
304
297
  ```ts
305
298
  import {
306
299
  fetchPayRequest, requestInvoice, claimMintedNote,
307
- deriveNoteRoot, deriveNoteSecret, hashK1
300
+ deriveNoteRoot, deriveNoteSecret, hashK1, namesMintOutput
308
301
  } from 'lnurlcash-kit'
309
302
 
310
303
  const pay = await fetchPayRequest(payUrl) // a Lightning Address resolves here
311
- if (!pay.mintToHash) { /* preimage path, rotate on claim */ }
304
+ if (!namesMintOutput(pay)) throw new Error('mint lacks commentAllowed: 64')
312
305
 
313
306
  const root = deriveNoteRoot(seed)
314
307
  const k1 = deriveNoteSecret(root, 'mint.example', nextIndex)
@@ -322,26 +315,20 @@ const claim = await claimMintedNote(pay.withdrawLink!, k1)
322
315
  // 'minted' -> claim.amountMsat is what it is worth, claim.callback melts it
323
316
  ```
324
317
 
325
- Ask before you buy. `mintToHash` is how a mint says it accepts the parameter,
326
- and reading it first is the difference between naming your own note and paying
327
- for one whose secret three other parties can learn. It turns up in three
328
- places, saying three different things:
318
+ Ask before you buy. `commentAllowed >= 64` is the normative minting
319
+ capability. `mintToHash` describes only the additive `h` and receipt fields:
329
320
 
330
321
  | Where | What it means |
331
322
  | --- | --- |
332
- | `PayRequestInfo.mintToHash` | "I accept an `h`." **Decide from this one.** |
323
+ | `PayRequestInfo.commentAllowed` | room for the mandatory hash comment; required for minting |
324
+ | `PayRequestInfo.mintToHash` | "I also accept the matching `h` extension." |
333
325
  | `MintAddressInfo.mintToHash` | the same fact on the experimental discovery document |
334
326
  | `InvoiceResult.mintToHash` | "I bound *this quote* to the hash you named" |
335
327
 
336
- Prefer the payRequest: it is the only endpoint every mint has, it is where
337
- your wallet already is when it is about to mint, and it sits next to the
338
- `withdrawLink` the draft already hangs there for LNURLcash's sake. Fall back
339
- to `fetchMintAddress` for a mint that only advertises on that document. A mint
340
- that says it in neither place ignores the `h`, keys the note by the preimage
341
- as it always has, and the verify path is unchanged. Anything that is not
342
- exactly `true` is a no, everywhere, and `false` on the invoice result is
343
- silence rather than a refusal, so decide from the advertisement and claim by
344
- probing.
328
+ Decide whether minting is possible from `commentAllowed` on the payRequest;
329
+ never substitute the mint-address extension field. Anything other than
330
+ boolean `true` is no for `mintToHash`, but that affects only extension receipt
331
+ handling. The note remains comment-bound either way.
345
332
 
346
333
  **Persist the secret before you ask for the invoice.** Paying for a note and
347
334
  then losing the secret is the one way this is worse than the preimage scheme,
package/dist/index.js CHANGED
@@ -1048,14 +1048,23 @@ var fetchPayRequest = async (url, options = {}) => {
1048
1048
  throw new ProtocolError("Not a payRequest (unexpected response).");
1049
1049
  }
1050
1050
  const mintFee = typeof body.metadata === "string" ? parseMintFee(body.metadata) : null;
1051
+ const commentAllowed = asNumber(body.commentAllowed);
1052
+ if (body.withdrawLink !== void 0 && typeof body.withdrawLink !== "string") {
1053
+ throw new ProtocolError("A minting payRequest has an invalid withdrawLink.");
1054
+ }
1055
+ if (typeof body.withdrawLink === "string" && !(typeof commentAllowed === "number" && commentAllowed >= 64)) {
1056
+ throw new ProtocolError(
1057
+ "A minting payRequest must allow a 64-character output commitment."
1058
+ );
1059
+ }
1051
1060
  return {
1052
1061
  ...body,
1053
1062
  mintFee: mintFee ?? void 0,
1054
1063
  mintToHash: asBoolean(body.mintToHash),
1055
- commentAllowed: asNumber(body.commentAllowed)
1064
+ commentAllowed
1056
1065
  };
1057
1066
  };
1058
- var namesMintOutput = (info) => info.mintToHash === true || typeof info.commentAllowed === "number" && info.commentAllowed >= 64;
1067
+ var namesMintOutput = (info) => typeof info.commentAllowed === "number" && info.commentAllowed >= 64;
1059
1068
  var asBoundMintCommitment = (value) => {
1060
1069
  if (!value || typeof value !== "object") return void 0;
1061
1070
  const raw = value;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lnurlcash-kit",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "LNURLcash (LUD-25) bearer note client for TypeScript - mint, rotate, split, merge, melt, and verify offline",
5
5
  "author": "TheCryptoDonkey",
6
6
  "license": "MIT",
@@ -60,7 +60,7 @@
60
60
  },
61
61
  "devDependencies": {
62
62
  "@types/node": "^26.2.0",
63
- "lnurlcash-conformance": "^0.3.0",
63
+ "lnurlcash-conformance": "^0.5.0",
64
64
  "tsup": "^8.5.0",
65
65
  "typescript": "^5.7.0",
66
66
  "vitest": "^3.0.0"