lnurlcash-conformance 0.4.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 +96 -0
- package/README.md +31 -48
- package/mock-mint/index.d.ts +25 -20
- package/mock-mint/index.mjs +167 -83
- package/package.json +1 -1
- package/runner/index.mjs +209 -77
- package/vectors/lifecycle.json +7 -6
- package/vectors/mint-to-hash.json +50 -32
- package/vectors/note-url.json +1 -1
- package/vectors/pay-request.json +51 -2
- package/vectors/responses.json +32 -3
- package/vectors/retried-mutation.json +1 -1
- package/vectors/signature.json +1 -1
- package/vectors/withdraw-info.json +59 -23
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,102 @@ Semantic versioning. While the LUD-25 draft is unmerged, `0.x` minor bumps
|
|
|
4
4
|
may add or tighten checks that a previously-passing mint now fails; pin an
|
|
5
5
|
exact version if you gate CI on the grade.
|
|
6
6
|
|
|
7
|
+
## Unreleased
|
|
8
|
+
|
|
9
|
+
## 0.6.0 - 2026-09-03
|
|
10
|
+
|
|
11
|
+
**Offline verification and mutation replay now grade the current LUD-25
|
|
12
|
+
MUSTs.** A rotate, split or merge without a valid note signature fails rather
|
|
13
|
+
than warning, as does a byte-identical retry that is answered as already
|
|
14
|
+
spent. The conforming mock now replays mutations by default; `signatures=false`
|
|
15
|
+
and `retriedMutation=refuse` remain explicit adversarial fixtures.
|
|
16
|
+
|
|
17
|
+
The optional hash lookup is also checked end to end once a mint offers it.
|
|
18
|
+
The runner requires exactly one of `k1` and `h`, compares the unknown-h response
|
|
19
|
+
with an unknown-k1 response, then burns the original note and confirms its hash
|
|
20
|
+
still gets that same unknown-note answer. This catches the privacy leak where a
|
|
21
|
+
mint answers a burned `h` as already spent. The mock gains separate
|
|
22
|
+
`revealsSpent` and `acceptsBoth` fixtures proving those checks fail.
|
|
23
|
+
|
|
24
|
+
The signature vectors now describe `mintPubkey` as a stable SERVICE key. It may
|
|
25
|
+
be the funding node identity where compatible signing exists, or a dedicated,
|
|
26
|
+
persistent secp256k1 key where it does not.
|
|
27
|
+
|
|
28
|
+
**The client-facing vectors carry the same MUSTs.** They had been left behind
|
|
29
|
+
by the prose, still describing a signature as one accepted shape among
|
|
30
|
+
several, so a wallet library could pass every vector while ignoring the
|
|
31
|
+
requirement entirely.
|
|
32
|
+
|
|
33
|
+
`withdraw-info.json` now puts `mintPubkey` on every accepted withdrawRequest
|
|
34
|
+
and rejects a response that omits it or publishes something that is not a
|
|
35
|
+
33-byte compressed secp256k1 key. A wallet handed a note by a mint that
|
|
36
|
+
publishes no key has nothing to verify it against, which is the gap offline
|
|
37
|
+
verification exists to close.
|
|
38
|
+
|
|
39
|
+
`responses.json` gains an `unverifiable` outcome for a mutation the SERVICE
|
|
40
|
+
confirms but does not sign. It is deliberately not folded into the existing
|
|
41
|
+
`error` or `ambiguous` outcomes: the mutation definitely landed, so the
|
|
42
|
+
wallet-generated secret is the only key to a real note and must be kept, and
|
|
43
|
+
a consumer that treats this as an ordinary failure loses the money it is
|
|
44
|
+
trying to protect. Every case now also declares the `op` it is driven
|
|
45
|
+
through, because a melt mints nothing and so needs no signature, while a
|
|
46
|
+
rotate returning none is the new outcome.
|
|
47
|
+
|
|
48
|
+
**The mock mint starts when its path reaches it through a symlink.** The
|
|
49
|
+
standalone guard compared `import.meta.url` against `file://` plus
|
|
50
|
+
`process.argv[1]`, and node resolves a module's URL to its real path while
|
|
51
|
+
argv[1] stays exactly as it was typed. A checkout symlinked into a sibling
|
|
52
|
+
workspace - which is how the language ports pick this repo up locally - failed
|
|
53
|
+
that comparison, so the mint exited silently, printing nothing at all. Every
|
|
54
|
+
port's adversarial suite then hung to EOF and reported the mint as never having
|
|
55
|
+
announced itself, which says nothing about the cause. CI, checking the repo out
|
|
56
|
+
for real, was never affected, so this only ever broke the local run.
|
|
57
|
+
|
|
58
|
+
## 0.5.0 - 2026-08-31
|
|
59
|
+
|
|
60
|
+
**Breaking: the current LUD-25 draft's mandatory mint comment is now the
|
|
61
|
+
baseline.** Every minting payRequest must advertise `commentAllowed >= 64`,
|
|
62
|
+
and every quote must carry `comment=hex(sha256(secret))`; missing or malformed
|
|
63
|
+
comments are refused before invoice creation. The payment preimage is
|
|
64
|
+
settlement proof and never a bearer credential. `mintToHash`/`h` remains an
|
|
65
|
+
additive ForgeSworn compatibility and receipt extension: when used, `h` must
|
|
66
|
+
repeat the mandatory comment and cannot replace it. The mock, runner,
|
|
67
|
+
lifecycle and pay-request vectors all enforce the same rule, including a
|
|
68
|
+
mismatched `h`/`comment` refusal. `commentFallsBack` remains only as the
|
|
69
|
+
non-compliant historical fixture documented in
|
|
70
|
+
`docs/COMMENT-IS-MANDATORY.md`.
|
|
71
|
+
|
|
72
|
+
**A mint that is not a Lightning Address is no longer failed for having no
|
|
73
|
+
mint address document.** The document is probed by swapping
|
|
74
|
+
`/.well-known/lnurlp/` for `/.well-known/lnurlw/` in the payRequest URL. On a
|
|
75
|
+
mint served from a plain path that swap changes nothing, so the probe
|
|
76
|
+
re-fetched the payRequest, read `tag: "payRequest"`, and failed the mint for
|
|
77
|
+
an optional document it has nowhere to publish. Found by grading
|
|
78
|
+
`bitkarrot/lnurlmint`, which is served at `/lnurlmint/lnurlp/<id>`; it now
|
|
79
|
+
grades 6 passed, 0 failed on the checks that predate the mandate above.
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
**Two checks for the draft's 25 August additions, both optional-graded.**
|
|
83
|
+
|
|
84
|
+
`answers a note lookup by hash without the secret` covers "Checking a note
|
|
85
|
+
without exposing it". The capability is detected rather than announced: the
|
|
86
|
+
draft deliberately gives an unrecognized `h` the same answer an unknown
|
|
87
|
+
`k1` gets, so a live note's own hash is the only probe that separates a
|
|
88
|
+
mint which implements the lookup from one which never did. A mint that does
|
|
89
|
+
not offer it is not graded down; one that offers it must omit `k1` from the
|
|
90
|
+
reply - a wallet asking by hash already holds the secret, and filling the
|
|
91
|
+
field in puts the note back on the wire the lookup exists to keep it off -
|
|
92
|
+
must report the same value by hash as by `k1`, and must refuse a hash it
|
|
93
|
+
never registered.
|
|
94
|
+
|
|
95
|
+
`refuses an oversized merge cleanly` covers the merge cap. The cap itself
|
|
96
|
+
is a MAY, so a mint that does not name one is not graded down, only
|
|
97
|
+
reported, since a wallet must then batch by URL length rather than rely on
|
|
98
|
+
a refusal. The one answer that fails is `OK`: every input on that probe is
|
|
99
|
+
fabricated by the runner, so a mint accepting it has minted an output
|
|
100
|
+
against notes it never held, which is what a truncated `k1` list read as a
|
|
101
|
+
shorter merge looks like from outside.
|
|
102
|
+
|
|
7
103
|
## 0.4.0 - 2026-08-26
|
|
8
104
|
|
|
9
105
|
**The mint-output naming check now reads the spelling LUD-25 actually
|
package/README.md
CHANGED
|
@@ -60,7 +60,7 @@ for (const c of cases) {
|
|
|
60
60
|
| `payment-request.json` | `lnurlcashreq1`: one holder asking another for value |
|
|
61
61
|
| `settle-for-value.json` | the decision table a server works through to take a note as payment |
|
|
62
62
|
| `retried-mutation.json` | what makes a repeated mutation a retry rather than a double-spend |
|
|
63
|
-
| `mint-to-hash.json` |
|
|
63
|
+
| `mint-to-hash.json` | additive `mintToHash` compatibility and optional bound LUD-21 receipts; not baseline LUD-25 |
|
|
64
64
|
| `lifecycle.json` | behavioural requirements, as scenarios to drive |
|
|
65
65
|
| `threat-suite.json` | the transport/exposure scorecard — candidate spec options against fixed attacks (non-normative) |
|
|
66
66
|
|
|
@@ -92,7 +92,7 @@ must survive:
|
|
|
92
92
|
| `--echoWrongK1` | answers the informational GET with a different `k1` |
|
|
93
93
|
| `--lieAboutValue=N` | reports a `maxWithdrawable` it never signed |
|
|
94
94
|
| `--signatureLayout=leading` | emits the recovery id at the other end |
|
|
95
|
-
| `--signatures=false` |
|
|
95
|
+
| `--signatures=false` | deliberately violates LUD-25 by issuing no signatures |
|
|
96
96
|
| `--serverGeneratedSecrets` | hands back a secret it generated — the exposure `h` exists to close |
|
|
97
97
|
| `--meltNeverSettles` | holds every melt in flight, so notes stay `pending` |
|
|
98
98
|
| `--meltAlwaysFails` | fails every payment, restoring the note |
|
|
@@ -100,24 +100,27 @@ must survive:
|
|
|
100
100
|
| `--sunset` | refuses anything that grows its liabilities |
|
|
101
101
|
| `--baseFeeMsat=N --feePpm=N` | advertises and withholds a mint fee |
|
|
102
102
|
| `--roundFeeToSat` | rounds the withheld fee up to a whole sat — the note mints short of the formula |
|
|
103
|
-
| `--verifyLeaksEarly` | serves
|
|
103
|
+
| `--verifyLeaksEarly` | serves a preimage before settlement, falsely claiming payment proof before payment happened |
|
|
104
|
+
| `--retriedMutation=refuse` | deliberately answers an identical mutation retry as already spent instead of replaying its original success |
|
|
105
|
+
| `--hashLookup=echoesK1` | offers hash lookup but puts a `k1` back in the response |
|
|
106
|
+
| `--hashLookup=answersUnknown` | offers hash lookup but invents a note for an unknown hash |
|
|
107
|
+
| `--hashLookup=revealsSpent` | offers hash lookup but distinguishes a burned hash from an unknown note |
|
|
108
|
+
| `--hashLookup=acceptsBoth` | offers hash lookup but accepts `k1` and `h` together |
|
|
104
109
|
| `--mintToHashAcceptsMalformedH` | claims `mintToHash` and invoices an `h` that is not 64 lowercase hex, so a wallet pays for a quote the mint will refuse |
|
|
105
110
|
| `--mintToHashAcceptsUsedH` | claims it and invoices an `h` that already names a note, an invoice or another quote's output |
|
|
106
|
-
| `--mintToHashIgnoresH` | claims it
|
|
111
|
+
| `--mintToHashIgnoresH` | claims it but accepts `h` and mandatory `comment` naming different outputs |
|
|
107
112
|
|
|
108
113
|
The three `mintToHash*` misbehaviours need `--mintToHash` alongside them;
|
|
109
114
|
on their own they do nothing, because a mint that never offered the
|
|
110
115
|
capability cannot misuse it.
|
|
111
|
-
| `--verify=false` | no LUD-21 endpoint at all, not merely unadvertised |
|
|
112
|
-
| `--withdrawLinkForm=lnurlw` | spells `withdrawLink` as `lnurlw://host/w` instead of the plain `https://host/w` the reference mint emits. Both are legal; a client has to take both |
|
|
113
116
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
for them, so a mock started with no options answers exactly what it always
|
|
117
|
-
answered:
|
|
117
|
+
The remaining flags enable optional features, legal wire variants, or make a
|
|
118
|
+
conforming default explicit. Optional fields stay absent unless requested:
|
|
118
119
|
|
|
119
120
|
| Flag | What it does |
|
|
120
121
|
| --- | --- |
|
|
122
|
+
| `--verify=false` | provides no LUD-21 endpoint at all, rather than merely leaving it unadvertised |
|
|
123
|
+
| `--withdrawLinkForm=lnurlw` | spells `withdrawLink` as `lnurlw://host/w` instead of the plain `https://host/w` the reference mint emits. Both are legal; a client has to take both |
|
|
121
124
|
| `--name --description --contact --tosUrl --motd --version` | mint info on the experimental discovery endpoint: who runs this, how to reach them, the terms, and what the operator wants holders to know today |
|
|
122
125
|
| `--baseFeeMsat --feePpm` | also publishes `fees: {baseFeeMsat, feePpm}` on that endpoint, the structured twin of the fee line in the payRequest metadata |
|
|
123
126
|
| `--stats` | serves `GET /stats`: what the mint owes, what is in flight, what the node holds, and the coverage between them |
|
|
@@ -125,8 +128,9 @@ answered:
|
|
|
125
128
|
| `--previousPubkeys=a,b` | keys this mint has signed under before, so notes issued before a rotation still verify |
|
|
126
129
|
| `--previousPrivateKey=<hex>` | an old signing key the mock still holds. Its public half joins `previousPubkeys` on its own |
|
|
127
130
|
| `--signWithPreviousKey` | issues every note under that old key while still advertising the new one: the mid-rotation state a mint passes through when the advertisement moves before the signer |
|
|
128
|
-
| `--retriedMutation=replay` | answers a byte-identical repeat of a mutation with the original success
|
|
129
|
-
| `--
|
|
131
|
+
| `--retriedMutation=replay` | answers a byte-identical repeat of a mutation with the original success. This is the conforming default; use `refuse` only as an adversarial fixture |
|
|
132
|
+
| `--hashLookup=true` | accepts an informational lookup by `h=sha256(k1)` without returning the bearer secret. Off by default because the capability is optional |
|
|
133
|
+
| `--mintToHash` | accepts `h` alongside the mandatory identical comment and enables the additive quote/receipt fields. Off by default; baseline comment-bound minting remains on |
|
|
130
134
|
| `--mintReceipt` | with `--mintToHash`, adds the optional quote commitment and signed LUD-21 settlement receipt |
|
|
131
135
|
| `--mintToHashAdvertisedOn=quote` | narrows which of the three places claim it (`payRequest`, `mintAddress`, `quote`); all three by default. Changes only what is claimed, never what the mint does |
|
|
132
136
|
|
|
@@ -155,43 +159,23 @@ npx lnurlcash-conform mint@example.com
|
|
|
155
159
|
|
|
156
160
|
Read-only by default: resolves the payRequest, checks the `withdrawLink`
|
|
157
161
|
(either legal spelling, and the report says which one the mint uses),
|
|
158
|
-
the fee advertisement, invoice amounts,
|
|
159
|
-
|
|
160
|
-
everyone on the payment's route knows the payment hash), whether an
|
|
162
|
+
the fee advertisement, invoice amounts, mandatory `commentAllowed: 64`,
|
|
163
|
+
pre-invoice rejection of missing or malformed mint comments, whether an
|
|
161
164
|
unknown note is reported distinguishably from a spent one, and the
|
|
162
165
|
experimental mint address.
|
|
163
166
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
money moves, because the other two can be cached). Anything that is not
|
|
177
|
-
exactly the boolean `true` is no.
|
|
178
|
-
|
|
179
|
-
A mint that says nothing anywhere is reported as not offering it and passes,
|
|
180
|
-
which is every mint today. A mint that claims it is asked to prove the
|
|
181
|
-
refusals: a malformed `h` must get no invoice at all, since a wallet that
|
|
182
|
-
pays for a quote the mint will reject has bought nothing. Malformed means
|
|
183
|
-
not 32 bytes of hex, in any casing. A wallet MUST send `h` as 64 lowercase
|
|
184
|
-
hex and every client here does, but hex is case-insensitive, so a service
|
|
185
|
-
SHOULD normalise before comparing and MUST NOT read `AAAA...` and `aaaa...`
|
|
186
|
-
as two different outputs: keying the string it was handed files the note
|
|
187
|
-
where the wallet will never look for it, and nobody is told. A service that
|
|
188
|
-
refuses upper case outright is being strict rather than wrong, so the grader
|
|
189
|
-
does not probe it either way. Where the three
|
|
190
|
-
claims disagree, the grader names the disagreement rather than failing it:
|
|
191
|
-
none of those loses anyone money on its own. What is failed is a mint that
|
|
192
|
-
claims the capability and does not bind, because a wallet believing the
|
|
193
|
-
claim stops rotating on sight; that one needs a settlement to see, so it
|
|
194
|
-
rides on `--preimage`.
|
|
167
|
+
Current LUD-25 minting is always comment-bound. The wallet persists a secret,
|
|
168
|
+
sends `comment=hex(sha256(secret))`, and the payment preimage remains ordinary
|
|
169
|
+
settlement proof. A mint that cannot accept the 64-character commitment, or
|
|
170
|
+
that silently creates a preimage-backed note, fails grading.
|
|
171
|
+
|
|
172
|
+
`mintToHash` is retained as an additive compatibility field. When advertised,
|
|
173
|
+
the runner sends `h` alongside the mandatory comment and requires both to name
|
|
174
|
+
the same output. A malformed `h` must be rejected before invoice creation.
|
|
175
|
+
The payRequest, experimental mint-address document and quote echo are checked
|
|
176
|
+
separately because each claim has a different lifetime. Disagreement between
|
|
177
|
+
those extension advertisements warns; failure to honour a claimed binding
|
|
178
|
+
fails when the paid `--preimage` check proves where the note actually landed.
|
|
195
179
|
|
|
196
180
|
Three other things a mint may publish are graded softly, because none of them
|
|
197
181
|
is in LUD-25: the mint info on the discovery endpoint, a `/stats` endpoint
|
|
@@ -243,8 +227,7 @@ advertised `mintPubkey` or any key the mint still publishes as a previous
|
|
|
243
227
|
one, that split and merge conserve value - exactly,
|
|
244
228
|
under LUD-25's fee algebra, when the mint's fee advertisement is known -
|
|
245
229
|
that a byte-identical repeat of a mutation is answered with the original
|
|
246
|
-
success rather than as an already-spent input
|
|
247
|
-
has not implemented it is reported as such rather than failed), and that a
|
|
230
|
+
success rather than as an already-spent input, and that a
|
|
248
231
|
burned secret cannot be replayed. It also probes three adversarial shapes a
|
|
249
232
|
mint must refuse atomically: a duplicated `k1` (which a careless mint counts
|
|
250
233
|
twice, minting money from nothing), an output hash that collides with an
|
package/mock-mint/index.d.ts
CHANGED
|
@@ -10,6 +10,13 @@ export type WithdrawLinkForm = 'lnurlw' | 'plain'
|
|
|
10
10
|
|
|
11
11
|
export type RetriedMutation = 'refuse' | 'replay'
|
|
12
12
|
|
|
13
|
+
export type HashLookup =
|
|
14
|
+
| boolean
|
|
15
|
+
| 'echoesK1'
|
|
16
|
+
| 'answersUnknown'
|
|
17
|
+
| 'revealsSpent'
|
|
18
|
+
| 'acceptsBoth'
|
|
19
|
+
|
|
13
20
|
/** where a mint claims it accepts an `h` on its pay callback */
|
|
14
21
|
export type MintToHashPlace = 'payRequest' | 'mintAddress' | 'quote'
|
|
15
22
|
|
|
@@ -26,7 +33,7 @@ export interface MockMintOptions {
|
|
|
26
33
|
* signmessage output unreordered.
|
|
27
34
|
*/
|
|
28
35
|
signatureLayout?: SignatureLayout
|
|
29
|
-
/** withhold sig/sig2
|
|
36
|
+
/** non-compliant: withhold the mandatory sig/sig2 */
|
|
30
37
|
signatures?: boolean
|
|
31
38
|
/**
|
|
32
39
|
* How the payRequest spells its withdrawLink. 'plain' (default) is the
|
|
@@ -124,27 +131,27 @@ export interface MockMintOptions {
|
|
|
124
131
|
/**
|
|
125
132
|
* What a retried mutation gets. A rotate, split or merge is a GET and
|
|
126
133
|
* HTTP stacks retry a GET on a dropped connection, so a SERVICE sees
|
|
127
|
-
* the byte-identical request twice. '
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
* SERVICE really did mint. Identical means the same input k1 set, the
|
|
134
|
+
* the byte-identical request twice. 'replay' (default) returns the
|
|
135
|
+
* original success as LUD-25 requires; 'refuse' is the non-compliant
|
|
136
|
+
* fixture. Identical means the same input k1 set, the
|
|
131
137
|
* same h, the same h2 and the same amount; anything else naming a
|
|
132
138
|
* burned input is refused exactly as before.
|
|
133
139
|
*/
|
|
134
140
|
retriedMutation?: RetriedMutation
|
|
141
|
+
/**
|
|
142
|
+
* Optional informational lookup by sha256(k1). `true` is conforming;
|
|
143
|
+
* string values reproduce distinct non-compliant responses.
|
|
144
|
+
*/
|
|
145
|
+
hashLookup?: HashLookup
|
|
135
146
|
/** serve GET /stats, the liabilities endpoint. Off means 404, as before. */
|
|
136
147
|
stats?: boolean
|
|
137
148
|
/** what the node behind a stats-publishing mock claims to hold */
|
|
138
149
|
localBalanceMsat?: number
|
|
139
150
|
/**
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
* thing `h` means on the withdraw callback - and credits the note at
|
|
145
|
-
* that id when the invoice settles. The payment preimage is then not a
|
|
146
|
-
* valid k1 for that note, which matters because every routing node on
|
|
147
|
-
* the payment path learns it, and so does anyone who saw the invoice.
|
|
151
|
+
* Additive compatibility spelling for the mandatory mint comment. Off,
|
|
152
|
+
* `h` is not advertised or read, while comment-bound minting remains the
|
|
153
|
+
* baseline. On, WALLET may repeat the same 64-hex output commitment as
|
|
154
|
+
* `h`, enabling the ForgeSworn/Moneyer quote and receipt vocabulary.
|
|
148
155
|
*
|
|
149
156
|
* The capability is advertised in three places: `mintToHash: true` on
|
|
150
157
|
* the payRequest (every mint has one, so it is what a wallet decides
|
|
@@ -176,11 +183,9 @@ export interface MockMintOptions {
|
|
|
176
183
|
*/
|
|
177
184
|
mintToHashAcceptsUsedH?: boolean
|
|
178
185
|
/**
|
|
179
|
-
* non-compliant, mintToHash on:
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
* nothing. The wallet stopped rotating on sight because it was told it
|
|
183
|
-
* did not need to, so this is the worst of both schemes.
|
|
186
|
+
* non-compliant, mintToHash on: accept `h` and mandatory `comment` even
|
|
187
|
+
* when they name different outputs. The normative comment still binds
|
|
188
|
+
* the note, but the extension claim and any h-watching signer disagree.
|
|
184
189
|
*/
|
|
185
190
|
mintToHashIgnoresH?: boolean
|
|
186
191
|
/**
|
|
@@ -236,8 +241,8 @@ export interface MockMintState {
|
|
|
236
241
|
opts: Required<MockMintOptions>
|
|
237
242
|
/**
|
|
238
243
|
* Fund a note directly, bypassing the minting flow. Returns the note's
|
|
239
|
-
* signature, or undefined
|
|
240
|
-
* false`
|
|
244
|
+
* signature, or undefined only for the deliberately non-compliant
|
|
245
|
+
* `signatures: false` fixture.
|
|
241
246
|
*
|
|
242
247
|
* Pass `{previousKey: true}` to sign it under `previousPrivateKey`
|
|
243
248
|
* instead, which is how a case puts one note under the old signing key
|