@forgesworn/moneyer 0.13.2 → 0.14.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 +13 -0
- package/README.md +6 -5
- package/dist/server.js +13 -13
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.14.0 - 2026-09-11
|
|
4
|
+
|
|
5
|
+
- **A plain note is unsigned.** LUD-25 Part 2 certifies `cp1` notes only:
|
|
6
|
+
a hash has nothing to attest to without disclosing the secret. A rotate,
|
|
7
|
+
split or merge to a hash output now answers a bare `{"status":"OK"}`,
|
|
8
|
+
where it used to carry the old Part 1 signature over the hash. `cp1`
|
|
9
|
+
outputs are certified as before, with `cs1`, on the callback and on the
|
|
10
|
+
informational GET by `ck1` or `cp1`. The web client no longer attaches
|
|
11
|
+
the settlement receipt's signature to the note it mints; the receipt
|
|
12
|
+
still authenticates settlement, and `/verify` still carries it.
|
|
13
|
+
- Graded by lnurlcash-conformance 0.10.0, whose new Part 2 check rotates
|
|
14
|
+
a note into a `cp1` key, verifies its certificate, and rotates it home.
|
|
15
|
+
|
|
3
16
|
## 0.13.2 - 2026-09-11
|
|
4
17
|
|
|
5
18
|
- An operator name that changes owner drops the branch the previous owner
|
package/README.md
CHANGED
|
@@ -151,11 +151,11 @@ against the same endpoints every wallet uses:
|
|
|
151
151
|
before the invoice exists, invoice QR (tap opens a wallet), LUD-21
|
|
152
152
|
polling with a countdown, and on settlement the note is claimed and
|
|
153
153
|
**immediately rotated** - the preimage any invoice-observer could poll
|
|
154
|
-
out of verify is dead before the note is shown. The
|
|
155
|
-
|
|
156
|
-
|
|
154
|
+
out of verify is dead before the note is shown. The settlement receipt
|
|
155
|
+
is verified against the mint's advertised key in front of the user, and
|
|
156
|
+
the QR arrives under scratch-off silver foil, rubbed away like a scratch card.
|
|
157
157
|
- **Check a note**: live value, spent/unknown/pending classified in plain
|
|
158
|
-
words, offline
|
|
158
|
+
words, and offline verification of a `cp1` note's certificate.
|
|
159
159
|
- `MONEYER_WALLET_URL` (optional) links minted notes straight into a
|
|
160
160
|
companion web wallet's `#/claim` route.
|
|
161
161
|
|
|
@@ -354,7 +354,8 @@ named or spent, the two kinds mix freely:
|
|
|
354
354
|
- `GET /w?k1=<ck1>` or `GET /w?p=<cp1>` looks a Part 2 note up, and the
|
|
355
355
|
answer carries `sig`, the mint's certificate for it as `cs1`.
|
|
356
356
|
- `p1`/`p2` on the callback may be `cp1` keys, and the matching `sig`/`sig2`
|
|
357
|
-
come back as `cs1`. A hash output
|
|
357
|
+
come back as `cs1`. A hash output gets none: a plain note is unsigned by
|
|
358
|
+
design, since there is nothing to attest to without disclosing the secret.
|
|
358
359
|
- A merge may combine Part 1 secrets and Part 2 `ck1`s in one request.
|
|
359
360
|
|
|
360
361
|
A recipient checks a Part 2 note offline from its `ck1` and `cs1` alone.
|
package/dist/server.js
CHANGED
|
@@ -91,10 +91,13 @@ export const createMoneyer = async (config, deps = {}) => {
|
|
|
91
91
|
const store = deps.store ?? new NoteStore(config.dbPath);
|
|
92
92
|
const backend = deps.backend ?? backendFor(config);
|
|
93
93
|
const signer = createNoteSigner(config.signingKey);
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
// LUD-25 Part 2 signs cp1 notes only, as cs1. A hash output is a plain
|
|
95
|
+
// Part 1 note: there is nothing to attest to without disclosing the
|
|
96
|
+
// secret, so it goes out unsigned.
|
|
97
|
+
const certify = (ref, amountMsat) => ref.cp1 ? encodeCs1(hexToBytes(signer.sign(ref.id, amountMsat))) : undefined;
|
|
98
|
+
const certified = (field, ref, amountMsat) => {
|
|
99
|
+
const sig = certify(ref, amountMsat);
|
|
100
|
+
return sig ? { [field]: sig } : {};
|
|
98
101
|
};
|
|
99
102
|
const webAssets = deps.webAssets === undefined ? loadWebAssets() : deps.webAssets;
|
|
100
103
|
// Node identity for the discovery endpoint, fetched once, best-effort: a
|
|
@@ -928,7 +931,7 @@ export const createMoneyer = async (config, deps = {}) => {
|
|
|
928
931
|
...(signer ? { mintPubkey: signer.pubkey } : {}),
|
|
929
932
|
// LUD-25 Part 2: a note named by cp1 or ck1 gets its certificate
|
|
930
933
|
// here, so a wallet need not rotate just to obtain one.
|
|
931
|
-
...(
|
|
934
|
+
...certified('sig', ref, note.amountMsat)
|
|
932
935
|
});
|
|
933
936
|
}
|
|
934
937
|
// ---- the mutating callback: melt / rotate / split / merge ----
|
|
@@ -1009,12 +1012,8 @@ export const createMoneyer = async (config, deps = {}) => {
|
|
|
1009
1012
|
if (first) {
|
|
1010
1013
|
return send({
|
|
1011
1014
|
status: 'OK',
|
|
1012
|
-
...(
|
|
1013
|
-
|
|
1014
|
-
sig: certify(o1, first.amountMsat),
|
|
1015
|
-
...(second ? { sig2: certify(o2, second.amountMsat) } : {})
|
|
1016
|
-
}
|
|
1017
|
-
: {})
|
|
1015
|
+
...certified('sig', o1, first.amountMsat),
|
|
1016
|
+
...(second ? certified('sig2', o2, second.amountMsat) : {})
|
|
1018
1017
|
});
|
|
1019
1018
|
}
|
|
1020
1019
|
}
|
|
@@ -1148,7 +1147,8 @@ export const createMoneyer = async (config, deps = {}) => {
|
|
|
1148
1147
|
}
|
|
1149
1148
|
return send({
|
|
1150
1149
|
status: 'OK',
|
|
1151
|
-
...(
|
|
1150
|
+
...certified('sig', o1, amount),
|
|
1151
|
+
...certified('sig2', o2, changeMsat)
|
|
1152
1152
|
});
|
|
1153
1153
|
}
|
|
1154
1154
|
// rotate (one k1) or merge (several) - the same swap either way.
|
|
@@ -1165,7 +1165,7 @@ export const createMoneyer = async (config, deps = {}) => {
|
|
|
1165
1165
|
// same oracle-free reason for a collision as for a dead k1
|
|
1166
1166
|
return fail('Invalid or already spent k1.');
|
|
1167
1167
|
}
|
|
1168
|
-
return send({ status: 'OK', ...(
|
|
1168
|
+
return send({ status: 'OK', ...certified('sig', o1, mergedMsat) });
|
|
1169
1169
|
}
|
|
1170
1170
|
return fail('Not found.', 404);
|
|
1171
1171
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forgesworn/moneyer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "An LNURLcash (LUD-25) mint - strikes Lightning bearer notes. Independent implementation, cln/lnd funding sources, SQLite, zero HTTP framework.",
|
|
5
5
|
"author": "TheCryptoDonkey",
|
|
6
6
|
"license": "MIT",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"@noble/hashes": "^2.3.0",
|
|
61
61
|
"@scure/base": "^1.2.4",
|
|
62
62
|
"farrier-kit": "^1.1.3",
|
|
63
|
-
"lnurlcash-kit": "^0.
|
|
63
|
+
"lnurlcash-kit": "^0.13.0",
|
|
64
64
|
"nostr-tools": "2.24.1"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"@types/node": "^24.0.0",
|
|
71
71
|
"animejs": "^4.0.0",
|
|
72
72
|
"happy-dom": "^20.0.0",
|
|
73
|
-
"lnurlcash-conformance": "^0.
|
|
73
|
+
"lnurlcash-conformance": "^0.10.0",
|
|
74
74
|
"playwright": "^1.62.1",
|
|
75
75
|
"typescript": "^5.7.0",
|
|
76
76
|
"uqr": "^0.1.2",
|