thunder-bridge 2.2.1 → 3.0.1

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/index.cjs CHANGED
@@ -41,7 +41,6 @@ __export(src_exports, {
41
41
  WrapRefusedError: () => WrapRefusedError,
42
42
  agreesWithItself: () => agreesWithItself,
43
43
  answerVerifyChallenge: () => answerVerifyChallenge,
44
- carriesProof: () => carriesProof,
45
44
  decodeInvoice: () => decodeInvoice,
46
45
  fiat: () => fiat,
47
46
  invoiceFrom: () => invoiceFrom,
@@ -185,9 +184,9 @@ function fillSchedule(schedule, reader, block) {
185
184
  schedule[at] = reader.getUint32(block + at * 4, false);
186
185
  }
187
186
  for (let at = 16; at < SCHEDULE_WORDS; at++) {
188
- const recent2 = schedule[at - 15];
187
+ const recent = schedule[at - 15];
189
188
  const distant = schedule[at - 2];
190
- const mixedRecent = rotate(recent2, 7) ^ rotate(recent2, 18) ^ recent2 >>> 3;
189
+ const mixedRecent = rotate(recent, 7) ^ rotate(recent, 18) ^ recent >>> 3;
191
190
  const mixedDistant = rotate(distant, 17) ^ rotate(distant, 19) ^ distant >>> 10;
192
191
  schedule[at] = schedule[at - 16] + mixedRecent + schedule[at - 7] + mixedDistant | 0;
193
192
  }
@@ -442,7 +441,7 @@ async function sealFor(purpose, secret, plaintext, { stable = false } = {}) {
442
441
  }
443
442
  async function unsealFor(purpose, secret, sealed) {
444
443
  if (!sealed.startsWith(`${PURPOSE_VERSION}.`)) {
445
- return await unseal(secret, sealed);
444
+ return null;
446
445
  }
447
446
  return await opened(
448
447
  await keyFor(secret, `${INFO}/${purpose}`),
@@ -985,13 +984,182 @@ function asBuffer2(bytes) {
985
984
  return bytes.buffer instanceof ArrayBuffer ? bytes : new Uint8Array(bytes);
986
985
  }
987
986
 
988
- // ../core/caller.ts
989
- var KEY_HEADER = "x-client-key";
990
- var SIGNATURE_HEADER = "x-signature";
991
- var TIMESTAMP_HEADER = "x-timestamp";
992
- var NONCE_HEADER = "x-nonce";
993
- var PREFIX = "ed25519=";
987
+ // ../core/signature.ts
988
+ var import_structured_headers = require("structured-headers");
989
+ var TOLERANCE_SECS = 300;
990
+ var LABEL = "thunder-bridge";
991
+ var ALGORITHM2 = "ed25519";
994
992
  var NONCE_BYTES = 16;
993
+ var SIGNATURE_BYTES2 = 64;
994
+ var PUBLIC_KEY = /^[0-9a-f]{64}$/i;
995
+ var CLIENT_SIGNS = {
996
+ tag: "thunder-bridge-client",
997
+ covers: ["@method", "@authority", "@path", "@query", "content-digest"]
998
+ };
999
+ var GATEWAY_SIGNS = {
1000
+ tag: "thunder-bridge-gateway",
1001
+ covers: ["@method", "@scheme", "@authority", "@path", "content-digest"]
1002
+ };
1003
+ function contentDigest(body) {
1004
+ return (0, import_structured_headers.serializeDictionary)({
1005
+ "sha-256": [Uint8Array.from(sha256(new TextEncoder().encode(body))), /* @__PURE__ */ new Map()]
1006
+ });
1007
+ }
1008
+ function signatureBase(message, covered) {
1009
+ const lines = [];
1010
+ const seen = /* @__PURE__ */ new Set();
1011
+ for (const [name, parameters] of covered[0]) {
1012
+ if (typeof name !== "string" || parameters.size > 0 || seen.has(name)) {
1013
+ return null;
1014
+ }
1015
+ seen.add(name);
1016
+ const value = componentValue(message, name);
1017
+ if (value === null) {
1018
+ return null;
1019
+ }
1020
+ lines.push(`${(0, import_structured_headers.serializeString)(name)}: ${value}`);
1021
+ }
1022
+ lines.push(`"@signature-params": ${(0, import_structured_headers.serializeInnerList)(covered)}`);
1023
+ return lines.join("\n");
1024
+ }
1025
+ async function signedBy(key, profile, message, body, { created = unixNow(), nonce = randomNonce() } = {}) {
1026
+ const digest = contentDigest(body);
1027
+ const covered = [
1028
+ profile.covers.map((name) => [name, /* @__PURE__ */ new Map()]),
1029
+ /* @__PURE__ */ new Map([
1030
+ ["created", created],
1031
+ ["nonce", nonce],
1032
+ ["keyid", key.publicKeyHex],
1033
+ ["alg", ALGORITHM2],
1034
+ ["tag", profile.tag]
1035
+ ])
1036
+ ];
1037
+ const base = signatureBase(
1038
+ { ...message, headers: new Headers({ "content-digest": digest }) },
1039
+ covered
1040
+ );
1041
+ if (base === null) {
1042
+ throw new Error(`${message.url} cannot be signed as ${profile.tag}`);
1043
+ }
1044
+ const signature = Uint8Array.from(hexToBytes(await key.sign(new TextEncoder().encode(base))));
1045
+ return {
1046
+ "content-digest": digest,
1047
+ "signature-input": (0, import_structured_headers.serializeDictionary)(/* @__PURE__ */ new Map([[LABEL, covered]])),
1048
+ signature: (0, import_structured_headers.serializeDictionary)(/* @__PURE__ */ new Map([[LABEL, [signature, /* @__PURE__ */ new Map()]]]))
1049
+ };
1050
+ }
1051
+ async function signerOf(message, body, profile, toleranceSecs = TOLERANCE_SECS) {
1052
+ const found = chosen(message.headers, profile.tag);
1053
+ if (found === null) {
1054
+ return null;
1055
+ }
1056
+ const [components, parameters] = found.covered;
1057
+ const named = components.map(([name]) => name);
1058
+ if (!profile.covers.every((name) => named.includes(name))) {
1059
+ return null;
1060
+ }
1061
+ const keyid = parameters.get("keyid");
1062
+ const created = parameters.get("created");
1063
+ const nonce = parameters.get("nonce");
1064
+ const alg = parameters.get("alg");
1065
+ if (typeof keyid !== "string" || !PUBLIC_KEY.test(keyid)) {
1066
+ return null;
1067
+ }
1068
+ if (typeof created !== "number" || !Number.isInteger(created)) {
1069
+ return null;
1070
+ }
1071
+ if (Math.abs(unixNow() - created) > toleranceSecs) {
1072
+ return null;
1073
+ }
1074
+ if (alg !== void 0 && alg !== ALGORITHM2 || nonce !== void 0 && typeof nonce !== "string") {
1075
+ return null;
1076
+ }
1077
+ if (!digestMatches(message.headers.get("content-digest"), body)) {
1078
+ return null;
1079
+ }
1080
+ const base = signatureBase(message, found.covered);
1081
+ if (base === null) {
1082
+ return null;
1083
+ }
1084
+ const signer = keyid.toLowerCase();
1085
+ const proven = await verifyHex(
1086
+ signer,
1087
+ bytesToHex(found.signature),
1088
+ new TextEncoder().encode(base)
1089
+ );
1090
+ return proven ? { keyid: signer, created, nonce: nonce ?? null } : null;
1091
+ }
1092
+ function componentValue(message, name) {
1093
+ let url;
1094
+ try {
1095
+ url = new URL(message.url);
1096
+ } catch {
1097
+ return null;
1098
+ }
1099
+ switch (name) {
1100
+ case "@method":
1101
+ return message.method;
1102
+ case "@authority":
1103
+ return (message.authority ?? url.host).toLowerCase();
1104
+ case "@scheme":
1105
+ return url.protocol.slice(0, -1).toLowerCase();
1106
+ case "@path":
1107
+ return url.pathname === "" ? "/" : url.pathname;
1108
+ case "@query":
1109
+ return url.search === "" ? "?" : url.search;
1110
+ default:
1111
+ return name.startsWith("@") ? null : message.headers.get(name)?.trim() ?? null;
1112
+ }
1113
+ }
1114
+ function chosen(headers, tag) {
1115
+ const inputs = dictionaryOf(headers.get("signature-input"));
1116
+ const signatures = dictionaryOf(headers.get("signature"));
1117
+ if (inputs === null || signatures === null) {
1118
+ return null;
1119
+ }
1120
+ for (const [label, member] of inputs) {
1121
+ if (!isInnerList(member) || member[1].get("tag") !== tag) {
1122
+ continue;
1123
+ }
1124
+ const value = signatures.get(label);
1125
+ if (value === void 0 || isInnerList(value) || !(value[0] instanceof ArrayBuffer)) {
1126
+ return null;
1127
+ }
1128
+ const signature = new Uint8Array(value[0]);
1129
+ return signature.length === SIGNATURE_BYTES2 ? { covered: member, signature } : null;
1130
+ }
1131
+ return null;
1132
+ }
1133
+ function digestMatches(field, body) {
1134
+ const digest = dictionaryOf(field)?.get("sha-256");
1135
+ if (digest === void 0 || isInnerList(digest) || !(digest[0] instanceof ArrayBuffer)) {
1136
+ return false;
1137
+ }
1138
+ const sent = new Uint8Array(digest[0]);
1139
+ const counted = sha256(new TextEncoder().encode(body));
1140
+ return sent.length === counted.length && sent.every((byte, at) => byte === counted[at]);
1141
+ }
1142
+ function dictionaryOf(field) {
1143
+ if (field === null) {
1144
+ return null;
1145
+ }
1146
+ try {
1147
+ return (0, import_structured_headers.parseDictionary)(field);
1148
+ } catch {
1149
+ return null;
1150
+ }
1151
+ }
1152
+ function isInnerList(member) {
1153
+ return Array.isArray(member[0]);
1154
+ }
1155
+ function randomNonce() {
1156
+ return bytesToHex(crypto.getRandomValues(new Uint8Array(NONCE_BYTES)));
1157
+ }
1158
+ function unixNow() {
1159
+ return Math.floor(Date.now() / 1e3);
1160
+ }
1161
+
1162
+ // ../core/caller.ts
995
1163
  var SIGNING_LABEL = "request-signing";
996
1164
  async function callerKey(secret) {
997
1165
  return await signingKeyFromSeed(hexToBytes(await hmacHex(secret, SIGNING_LABEL)));
@@ -1000,20 +1168,11 @@ function paymentNamedBy(caller, paymentHash) {
1000
1168
  return sha256Hex(`${caller}.payment-id.${paymentHash}`);
1001
1169
  }
1002
1170
  async function signedAs(key, method, path, body, host) {
1003
- const timestamp = String(Math.floor(Date.now() / 1e3));
1004
- const nonce = bytesToHex(crypto.getRandomValues(new Uint8Array(NONCE_BYTES)));
1005
- return {
1006
- [KEY_HEADER]: key.publicKeyHex,
1007
- [TIMESTAMP_HEADER]: timestamp,
1008
- [NONCE_HEADER]: nonce,
1009
- [SIGNATURE_HEADER]: `${PREFIX}${await key.sign(spokenTo(host, timestamp, nonce, method, path, body))}`
1010
- };
1011
- }
1012
- function spokenTo(host, timestamp, nonce, method, path, body) {
1013
- return new TextEncoder().encode(
1014
- ["v2", host.toLowerCase(), timestamp, nonce, method.toUpperCase(), path, sha256Hex(body)].join(
1015
- "\n"
1016
- )
1171
+ return await signedBy(
1172
+ key,
1173
+ CLIENT_SIGNS,
1174
+ { method, url: `https://${host}${path}`, authority: host },
1175
+ body
1017
1176
  );
1018
1177
  }
1019
1178
 
@@ -1195,14 +1354,23 @@ async function addressesToReach(url) {
1195
1354
  return found;
1196
1355
  }
1197
1356
  async function resolved(url) {
1198
- const { isIP } = await import("net");
1357
+ const { isIP } = await import(
1358
+ /* webpackIgnore: true */
1359
+ "node:net"
1360
+ );
1199
1361
  const host = new URL(url).hostname.replace(/^\[|]$/g, "");
1200
1362
  const literal = isIP(host);
1201
1363
  if (literal !== 0) {
1202
1364
  return [{ address: host, family: literal }];
1203
1365
  }
1204
- const { Resolver } = await import("dns/promises");
1205
- const { setTimeout: sleep } = await import("timers/promises");
1366
+ const { Resolver } = await import(
1367
+ /* webpackIgnore: true */
1368
+ "node:dns/promises"
1369
+ );
1370
+ const { setTimeout: sleep } = await import(
1371
+ /* webpackIgnore: true */
1372
+ "node:timers/promises"
1373
+ );
1206
1374
  const resolver = new Resolver({ timeout: LOOKUP_TIMEOUT_MS, tries: 1 });
1207
1375
  const found = await Promise.race([
1208
1376
  Promise.all([
@@ -1289,6 +1457,7 @@ var NoWalletAvailable = class extends Error {
1289
1457
 
1290
1458
  // ../core/lnurl.ts
1291
1459
  var VERIFY_WITHOUT_PREIMAGE = ["zeuspay.com", "zeusnuts.com", "ecash.love"];
1460
+ var NAMES_AN_ADDRESS = ["text/identifier", "text/email"];
1292
1461
  var RESOLVE_TIMEOUT_MS = 3e4;
1293
1462
  var NAMES_ONE_ACCOUNT = /^[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+)*$/;
1294
1463
  var MIN_PACE_SECS = 1;
@@ -1350,6 +1519,12 @@ async function probe(send, address, amountMsat, deadline) {
1350
1519
  if (pay.tag !== "payRequest" || !pay.callback) {
1351
1520
  throw new WalletRefused("unreachable", `${address} answered with no payRequest`);
1352
1521
  }
1522
+ if (!namesTheAddress(pay.metadata, address)) {
1523
+ throw new WalletRefused(
1524
+ "cannot-prove-delivery",
1525
+ `${address} serves metadata that does not name it, so no invoice binds to it`
1526
+ );
1527
+ }
1353
1528
  if (amountMsat < pay.minSendable || amountMsat > pay.maxSendable) {
1354
1529
  throw new WalletRefused(
1355
1530
  "amount-not-accepted",
@@ -1442,6 +1617,19 @@ function ceilingAskedFor(headers) {
1442
1617
  }
1443
1618
  return Math.min(Math.max(perWindow / window, MIN_PER_SECOND), MAX_PER_SECOND);
1444
1619
  }
1620
+ function namesTheAddress(metadata, address) {
1621
+ if (typeof metadata !== "string") {
1622
+ return false;
1623
+ }
1624
+ try {
1625
+ const entries = JSON.parse(metadata);
1626
+ return Array.isArray(entries) && entries.some(
1627
+ (entry) => Array.isArray(entry) && NAMES_AN_ADDRESS.includes(entry[0]) && typeof entry[1] === "string" && entry[1].toLowerCase() === address.toLowerCase()
1628
+ );
1629
+ } catch {
1630
+ return false;
1631
+ }
1632
+ }
1445
1633
  function cannotReleaseAPreimage(host) {
1446
1634
  const lowered = host.toLowerCase();
1447
1635
  return VERIFY_WITHOUT_PREIMAGE.some(
@@ -1490,8 +1678,14 @@ var pinnedToTheAddressWeVerified = async (url, sent, signal, at) => {
1490
1678
  if (first === void 0) {
1491
1679
  throw new Error(`${url} resolved to nothing worth connecting to`);
1492
1680
  }
1493
- const { request } = await import("https");
1494
- const { Readable } = await import("stream");
1681
+ const { request } = await import(
1682
+ /* webpackIgnore: true */
1683
+ "node:https"
1684
+ );
1685
+ const { Readable } = await import(
1686
+ /* webpackIgnore: true */
1687
+ "node:stream"
1688
+ );
1495
1689
  return new Promise((settle, fail) => {
1496
1690
  if (signal.aborted) {
1497
1691
  fail(signal.reason);
@@ -1556,18 +1750,9 @@ function headersOf(raw) {
1556
1750
  return headers;
1557
1751
  }
1558
1752
 
1559
- // ../core/delivery.ts
1560
- function deliverySigned(url, timestamp, body) {
1561
- const endpoint = new URL(url);
1562
- return new TextEncoder().encode(
1563
- ["v2", `${endpoint.origin}${endpoint.pathname}`, timestamp, body].join("\n")
1564
- );
1565
- }
1566
-
1567
1753
  // src/verify.ts
1568
1754
  var HTTP_TIMEOUT_MS2 = 15e3;
1569
1755
  var ISSUED_SKEW_SECS = 300;
1570
- var NAMES_AN_ADDRESS = ["text/identifier", "text/email"];
1571
1756
  var DEFAULT_WRAP_PROPORTION = 0.01;
1572
1757
  var DEFAULT_WRAP_BASE_MSAT = 1e3;
1573
1758
  async function proveOrigin(payment, asked, askedAt) {
@@ -1614,16 +1799,6 @@ async function proveOrigin(payment, asked, askedAt) {
1614
1799
  throw cheat("invoice_settled");
1615
1800
  }
1616
1801
  }
1617
- function namesTheAddress(metadata, address) {
1618
- try {
1619
- const entries = JSON.parse(metadata);
1620
- return Array.isArray(entries) && entries.some(
1621
- (entry) => Array.isArray(entry) && NAMES_AN_ADDRESS.includes(entry[0]) && typeof entry[1] === "string" && equalIgnoringCase(entry[1], address)
1622
- );
1623
- } catch {
1624
- return false;
1625
- }
1626
- }
1627
1802
  async function proveSettlement(payment, asked) {
1628
1803
  await proveOrigin(payment, asked);
1629
1804
  const verified = await reachable(payment.verifyUrl, payment);
@@ -1647,7 +1822,6 @@ function agreesWithItself(report) {
1647
1822
  }
1648
1823
  return preimageMatchesHash(report.preimage, report.paymentHash);
1649
1824
  }
1650
- var carriesProof = agreesWithItself;
1651
1825
  function wrapFeeCeiling(amountMsat, allowance) {
1652
1826
  const proportional = amountMsat * (allowance?.proportion ?? DEFAULT_WRAP_PROPORTION);
1653
1827
  return msat(Math.max(allowance?.baseMsat ?? DEFAULT_WRAP_BASE_MSAT, Math.floor(proportional)));
@@ -1929,10 +2103,6 @@ function isStatus(value) {
1929
2103
  }
1930
2104
 
1931
2105
  // src/webhook.ts
1932
- var SIGNATURE_HEADER2 = "x-signature-v2";
1933
- var TIMESTAMP_HEADER2 = "x-timestamp";
1934
- var GATEWAY_KEY_PREFIX = "ed25519=";
1935
- var DEFAULT_TOLERANCE_SECS = 300;
1936
2106
  var CHALLENGE = "webhook-challenge";
1937
2107
  var VERIFY_CHALLENGE = "verify-challenge";
1938
2108
  async function readPayment(request, credential, options = {}) {
@@ -1956,24 +2126,14 @@ async function answerVerifyChallenge(request) {
1956
2126
  return nonce === null ? null : answered2(nonce);
1957
2127
  }
1958
2128
  async function believable(request, credential, options) {
1959
- const signature = request.headers.get(SIGNATURE_HEADER2);
1960
- const timestamp = request.headers.get(TIMESTAMP_HEADER2);
1961
- if (signature === null || timestamp === null) {
1962
- return null;
1963
- }
1964
- if (!recent(timestamp, options.toleranceSecs ?? DEFAULT_TOLERANCE_SECS)) {
1965
- return null;
1966
- }
1967
- if (!signature.startsWith(GATEWAY_KEY_PREFIX)) {
1968
- return null;
1969
- }
1970
2129
  const body = await request.clone().text();
1971
- const signed = await verifyHex(
1972
- credential.publicKey.toLowerCase(),
1973
- signature.slice(GATEWAY_KEY_PREFIX.length).toLowerCase(),
1974
- deliverySigned(options.url ?? request.url, timestamp, body)
2130
+ const signer = await signerOf(
2131
+ { method: request.method, url: options.url ?? request.url, headers: request.headers },
2132
+ body,
2133
+ GATEWAY_SIGNS,
2134
+ options.toleranceSecs
1975
2135
  );
1976
- return signed ? body : null;
2136
+ return signer !== null && signer.keyid === credential.publicKey.toLowerCase() ? body : null;
1977
2137
  }
1978
2138
  function proved(read) {
1979
2139
  return read !== null && read.status === "paid" && !agreesWithItself(read) ? null : read;
@@ -2001,13 +2161,6 @@ function nonceOf2(text3, type) {
2001
2161
  return null;
2002
2162
  }
2003
2163
  }
2004
- function recent(timestamp, toleranceSecs) {
2005
- const sent = Number(timestamp);
2006
- if (!Number.isFinite(sent)) {
2007
- return false;
2008
- }
2009
- return Math.abs(Math.floor(Date.now() / 1e3) - sent) <= toleranceSecs;
2010
- }
2011
2164
 
2012
2165
  // src/bank.ts
2013
2166
  var DEFAULT_CURRENCY = "CZK";
@@ -2086,7 +2239,7 @@ async function answeredAt(params, subject) {
2086
2239
  return polling.toString();
2087
2240
  }
2088
2241
  async function creditedPreimage(params) {
2089
- const since = unixNow() - (params.lookBackSecs ?? DEFAULT_LOOK_BACK_SECS);
2242
+ const since = unixNow2() - (params.lookBackSecs ?? DEFAULT_LOOK_BACK_SECS);
2090
2243
  const landed = (await params.statement(since)).some((credit) => pays(credit, params.asked));
2091
2244
  if (!landed) {
2092
2245
  return null;
@@ -2184,7 +2337,7 @@ function refuseUnusable(params, iban, currency) {
2184
2337
  function accountOf(iban) {
2185
2338
  return iban.replace(/\s+/g, "").toUpperCase();
2186
2339
  }
2187
- function unixNow() {
2340
+ function unixNow2() {
2188
2341
  return Math.floor(Date.now() / 1e3);
2189
2342
  }
2190
2343
 
@@ -2620,6 +2773,19 @@ function nwcRail(gateway, config) {
2620
2773
  // src/relay.ts
2621
2774
  var DEFAULT_POLL_EVERY_SECS3 = 5;
2622
2775
  var WALLET = "w";
2776
+ function verifiedThrough(path) {
2777
+ const through = path.verifyThrough ?? null;
2778
+ const mints = path.gatewayMints === true;
2779
+ if (through !== null && mints) {
2780
+ throw new Error("verifyThrough and gatewayMints are two different paths, name only one");
2781
+ }
2782
+ if (through === null && !mints) {
2783
+ throw new Error(
2784
+ "name verifyThrough, where your serve.lightningVerify is mounted, or gatewayMints: true to let the gateway mint and poll the wallet itself"
2785
+ );
2786
+ }
2787
+ return through;
2788
+ }
2623
2789
  function lightningVerifyEndpoint(config) {
2624
2790
  const paced = {
2625
2791
  "cache-control": `max-age=${config.pollEverySecs ?? DEFAULT_POLL_EVERY_SECS3}`
@@ -2698,6 +2864,10 @@ function bankRail(gateway, config) {
2698
2864
  };
2699
2865
  }
2700
2866
  function lightningRail(gateway, config) {
2867
+ const through = verifiedThrough(config);
2868
+ return through === null ? mintedByGateway(gateway, config) : resolvedHere(gateway, config, through);
2869
+ }
2870
+ function mintedByGateway(gateway, config) {
2701
2871
  return async (order) => {
2702
2872
  const payment = await gateway.mint(
2703
2873
  {
@@ -2721,21 +2891,20 @@ function lightningRail(gateway, config) {
2721
2891
  };
2722
2892
  };
2723
2893
  }
2724
- function blindLightningRail(gateway, config) {
2894
+ function resolvedHere(gateway, config, through) {
2725
2895
  return async (order) => {
2726
2896
  const resolved2 = await invoiceFrom(
2727
2897
  config.paidTo,
2728
2898
  await msatForOrder(order, config.amount, config.rate),
2729
2899
  config.send
2730
2900
  );
2731
- const relay = config.relayThrough;
2732
2901
  const watched = await gateway.watch({
2733
2902
  paymentHash: resolved2.paymentHash,
2734
- verifyUrl: relay ? await relayedVerifyUrl(
2735
- relay.endpoint,
2903
+ verifyUrl: await relayedVerifyUrl(
2904
+ through.endpoint,
2736
2905
  { url: resolved2.verifyUrl, hash: resolved2.paymentHash },
2737
- relay.secret
2738
- ) : resolved2.verifyUrl,
2906
+ through.secret
2907
+ ),
2739
2908
  expiresAt: resolved2.expiresAt,
2740
2909
  trigger: config.trigger,
2741
2910
  replay: config.replay,
@@ -2778,16 +2947,12 @@ var Rails = class {
2778
2947
  this.gateway = gateway;
2779
2948
  }
2780
2949
  gateway;
2781
- /** Lightning, with the gateway minting against a priority list of addresses */
2782
- lightning(config) {
2783
- return lightningRail(this.gateway, config);
2784
- }
2785
2950
  /**
2786
- * Lightning, with the invoice resolved here so the gateway is told neither the
2787
- * address nor the amount
2951
+ * Lightning against a priority list of addresses, verified through your
2952
+ * `serve.lightningVerify`, or minted by the gateway when you say `gatewayMints`
2788
2953
  */
2789
- blindLightning(config) {
2790
- return blindLightningRail(this.gateway, config);
2954
+ lightning(config) {
2955
+ return lightningRail(this.gateway, config);
2791
2956
  }
2792
2957
  /** A bank transfer, proved the way a Lightning payment is */
2793
2958
  bank(config) {
@@ -2853,11 +3018,12 @@ function waitingOf(options) {
2853
3018
  // src/trigger.ts
2854
3019
  var NONCE_BYTES3 = 16;
2855
3020
  function lnurlPayEndpoint(gateway, config) {
3021
+ const through = verifiedThrough(config);
2856
3022
  return async (request) => {
2857
3023
  const url = new URL(request.url);
2858
3024
  const asked = url.searchParams.get("to");
2859
3025
  try {
2860
- return asked === null ? await offer(gateway, config, url) : await mint(gateway, config, url, asked);
3026
+ return asked === null ? await offer(gateway, config, through, url) : await mint(gateway, config, through, url, asked);
2861
3027
  } catch (failure) {
2862
3028
  return refuse(failure instanceof Error ? failure.message : "the trigger could not be served");
2863
3029
  }
@@ -2875,10 +3041,10 @@ function watchTicketEndpoint(gateway, config) {
2875
3041
  function publicWatchTicketEndpoint(gateway, config) {
2876
3042
  return () => issue(gateway, config);
2877
3043
  }
2878
- async function offer(gateway, config, url) {
3044
+ async function offer(gateway, config, through, url) {
2879
3045
  const { least, most } = await spread(config.amount);
2880
- const winner = await quoted(gateway, config, least);
2881
- const nonce = randomNonce();
3046
+ const winner = await quoted(gateway, config, through, least);
3047
+ const nonce = randomNonce2();
2882
3048
  const callback = new URL(config.baseUrl ?? `${url.origin}${url.pathname}`);
2883
3049
  callback.searchParams.set("to", winner.address);
2884
3050
  callback.searchParams.set("least", String(least));
@@ -2893,8 +3059,8 @@ async function offer(gateway, config, url) {
2893
3059
  maxSendable: most
2894
3060
  });
2895
3061
  }
2896
- async function quoted(gateway, config, amountMsat) {
2897
- if (config.blind) {
3062
+ async function quoted(gateway, config, through, amountMsat) {
3063
+ if (through !== null) {
2898
3064
  const { won } = await quote(
2899
3065
  config.send ?? pinnedToTheAddressWeVerified,
2900
3066
  listed(config.paidTo),
@@ -2920,7 +3086,7 @@ async function spread(amount) {
2920
3086
  }
2921
3087
  return { least, most };
2922
3088
  }
2923
- async function mint(gateway, config, url, address) {
3089
+ async function mint(gateway, config, through, url, address) {
2924
3090
  const least = Number(url.searchParams.get("least"));
2925
3091
  const most = Number(url.searchParams.get("most"));
2926
3092
  const nonce = url.searchParams.get("n") ?? "";
@@ -2939,7 +3105,7 @@ async function mint(gateway, config, url, address) {
2939
3105
  least === most ? `this trigger costs exactly ${least} msat` : `this trigger takes between ${least} and ${most} msat`
2940
3106
  );
2941
3107
  }
2942
- const minted = config.blind ? await mintBlind(gateway, config, address, amountMsat) : await mintThroughGateway(gateway, config, address, amountMsat, nonce);
3108
+ const minted = through === null ? await mintThroughGateway(gateway, config, address, amountMsat, nonce) : await mintHere(gateway, config, through, address, amountMsat);
2943
3109
  return Response.json({
2944
3110
  status: "OK",
2945
3111
  pr: minted.bolt11,
@@ -2954,7 +3120,7 @@ async function mintThroughGateway(gateway, config, address, amountMsat, nonce) {
2954
3120
  );
2955
3121
  return { bolt11: payment.bolt11, verifyUrl: payment.verifyUrl };
2956
3122
  }
2957
- async function mintBlind(gateway, config, address, amountMsat) {
3123
+ async function mintHere(gateway, config, through, address, amountMsat) {
2958
3124
  const resolved2 = await resolve(
2959
3125
  config.send ?? pinnedToTheAddressWeVerified,
2960
3126
  [address],
@@ -2962,15 +3128,14 @@ async function mintBlind(gateway, config, address, amountMsat) {
2962
3128
  );
2963
3129
  const minted = { ...resolved2, amountMsat, lnAddress: resolved2.address };
2964
3130
  const locked = config.sealed;
2965
- const relay = config.relayThrough;
2966
3131
  try {
2967
3132
  await gateway.watch({
2968
3133
  paymentHash: resolved2.paymentHash,
2969
- verifyUrl: relay ? await relayedVerifyUrl(
2970
- relay.endpoint,
3134
+ verifyUrl: await relayedVerifyUrl(
3135
+ through.endpoint,
2971
3136
  { url: resolved2.verifyUrl, hash: resolved2.paymentHash },
2972
- relay.secret
2973
- ) : resolved2.verifyUrl,
3137
+ through.secret
3138
+ ),
2974
3139
  expiresAt: resolved2.expiresAt,
2975
3140
  trigger: config.watchSecret,
2976
3141
  replay: config.replay,
@@ -2992,7 +3157,7 @@ function alreadyWatched(refused) {
2992
3157
  function sign(secret, address, least, most, nonce) {
2993
3158
  return hmacHex(secret, `lnurl-callback|${address}|${least}|${most}|${nonce}`);
2994
3159
  }
2995
- function randomNonce() {
3160
+ function randomNonce2() {
2996
3161
  const bytes = crypto.getRandomValues(new Uint8Array(NONCE_BYTES3));
2997
3162
  let hex = "";
2998
3163
  for (const byte of bytes) {
@@ -3046,14 +3211,6 @@ var Serve = class {
3046
3211
  lightningVerify(config) {
3047
3212
  return lightningVerifyEndpoint(config);
3048
3213
  }
3049
- /**
3050
- * What `lightningVerify` was called before 2.2.0
3051
- *
3052
- * @deprecated Use `lightningVerify`, beside `bankVerify` and `nwcVerify`
3053
- */
3054
- verify(config) {
3055
- return this.lightningVerify(config);
3056
- }
3057
3214
  /** The verify endpoint a bank rail is polled at, answering off your own statement */
3058
3215
  bankVerify(config) {
3059
3216
  return bankVerifyEndpoint(config);
@@ -3075,7 +3232,7 @@ var Serve = class {
3075
3232
  */
3076
3233
  webhook(handlers) {
3077
3234
  const options = { toleranceSecs: handlers.toleranceSecs, url: handlers.url };
3078
- const once = onceWhileReplayable(handlers.toleranceSecs ?? DEFAULT_TOLERANCE_SECS);
3235
+ const once = onceWhileReplayable(handlers.toleranceSecs ?? TOLERANCE_SECS);
3079
3236
  return async (request) => {
3080
3237
  const credential = await this.credential(handlers);
3081
3238
  const challenge = await answerWebhookChallenge(request, credential, options);
@@ -3926,7 +4083,6 @@ var Gateways = class {
3926
4083
  WrapRefusedError,
3927
4084
  agreesWithItself,
3928
4085
  answerVerifyChallenge,
3929
- carriesProof,
3930
4086
  decodeInvoice,
3931
4087
  fiat,
3932
4088
  invoiceFrom,
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { R as Resolved } from './qr-CF-YeXU1.cjs';
2
- import { T as ThunderBridge, r as ThunderBridgeOptions, W as WaitOptions } from './bank-DvcV85sa.cjs';
3
- export { A as AttendOptions, s as BankRailConfig, d as BankVerifyConfig, t as BlindLightningRailConfig, u as CreateOptions, F as FollowOptions, H as Handler, L as Leg, v as LightningRailConfig, w as LightningVerifyConfig, M as Minted, N as NwcConnection, h as NwcRailConfig, i as NwcVerifyConfig, O as Order, P as PaymentRequest, x as PaymentRequestInit, y as PaymentRequestOptions, z as Provable, D as Proven, R as Rail, E as RailConfig, G as Rails, I as Range, J as Relayed, K as SelfConsistent, Q as Send, U as Serve, V as TicketOptions, X as TriggerConfig, Y as WatchTicketConfig, Z as WebhookCredential, _ as WebhookHandlers, $ as WebhookOptions, a0 as WrapAllowance, a1 as agreesWithItself, a2 as answerVerifyChallenge, a3 as carriesProof, a4 as invoiceFrom, a5 as proveOrigin, a6 as proveSettlement, a7 as proveWrapped, a8 as relayedVerifyUrl, a9 as wrapFeeCeiling } from './bank-DvcV85sa.cjs';
2
+ import { T as ThunderBridge, f as ThunderBridgeOptions, W as WaitOptions } from './nwc-h_-cGvMb.cjs';
3
+ export { A as AttendOptions, g as BankRailConfig, d as BankVerifyConfig, h as CreateOptions, F as FollowOptions, H as Handler, L as Leg, i as LightningRailConfig, j as LightningRailSettings, k as LightningVerifyConfig, M as Minted, N as NwcConnection, l as NwcRailConfig, m as NwcVerifyConfig, O as Order, P as PaymentRequest, n as PaymentRequestInit, o as PaymentRequestOptions, p as Provable, R as Rail, q as RailConfig, r as Rails, s as Range, t as Relayed, u as SelfConsistent, v as Send, w as Serve, x as TicketOptions, y as TriggerConfig, z as TriggerSettings, V as VerifyPath, D as VerifyThrough, E as WatchTicketConfig, G as WebhookCredential, I as WebhookHandlers, J as WebhookOptions, K as WrapAllowance, Q as agreesWithItself, U as answerVerifyChallenge, X as invoiceFrom, Y as proveOrigin, Z as proveSettlement, _ as proveWrapped, $ as relayedVerifyUrl, a0 as wrapFeeCeiling } from './nwc-h_-cGvMb.cjs';
4
4
  import { H as Handover, P as Payment, e as Held } from './types-DYZ9EkmJ.cjs';
5
5
  export { A as Amount, C as Charge, F as FiatOptions, f as MintedPayment, g as Msat, h as PaymentStatus, i as Priced, Q as Quote, S as Settlement, j as SocketTicket, W as WalletFailure, l as WalletReason, n as WatchedPayment, o as fiat, p as msat, s as sats } from './types-DYZ9EkmJ.cjs';
6
6
  import { G as GatewayCheatError } from './errors-DJmsalYZ.cjs';
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { R as Resolved } from './qr-CF-YeXU1.js';
2
- import { T as ThunderBridge, r as ThunderBridgeOptions, W as WaitOptions } from './bank-BgA45yOU.js';
3
- export { A as AttendOptions, s as BankRailConfig, d as BankVerifyConfig, t as BlindLightningRailConfig, u as CreateOptions, F as FollowOptions, H as Handler, L as Leg, v as LightningRailConfig, w as LightningVerifyConfig, M as Minted, N as NwcConnection, h as NwcRailConfig, i as NwcVerifyConfig, O as Order, P as PaymentRequest, x as PaymentRequestInit, y as PaymentRequestOptions, z as Provable, D as Proven, R as Rail, E as RailConfig, G as Rails, I as Range, J as Relayed, K as SelfConsistent, Q as Send, U as Serve, V as TicketOptions, X as TriggerConfig, Y as WatchTicketConfig, Z as WebhookCredential, _ as WebhookHandlers, $ as WebhookOptions, a0 as WrapAllowance, a1 as agreesWithItself, a2 as answerVerifyChallenge, a3 as carriesProof, a4 as invoiceFrom, a5 as proveOrigin, a6 as proveSettlement, a7 as proveWrapped, a8 as relayedVerifyUrl, a9 as wrapFeeCeiling } from './bank-BgA45yOU.js';
2
+ import { T as ThunderBridge, f as ThunderBridgeOptions, W as WaitOptions } from './nwc-BivXGSPY.js';
3
+ export { A as AttendOptions, g as BankRailConfig, d as BankVerifyConfig, h as CreateOptions, F as FollowOptions, H as Handler, L as Leg, i as LightningRailConfig, j as LightningRailSettings, k as LightningVerifyConfig, M as Minted, N as NwcConnection, l as NwcRailConfig, m as NwcVerifyConfig, O as Order, P as PaymentRequest, n as PaymentRequestInit, o as PaymentRequestOptions, p as Provable, R as Rail, q as RailConfig, r as Rails, s as Range, t as Relayed, u as SelfConsistent, v as Send, w as Serve, x as TicketOptions, y as TriggerConfig, z as TriggerSettings, V as VerifyPath, D as VerifyThrough, E as WatchTicketConfig, G as WebhookCredential, I as WebhookHandlers, J as WebhookOptions, K as WrapAllowance, Q as agreesWithItself, U as answerVerifyChallenge, X as invoiceFrom, Y as proveOrigin, Z as proveSettlement, _ as proveWrapped, $ as relayedVerifyUrl, a0 as wrapFeeCeiling } from './nwc-BivXGSPY.js';
4
4
  import { H as Handover, P as Payment, e as Held } from './types-DYZ9EkmJ.js';
5
5
  export { A as Amount, C as Charge, F as FiatOptions, f as MintedPayment, g as Msat, h as PaymentStatus, i as Priced, Q as Quote, S as Settlement, j as SocketTicket, W as WalletFailure, l as WalletReason, n as WatchedPayment, o as fiat, p as msat, s as sats } from './types-DYZ9EkmJ.js';
6
6
  import { G as GatewayCheatError } from './errors-VX0R18oE.js';