thunder-bridge 1.5.0 → 2.2.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/dist/nwc.js CHANGED
@@ -172,6 +172,7 @@ function rotate(value, by) {
172
172
 
173
173
  // ../core/bolt11.ts
174
174
  var BECH32_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
175
+ var BECH32_GENERATOR = [996825010, 642813549, 513874426, 1027748829, 705979059];
175
176
  var CHECKSUM_WORDS = 6;
176
177
  var TIMESTAMP_WORDS = 7;
177
178
  var SIGNATURE_WORDS = 104;
@@ -189,18 +190,27 @@ var HRP_MULTIPLIER_MSAT = {
189
190
  n: MSAT_PER_BTC / 1e9,
190
191
  p: MSAT_PER_BTC / 1e12
191
192
  };
192
- var HRP_AMOUNT = /^ln[a-z]+?(\d+)([munp]?)$/;
193
+ var HRP = /^ln(?:bcrt|bc|tbs|tb|sb)(?:(\d+)([munp]?))?$/;
193
194
  function decodeInvoice(bolt11) {
194
195
  const parts = splitBech32(bolt11);
195
196
  if (parts === null) {
196
- return { paymentHash: null, descriptionHash: null, amountMsat: null, expiresAt: null };
197
+ return {
198
+ paymentHash: null,
199
+ descriptionHash: null,
200
+ amountMsat: null,
201
+ issuedAt: null,
202
+ expiresAt: null
203
+ };
197
204
  }
198
205
  const tagged = taggedFields(parts.words);
206
+ const issuedAt = issuedAtOf(parts.words);
207
+ const expiry = tagged.get(EXPIRY_TAG);
199
208
  return {
200
209
  paymentHash: hexTag(tagged, PAYMENT_HASH_TAG, PAYMENT_HASH_WORDS),
201
210
  descriptionHash: hexTag(tagged, DESCRIPTION_HASH_TAG, DESCRIPTION_HASH_WORDS),
202
211
  amountMsat: amountFromHrp(parts.hrp),
203
- expiresAt: expiryOf(parts.words, tagged)
212
+ issuedAt,
213
+ expiresAt: issuedAt === null ? null : issuedAt + (expiry ? readNumber(expiry) : DEFAULT_EXPIRY_SECS)
204
214
  };
205
215
  }
206
216
  function preimageMatchesHash(preimage, paymentHash) {
@@ -211,7 +221,7 @@ function preimageMatchesHash(preimage, paymentHash) {
211
221
  }
212
222
  function splitBech32(bolt11) {
213
223
  const lower = bolt11.toLowerCase();
214
- if (isBolt12Encoding(lower)) {
224
+ if (isBolt12Encoding(lower) || bolt11 !== lower && bolt11 !== bolt11.toUpperCase()) {
215
225
  return null;
216
226
  }
217
227
  const separator = lower.lastIndexOf("1");
@@ -230,7 +240,28 @@ function splitBech32(bolt11) {
230
240
  }
231
241
  words.push(value);
232
242
  }
233
- return { hrp: lower.slice(0, separator), words: words.slice(0, -CHECKSUM_WORDS) };
243
+ const hrp = lower.slice(0, separator);
244
+ if (!HRP.test(hrp) || bech32Polymod([...expandedHrp(hrp), ...words]) !== 1) {
245
+ return null;
246
+ }
247
+ return { hrp, words: words.slice(0, -CHECKSUM_WORDS) };
248
+ }
249
+ function expandedHrp(hrp) {
250
+ const codes = [...hrp].map((char) => char.charCodeAt(0));
251
+ return [...codes.map((code) => code >> 5), 0, ...codes.map((code) => code & 31)];
252
+ }
253
+ function bech32Polymod(values) {
254
+ let checksum = 1;
255
+ for (const value of values) {
256
+ const top = checksum >> 25;
257
+ checksum = (checksum & 33554431) << 5 ^ value;
258
+ for (let bit = 0; bit < 5; bit++) {
259
+ if (top >> bit & 1) {
260
+ checksum ^= BECH32_GENERATOR[bit];
261
+ }
262
+ }
263
+ }
264
+ return checksum;
234
265
  }
235
266
  function isBolt12Encoding(lower) {
236
267
  return lower.startsWith("lno") || lower.startsWith("lni") || lower.startsWith("lnr");
@@ -260,19 +291,17 @@ function hexTag(fields, tag, expectedWords) {
260
291
  }
261
292
  return bytesToHex(wordsToBytes(words));
262
293
  }
263
- function expiryOf(words, fields) {
294
+ function issuedAtOf(words) {
264
295
  if (words.length < TIMESTAMP_WORDS + SIGNATURE_WORDS) {
265
296
  return null;
266
297
  }
267
- const expiry = fields.get(EXPIRY_TAG);
268
- return readNumber(words.slice(0, TIMESTAMP_WORDS)) + (expiry ? readNumber(expiry) : DEFAULT_EXPIRY_SECS);
298
+ return readNumber(words.slice(0, TIMESTAMP_WORDS));
269
299
  }
270
300
  function amountFromHrp(hrp) {
271
- const match = HRP_AMOUNT.exec(hrp);
272
- if (match === null) {
301
+ const [, digits, multiplier] = HRP.exec(hrp) ?? [];
302
+ if (digits === void 0 || multiplier === "p" && !digits.endsWith("0")) {
273
303
  return null;
274
304
  }
275
- const [, digits, multiplier] = match;
276
305
  return multiplier ? Number(digits) * HRP_MULTIPLIER_MSAT[multiplier] : Number(digits) * MSAT_PER_BTC;
277
306
  }
278
307
  function readNumber(words) {
@@ -302,49 +331,138 @@ var WalletRefused = class extends Error {
302
331
  }
303
332
  };
304
333
 
334
+ // ../core/hmac.ts
335
+ async function hmacHex(secret, payload) {
336
+ const raw = typeof secret === "string" ? new TextEncoder().encode(secret) : secret;
337
+ const key = await crypto.subtle.importKey(
338
+ "raw",
339
+ asBuffer(raw),
340
+ { name: "HMAC", hash: "SHA-256" },
341
+ false,
342
+ ["sign"]
343
+ );
344
+ const message = typeof payload === "string" ? new TextEncoder().encode(payload) : payload;
345
+ return bytesToHex(new Uint8Array(await crypto.subtle.sign("HMAC", key, asBuffer(message))));
346
+ }
347
+ function asBuffer(bytes) {
348
+ return bytes instanceof Uint8Array && bytes.buffer instanceof ArrayBuffer ? bytes : new Uint8Array(bytes);
349
+ }
350
+
305
351
  // ../core/sealed.ts
306
- var VERSION = "v1";
352
+ var VERSION = "v2";
353
+ var UNPADDED_VERSION = "v1";
354
+ var PURPOSE_VERSION = "v3";
307
355
  var IV_BYTES = 12;
308
356
  var MIN_SECRET_CHARS = 32;
309
357
  var MAX_PLAIN_BYTES = 3e3;
310
- var INFO = new TextEncoder().encode("thunder-bridge/sealed");
311
- async function seal(secret, plaintext) {
312
- const body = new TextEncoder().encode(plaintext);
313
- if (body.length > MAX_PLAIN_BYTES) {
314
- throw new Error(`sealed takes at most ${MAX_PLAIN_BYTES} bytes, this was ${body.length}`);
358
+ var SIZE_CLASSES = [256, 1024, MAX_PLAIN_BYTES];
359
+ var END_OF_TEXT = 128;
360
+ var INFO = "thunder-bridge/sealed";
361
+ async function seal(secret, plaintext, paymentHash) {
362
+ return await sealUnder(
363
+ await keyFor(secret, INFO),
364
+ plaintext,
365
+ crypto.getRandomValues(new Uint8Array(IV_BYTES)),
366
+ VERSION,
367
+ boundTo(paymentHash)
368
+ );
369
+ }
370
+ async function sealFor(purpose, secret, plaintext, { stable = false } = {}) {
371
+ return await sealUnder(
372
+ await keyFor(secret, `${INFO}/${purpose}`),
373
+ plaintext,
374
+ stable ? await nonceOf(secret, plaintext) : crypto.getRandomValues(new Uint8Array(IV_BYTES)),
375
+ PURPOSE_VERSION,
376
+ new TextEncoder().encode(PURPOSE_VERSION)
377
+ );
378
+ }
379
+ async function unsealFor(purpose, secret, sealed) {
380
+ if (!sealed.startsWith(`${PURPOSE_VERSION}.`)) {
381
+ return await unseal(secret, sealed);
315
382
  }
316
- const key = await keyFor(secret);
317
- const iv = crypto.getRandomValues(new Uint8Array(IV_BYTES));
318
- const cipher = new Uint8Array(await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, body));
383
+ return await opened(
384
+ await keyFor(secret, `${INFO}/${purpose}`),
385
+ sealed.slice(PURPOSE_VERSION.length + 1),
386
+ new TextEncoder().encode(PURPOSE_VERSION)
387
+ );
388
+ }
389
+ async function nonceOf(secret, plaintext) {
390
+ const derived = await hmacHex(secret, `sealed-nonce|${plaintext}`);
391
+ const nonce = new Uint8Array(IV_BYTES);
392
+ for (let at = 0; at < IV_BYTES; at++) {
393
+ nonce[at] = Number.parseInt(derived.slice(at * 2, at * 2 + 2), 16);
394
+ }
395
+ return nonce;
396
+ }
397
+ async function sealUnder(key, plaintext, iv, version, additionalData) {
398
+ const body = padToClass(plaintext);
399
+ const cipher = new Uint8Array(
400
+ await crypto.subtle.encrypt({ name: "AES-GCM", iv, additionalData }, key, body)
401
+ );
319
402
  const joined2 = new Uint8Array(iv.length + cipher.length);
320
403
  joined2.set(iv);
321
404
  joined2.set(cipher, iv.length);
322
- return `${VERSION}.${toBase64Url(joined2)}`;
405
+ return `${version}.${toBase64Url(joined2)}`;
323
406
  }
324
- async function unseal(secret, sealed) {
325
- const key = await keyFor(secret);
326
- if (!sealed.startsWith(`${VERSION}.`)) {
407
+ async function unseal(secret, sealed, paymentHash) {
408
+ const key = await keyFor(secret, INFO);
409
+ const padded2 = sealed.startsWith(`${VERSION}.`);
410
+ const unbound = !padded2 && sealed.startsWith(`${UNPADDED_VERSION}.`);
411
+ if (!padded2 && (!unbound || paymentHash !== void 0)) {
327
412
  return null;
328
413
  }
329
- const joined2 = fromBase64Url(sealed.slice(VERSION.length + 1));
414
+ return await opened(
415
+ key,
416
+ sealed.slice(VERSION.length + 1),
417
+ padded2 ? boundTo(paymentHash) : void 0
418
+ );
419
+ }
420
+ async function opened(key, encoded, additionalData) {
421
+ const joined2 = fromBase64Url(encoded);
330
422
  if (joined2 === null || joined2.length <= IV_BYTES) {
331
423
  return null;
332
424
  }
333
425
  try {
334
- const body = await crypto.subtle.decrypt(
335
- { name: "AES-GCM", iv: joined2.slice(0, IV_BYTES) },
336
- key,
337
- joined2.slice(IV_BYTES)
426
+ const body = new Uint8Array(
427
+ await crypto.subtle.decrypt(
428
+ { name: "AES-GCM", iv: joined2.slice(0, IV_BYTES), additionalData },
429
+ key,
430
+ joined2.slice(IV_BYTES)
431
+ )
338
432
  );
339
- return new TextDecoder().decode(body);
433
+ const text = additionalData === void 0 ? body : unpad(body);
434
+ return text === null ? null : new TextDecoder().decode(text);
340
435
  } catch {
341
436
  return null;
342
437
  }
343
438
  }
344
- async function keyFor(secret) {
439
+ function refuseAWeakSecret(secret) {
345
440
  if (secret.length < MIN_SECRET_CHARS) {
346
441
  throw new Error(`the sealing secret needs ${MIN_SECRET_CHARS} characters of randomness`);
347
442
  }
443
+ }
444
+ function boundTo(paymentHash) {
445
+ return new TextEncoder().encode(
446
+ paymentHash === void 0 ? VERSION : `${VERSION}|${paymentHash.toLowerCase()}`
447
+ );
448
+ }
449
+ function padToClass(plaintext) {
450
+ const text = new TextEncoder().encode(plaintext);
451
+ const size = SIZE_CLASSES.find((limit) => text.length <= limit);
452
+ if (size === void 0) {
453
+ throw new Error(`sealed takes at most ${MAX_PLAIN_BYTES} bytes, this was ${text.length}`);
454
+ }
455
+ const body = new Uint8Array(size + 1);
456
+ body.set(text);
457
+ body[text.length] = END_OF_TEXT;
458
+ return body;
459
+ }
460
+ function unpad(body) {
461
+ const end = body.lastIndexOf(END_OF_TEXT);
462
+ return end === -1 ? null : body.subarray(0, end);
463
+ }
464
+ async function keyFor(secret, info) {
465
+ refuseAWeakSecret(secret);
348
466
  const material = await crypto.subtle.importKey(
349
467
  "raw",
350
468
  new TextEncoder().encode(secret),
@@ -353,7 +471,12 @@ async function keyFor(secret) {
353
471
  ["deriveKey"]
354
472
  );
355
473
  return crypto.subtle.deriveKey(
356
- { name: "HKDF", hash: "SHA-256", salt: new Uint8Array(0), info: INFO },
474
+ {
475
+ name: "HKDF",
476
+ hash: "SHA-256",
477
+ salt: new Uint8Array(0),
478
+ info: new TextEncoder().encode(info)
479
+ },
357
480
  material,
358
481
  { name: "AES-GCM", length: 256 },
359
482
  false,
@@ -762,6 +885,15 @@ async function amountNow(amount) {
762
885
 
763
886
  // ../core/ed25519.ts
764
887
  var PKCS8_HEADER = hexToBytes("302e020100300506032b657004220420");
888
+ var SMALL_ORDER = [
889
+ "00".repeat(32),
890
+ `01${"00".repeat(31)}`,
891
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05",
892
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a",
893
+ `ec${"ff".repeat(30)}7f`,
894
+ `ed${"ff".repeat(30)}7f`,
895
+ `ee${"ff".repeat(30)}7f`
896
+ ];
765
897
 
766
898
  // src/webhook.ts
767
899
  var VERIFY_CHALLENGE = "verify-challenge";
@@ -769,7 +901,7 @@ async function answerVerifyChallenge(request) {
769
901
  if (request.method !== "POST") {
770
902
  return null;
771
903
  }
772
- const nonce = nonceOf(await request.clone().text(), VERIFY_CHALLENGE);
904
+ const nonce = nonceOf2(await request.clone().text(), VERIFY_CHALLENGE);
773
905
  return nonce === null ? null : answered(nonce);
774
906
  }
775
907
  function answered(nonce) {
@@ -777,7 +909,7 @@ function answered(nonce) {
777
909
  headers: { "content-type": "application/json" }
778
910
  });
779
911
  }
780
- function nonceOf(text, type) {
912
+ function nonceOf2(text, type) {
781
913
  try {
782
914
  const said = JSON.parse(text);
783
915
  if (said["type"] !== type || typeof said["nonce"] !== "string") {
@@ -923,7 +1055,7 @@ function nwcVerifyEndpoint(config) {
923
1055
  if (sealed === null) {
924
1056
  return Response.json({ settled: false }, { status: 400 });
925
1057
  }
926
- const paymentHash = await unseal(config.secret, sealed);
1058
+ const paymentHash = await unsealFor("nwc-verify", config.secret, sealed);
927
1059
  if (paymentHash === null) {
928
1060
  return Response.json({ settled: false }, { status: 403 });
929
1061
  }
@@ -938,7 +1070,7 @@ function nwcVerifyEndpoint(config) {
938
1070
  }
939
1071
  async function nwcVerifyUrl(endpoint, paymentHash, secret) {
940
1072
  const sealed = new URL(endpoint);
941
- sealed.searchParams.set(HASH, await seal(secret, paymentHash));
1073
+ sealed.searchParams.set(HASH, await sealFor("nwc-verify", secret, paymentHash));
942
1074
  return sealed.toString();
943
1075
  }
944
1076
  async function askWallet(connection, method, params, timeoutMs = DEFAULT_ASK_TIMEOUT_MS) {
@@ -1092,11 +1224,16 @@ function nwcRail(gateway, config) {
1092
1224
  expiresAt: invoice.expiresAt,
1093
1225
  trigger: config.trigger,
1094
1226
  replay: config.replay,
1095
- sealed: await config.sealed?.(order),
1227
+ sealed: config.sealed ? await seal(
1228
+ config.sealed.secret,
1229
+ JSON.stringify(config.sealed.data(order)),
1230
+ invoice.paymentHash
1231
+ ) : void 0,
1096
1232
  webhookUrl: config.webhookUrl
1097
1233
  });
1098
1234
  return {
1099
1235
  id: watched.id,
1236
+ paymentHash: invoice.paymentHash,
1100
1237
  rail: config.name ?? "lightning",
1101
1238
  scan: invoice.bolt11,
1102
1239
  qr: toLightningUri(invoice.bolt11),
@@ -1104,14 +1241,18 @@ function nwcRail(gateway, config) {
1104
1241
  };
1105
1242
  };
1106
1243
  }
1244
+
1245
+ // src/entry/nwc.ts
1246
+ var nwcRail2 = nwcRail;
1247
+ var nwcVerifyEndpoint2 = nwcVerifyEndpoint;
1107
1248
  export {
1108
1249
  askWallet,
1109
1250
  nwcConnection,
1110
1251
  nwcHoldInvoice,
1111
1252
  nwcInvoice,
1112
1253
  nwcPay,
1113
- nwcRail,
1254
+ nwcRail2 as nwcRail,
1114
1255
  nwcSettlement,
1115
- nwcVerifyEndpoint,
1256
+ nwcVerifyEndpoint2 as nwcVerifyEndpoint,
1116
1257
  nwcVerifyUrl
1117
1258
  };
package/dist/price.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- export { A as AmountError, a as AmountFault } from './errors-Dmh-Uoh8.cjs';
2
- export { M as MedianOptions, T as Ticker, b as bitstamp, c as coinbase, a as coinmate, k as kraken, m as medianOf, d as msatFor } from './types-BNPmVnA7.cjs';
1
+ export { A as AmountError, a as AmountFault } from './errors-DJmsalYZ.cjs';
2
+ export { M as MedianOptions, T as Ticker, b as bitstamp, c as coinbase, a as coinmate, k as kraken, m as medianOf, d as msatFor } from './types-DYZ9EkmJ.cjs';
3
3
 
4
4
  /**
5
5
  * How many digits ISO 4217 gives the currency's minor unit, so 2 for a crown and a
package/dist/price.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export { A as AmountError, a as AmountFault } from './errors-0vbVoISA.js';
2
- export { M as MedianOptions, T as Ticker, b as bitstamp, c as coinbase, a as coinmate, k as kraken, m as medianOf, d as msatFor } from './types-BNPmVnA7.js';
1
+ export { A as AmountError, a as AmountFault } from './errors-VX0R18oE.js';
2
+ export { M as MedianOptions, T as Ticker, b as bitstamp, c as coinbase, a as coinmate, k as kraken, m as medianOf, d as msatFor } from './types-DYZ9EkmJ.js';
3
3
 
4
4
  /**
5
5
  * How many digits ISO 4217 gives the currency's minor unit, so 2 for a crown and a
package/dist/qr.cjs CHANGED
@@ -111,6 +111,8 @@ var INITIAL_STATE = new Uint32Array([
111
111
  ]);
112
112
 
113
113
  // ../core/bolt11.ts
114
+ var BECH32_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
115
+ var BECH32_GENERATOR = [996825010, 642813549, 513874426, 1027748829, 705979059];
114
116
  var MSAT_PER_BTC = 1e11;
115
117
  var HRP_MULTIPLIER_MSAT = {
116
118
  m: MSAT_PER_BTC / 1e3,
@@ -118,10 +120,25 @@ var HRP_MULTIPLIER_MSAT = {
118
120
  n: MSAT_PER_BTC / 1e9,
119
121
  p: MSAT_PER_BTC / 1e12
120
122
  };
123
+ function expandedHrp(hrp) {
124
+ const codes = [...hrp].map((char) => char.charCodeAt(0));
125
+ return [...codes.map((code) => code >> 5), 0, ...codes.map((code) => code & 31)];
126
+ }
127
+ function bech32Polymod(values) {
128
+ let checksum = 1;
129
+ for (const value of values) {
130
+ const top = checksum >> 25;
131
+ checksum = (checksum & 33554431) << 5 ^ value;
132
+ for (let bit = 0; bit < 5; bit++) {
133
+ if (top >> bit & 1) {
134
+ checksum ^= BECH32_GENERATOR[bit];
135
+ }
136
+ }
137
+ }
138
+ return checksum;
139
+ }
121
140
 
122
141
  // ../core/lnurl.ts
123
- var BECH32_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
124
- var BECH32_GENERATOR = [996825010, 642813549, 513874426, 1027748829, 705979059];
125
142
  var CHECKSUM_SLOTS = [0, 1, 2, 3, 4, 5];
126
143
  var LNURL_HRP = "lnurl";
127
144
  function toLnurl(endpoint) {
@@ -158,30 +175,10 @@ function toWords(bytes) {
158
175
  return words;
159
176
  }
160
177
  function checksumWords(hrp, words) {
161
- const expanded = [...hrp].map((char) => char.charCodeAt(0));
162
- const values = [
163
- ...expanded.map((code) => code >> 5),
164
- 0,
165
- ...expanded.map((code) => code & 31),
166
- ...words,
167
- ...CHECKSUM_SLOTS.map(() => 0)
168
- ];
178
+ const values = [...expandedHrp(hrp), ...words, ...CHECKSUM_SLOTS.map(() => 0)];
169
179
  const polymod = bech32Polymod(values) ^ 1;
170
180
  return CHECKSUM_SLOTS.map((slot) => polymod >> 5 * (5 - slot) & 31);
171
181
  }
172
- function bech32Polymod(values) {
173
- let checksum = 1;
174
- for (const value of values) {
175
- const top = checksum >> 25;
176
- checksum = (checksum & 33554431) << 5 ^ value;
177
- for (let bit = 0; bit < 5; bit++) {
178
- if (top >> bit & 1) {
179
- checksum ^= BECH32_GENERATOR[bit];
180
- }
181
- }
182
- }
183
- return checksum;
184
- }
185
182
 
186
183
  // src/qr.ts
187
184
  var import_uqr = require("uqr");
package/dist/qr.js CHANGED
@@ -77,6 +77,8 @@ var INITIAL_STATE = new Uint32Array([
77
77
  ]);
78
78
 
79
79
  // ../core/bolt11.ts
80
+ var BECH32_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
81
+ var BECH32_GENERATOR = [996825010, 642813549, 513874426, 1027748829, 705979059];
80
82
  var MSAT_PER_BTC = 1e11;
81
83
  var HRP_MULTIPLIER_MSAT = {
82
84
  m: MSAT_PER_BTC / 1e3,
@@ -84,10 +86,25 @@ var HRP_MULTIPLIER_MSAT = {
84
86
  n: MSAT_PER_BTC / 1e9,
85
87
  p: MSAT_PER_BTC / 1e12
86
88
  };
89
+ function expandedHrp(hrp) {
90
+ const codes = [...hrp].map((char) => char.charCodeAt(0));
91
+ return [...codes.map((code) => code >> 5), 0, ...codes.map((code) => code & 31)];
92
+ }
93
+ function bech32Polymod(values) {
94
+ let checksum = 1;
95
+ for (const value of values) {
96
+ const top = checksum >> 25;
97
+ checksum = (checksum & 33554431) << 5 ^ value;
98
+ for (let bit = 0; bit < 5; bit++) {
99
+ if (top >> bit & 1) {
100
+ checksum ^= BECH32_GENERATOR[bit];
101
+ }
102
+ }
103
+ }
104
+ return checksum;
105
+ }
87
106
 
88
107
  // ../core/lnurl.ts
89
- var BECH32_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
90
- var BECH32_GENERATOR = [996825010, 642813549, 513874426, 1027748829, 705979059];
91
108
  var CHECKSUM_SLOTS = [0, 1, 2, 3, 4, 5];
92
109
  var LNURL_HRP = "lnurl";
93
110
  function toLnurl(endpoint) {
@@ -124,30 +141,10 @@ function toWords(bytes) {
124
141
  return words;
125
142
  }
126
143
  function checksumWords(hrp, words) {
127
- const expanded = [...hrp].map((char) => char.charCodeAt(0));
128
- const values = [
129
- ...expanded.map((code) => code >> 5),
130
- 0,
131
- ...expanded.map((code) => code & 31),
132
- ...words,
133
- ...CHECKSUM_SLOTS.map(() => 0)
134
- ];
144
+ const values = [...expandedHrp(hrp), ...words, ...CHECKSUM_SLOTS.map(() => 0)];
135
145
  const polymod = bech32Polymod(values) ^ 1;
136
146
  return CHECKSUM_SLOTS.map((slot) => polymod >> 5 * (5 - slot) & 31);
137
147
  }
138
- function bech32Polymod(values) {
139
- let checksum = 1;
140
- for (const value of values) {
141
- const top = checksum >> 25;
142
- checksum = (checksum & 33554431) << 5 ^ value;
143
- for (let bit = 0; bit < 5; bit++) {
144
- if (top >> bit & 1) {
145
- checksum ^= BECH32_GENERATOR[bit];
146
- }
147
- }
148
- }
149
- return checksum;
150
- }
151
148
 
152
149
  // src/qr.ts
153
150
  import { encode } from "uqr";
@@ -166,6 +166,15 @@ interface WatchedPayment extends Reported {
166
166
  * needs an assertion to read
167
167
  */
168
168
  type Payment = MintedPayment | WatchedPayment;
169
+ /**
170
+ * A payment as the caller already knows it: what the gateway calls it and the
171
+ * hash it settles against. Every `Payment` is one, and a caller reading back
172
+ * after a restart builds one from the two fields it stored
173
+ */
174
+ interface Held {
175
+ id: string;
176
+ paymentHash: string;
177
+ }
169
178
  /**
170
179
  * Who is paid and how much. `to` is a priority list when it is an array: the
171
180
  * gateway takes the first address that can issue a provable invoice for the
@@ -203,7 +212,7 @@ interface Handover {
203
212
  * Needs `trigger`, defaults to none
204
213
  */
205
214
  replay?: number;
206
- /** Sealed with `seal`, so the gateway stores what it cannot read */
215
+ /** Sealed with `seal` for this payment hash, so the gateway stores what it cannot read or move */
207
216
  sealed?: string;
208
217
  webhookUrl?: string;
209
218
  }
@@ -249,4 +258,4 @@ interface Settlement {
249
258
  settledAt: number;
250
259
  }
251
260
 
252
- export { type Amount as A, type Charge as C, type FiatOptions as F, type Handover as H, type MedianOptions as M, type Payment as P, type Quote as Q, type Settlement as S, type Ticker as T, type WalletFailure as W, coinmate as a, bitstamp as b, coinbase as c, msatFor as d, type MintedPayment as e, type Msat as f, type PaymentStatus as g, type Priced as h, type SocketTicket as i, type WalletReason as j, kraken as k, type WatchedPayment as l, medianOf as m, fiat as n, msat as o, sats as s };
261
+ export { type Amount as A, type Charge as C, type FiatOptions as F, type Handover as H, type MedianOptions as M, type Payment as P, type Quote as Q, type Settlement as S, type Ticker as T, type WalletFailure as W, coinmate as a, bitstamp as b, coinbase as c, msatFor as d, type Held as e, type MintedPayment as f, type Msat as g, type PaymentStatus as h, type Priced as i, type SocketTicket as j, kraken as k, type WalletReason as l, medianOf as m, type WatchedPayment as n, fiat as o, msat as p, sats as s };
@@ -166,6 +166,15 @@ interface WatchedPayment extends Reported {
166
166
  * needs an assertion to read
167
167
  */
168
168
  type Payment = MintedPayment | WatchedPayment;
169
+ /**
170
+ * A payment as the caller already knows it: what the gateway calls it and the
171
+ * hash it settles against. Every `Payment` is one, and a caller reading back
172
+ * after a restart builds one from the two fields it stored
173
+ */
174
+ interface Held {
175
+ id: string;
176
+ paymentHash: string;
177
+ }
169
178
  /**
170
179
  * Who is paid and how much. `to` is a priority list when it is an array: the
171
180
  * gateway takes the first address that can issue a provable invoice for the
@@ -203,7 +212,7 @@ interface Handover {
203
212
  * Needs `trigger`, defaults to none
204
213
  */
205
214
  replay?: number;
206
- /** Sealed with `seal`, so the gateway stores what it cannot read */
215
+ /** Sealed with `seal` for this payment hash, so the gateway stores what it cannot read or move */
207
216
  sealed?: string;
208
217
  webhookUrl?: string;
209
218
  }
@@ -249,4 +258,4 @@ interface Settlement {
249
258
  settledAt: number;
250
259
  }
251
260
 
252
- export { type Amount as A, type Charge as C, type FiatOptions as F, type Handover as H, type MedianOptions as M, type Payment as P, type Quote as Q, type Settlement as S, type Ticker as T, type WalletFailure as W, coinmate as a, bitstamp as b, coinbase as c, msatFor as d, type MintedPayment as e, type Msat as f, type PaymentStatus as g, type Priced as h, type SocketTicket as i, type WalletReason as j, kraken as k, type WatchedPayment as l, medianOf as m, fiat as n, msat as o, sats as s };
261
+ export { type Amount as A, type Charge as C, type FiatOptions as F, type Handover as H, type MedianOptions as M, type Payment as P, type Quote as Q, type Settlement as S, type Ticker as T, type WalletFailure as W, coinmate as a, bitstamp as b, coinbase as c, msatFor as d, type Held as e, type MintedPayment as f, type Msat as g, type PaymentStatus as h, type Priced as i, type SocketTicket as j, kraken as k, type WalletReason as l, medianOf as m, type WatchedPayment as n, fiat as o, msat as p, sats as s };