@getalby/lightning-tools 8.0.0 → 8.1.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.
@@ -12,6 +12,313 @@ const buildX402PaymentSignature = (scheme, network, invoice, requirements) => {
12
12
  return btoa(unescape(encodeURIComponent(json)));
13
13
  };
14
14
 
15
+ function bytes(b, ...lengths) {
16
+ if (!(b instanceof Uint8Array))
17
+ throw new Error('Expected Uint8Array');
18
+ if (lengths.length > 0 && !lengths.includes(b.length))
19
+ throw new Error(`Expected Uint8Array of length ${lengths}, not of length=${b.length}`);
20
+ }
21
+ function exists(instance, checkFinished = true) {
22
+ if (instance.destroyed)
23
+ throw new Error('Hash instance has been destroyed');
24
+ if (checkFinished && instance.finished)
25
+ throw new Error('Hash#digest() has already been called');
26
+ }
27
+ function output(out, instance) {
28
+ bytes(out);
29
+ const min = instance.outputLen;
30
+ if (out.length < min) {
31
+ throw new Error(`digestInto() expects output buffer of length at least ${min}`);
32
+ }
33
+ }
34
+
35
+ /*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */
36
+ // We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+.
37
+ // node.js versions earlier than v19 don't declare it in global scope.
38
+ // For node.js, package.json#exports field mapping rewrites import
39
+ // from `crypto` to `cryptoNode`, which imports native module.
40
+ // Makes the utils un-importable in browsers without a bundler.
41
+ // Once node.js 18 is deprecated, we can just drop the import.
42
+ const u8a = (a) => a instanceof Uint8Array;
43
+ // Cast array to view
44
+ const createView = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
45
+ // The rotate right (circular right shift) operation for uint32
46
+ const rotr = (word, shift) => (word << (32 - shift)) | (word >>> shift);
47
+ // big-endian hardware is rare. Just in case someone still decides to run hashes:
48
+ // early-throw an error because we don't support BE yet.
49
+ const isLE = new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44;
50
+ if (!isLE)
51
+ throw new Error('Non little-endian hardware is not supported');
52
+ const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'));
53
+ /**
54
+ * @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'
55
+ */
56
+ function bytesToHex(bytes) {
57
+ if (!u8a(bytes))
58
+ throw new Error('Uint8Array expected');
59
+ // pre-caching improves the speed 6x
60
+ let hex = '';
61
+ for (let i = 0; i < bytes.length; i++) {
62
+ hex += hexes[bytes[i]];
63
+ }
64
+ return hex;
65
+ }
66
+ /**
67
+ * @example utf8ToBytes('abc') // new Uint8Array([97, 98, 99])
68
+ */
69
+ function utf8ToBytes(str) {
70
+ if (typeof str !== 'string')
71
+ throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
72
+ return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809
73
+ }
74
+ /**
75
+ * Normalizes (non-hex) string or Uint8Array to Uint8Array.
76
+ * Warning: when Uint8Array is passed, it would NOT get copied.
77
+ * Keep in mind for future mutable operations.
78
+ */
79
+ function toBytes(data) {
80
+ if (typeof data === 'string')
81
+ data = utf8ToBytes(data);
82
+ if (!u8a(data))
83
+ throw new Error(`expected Uint8Array, got ${typeof data}`);
84
+ return data;
85
+ }
86
+ // For runtime check if class implements interface
87
+ class Hash {
88
+ // Safe version that clones internal state
89
+ clone() {
90
+ return this._cloneInto();
91
+ }
92
+ }
93
+ function wrapConstructor(hashCons) {
94
+ const hashC = (msg) => hashCons().update(toBytes(msg)).digest();
95
+ const tmp = hashCons();
96
+ hashC.outputLen = tmp.outputLen;
97
+ hashC.blockLen = tmp.blockLen;
98
+ hashC.create = () => hashCons();
99
+ return hashC;
100
+ }
101
+
102
+ // Polyfill for Safari 14
103
+ function setBigUint64(view, byteOffset, value, isLE) {
104
+ if (typeof view.setBigUint64 === 'function')
105
+ return view.setBigUint64(byteOffset, value, isLE);
106
+ const _32n = BigInt(32);
107
+ const _u32_max = BigInt(0xffffffff);
108
+ const wh = Number((value >> _32n) & _u32_max);
109
+ const wl = Number(value & _u32_max);
110
+ const h = isLE ? 4 : 0;
111
+ const l = isLE ? 0 : 4;
112
+ view.setUint32(byteOffset + h, wh, isLE);
113
+ view.setUint32(byteOffset + l, wl, isLE);
114
+ }
115
+ // Base SHA2 class (RFC 6234)
116
+ class SHA2 extends Hash {
117
+ constructor(blockLen, outputLen, padOffset, isLE) {
118
+ super();
119
+ this.blockLen = blockLen;
120
+ this.outputLen = outputLen;
121
+ this.padOffset = padOffset;
122
+ this.isLE = isLE;
123
+ this.finished = false;
124
+ this.length = 0;
125
+ this.pos = 0;
126
+ this.destroyed = false;
127
+ this.buffer = new Uint8Array(blockLen);
128
+ this.view = createView(this.buffer);
129
+ }
130
+ update(data) {
131
+ exists(this);
132
+ const { view, buffer, blockLen } = this;
133
+ data = toBytes(data);
134
+ const len = data.length;
135
+ for (let pos = 0; pos < len;) {
136
+ const take = Math.min(blockLen - this.pos, len - pos);
137
+ // Fast path: we have at least one block in input, cast it to view and process
138
+ if (take === blockLen) {
139
+ const dataView = createView(data);
140
+ for (; blockLen <= len - pos; pos += blockLen)
141
+ this.process(dataView, pos);
142
+ continue;
143
+ }
144
+ buffer.set(data.subarray(pos, pos + take), this.pos);
145
+ this.pos += take;
146
+ pos += take;
147
+ if (this.pos === blockLen) {
148
+ this.process(view, 0);
149
+ this.pos = 0;
150
+ }
151
+ }
152
+ this.length += data.length;
153
+ this.roundClean();
154
+ return this;
155
+ }
156
+ digestInto(out) {
157
+ exists(this);
158
+ output(out, this);
159
+ this.finished = true;
160
+ // Padding
161
+ // We can avoid allocation of buffer for padding completely if it
162
+ // was previously not allocated here. But it won't change performance.
163
+ const { buffer, view, blockLen, isLE } = this;
164
+ let { pos } = this;
165
+ // append the bit '1' to the message
166
+ buffer[pos++] = 0b10000000;
167
+ this.buffer.subarray(pos).fill(0);
168
+ // we have less than padOffset left in buffer, so we cannot put length in current block, need process it and pad again
169
+ if (this.padOffset > blockLen - pos) {
170
+ this.process(view, 0);
171
+ pos = 0;
172
+ }
173
+ // Pad until full block byte with zeros
174
+ for (let i = pos; i < blockLen; i++)
175
+ buffer[i] = 0;
176
+ // Note: sha512 requires length to be 128bit integer, but length in JS will overflow before that
177
+ // You need to write around 2 exabytes (u64_max / 8 / (1024**6)) for this to happen.
178
+ // So we just write lowest 64 bits of that value.
179
+ setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE);
180
+ this.process(view, 0);
181
+ const oview = createView(out);
182
+ const len = this.outputLen;
183
+ // NOTE: we do division by 4 later, which should be fused in single op with modulo by JIT
184
+ if (len % 4)
185
+ throw new Error('_sha2: outputLen should be aligned to 32bit');
186
+ const outLen = len / 4;
187
+ const state = this.get();
188
+ if (outLen > state.length)
189
+ throw new Error('_sha2: outputLen bigger than state');
190
+ for (let i = 0; i < outLen; i++)
191
+ oview.setUint32(4 * i, state[i], isLE);
192
+ }
193
+ digest() {
194
+ const { buffer, outputLen } = this;
195
+ this.digestInto(buffer);
196
+ const res = buffer.slice(0, outputLen);
197
+ this.destroy();
198
+ return res;
199
+ }
200
+ _cloneInto(to) {
201
+ to || (to = new this.constructor());
202
+ to.set(...this.get());
203
+ const { blockLen, buffer, length, finished, destroyed, pos } = this;
204
+ to.length = length;
205
+ to.pos = pos;
206
+ to.finished = finished;
207
+ to.destroyed = destroyed;
208
+ if (length % blockLen)
209
+ to.buffer.set(buffer);
210
+ return to;
211
+ }
212
+ }
213
+
214
+ // SHA2-256 need to try 2^128 hashes to execute birthday attack.
215
+ // BTC network is doing 2^67 hashes/sec as per early 2023.
216
+ // Choice: a ? b : c
217
+ const Chi = (a, b, c) => (a & b) ^ (~a & c);
218
+ // Majority function, true if any two inpust is true
219
+ const Maj = (a, b, c) => (a & b) ^ (a & c) ^ (b & c);
220
+ // Round constants:
221
+ // first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311)
222
+ // prettier-ignore
223
+ const SHA256_K = /* @__PURE__ */ new Uint32Array([
224
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
225
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
226
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
227
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
228
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
229
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
230
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
231
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
232
+ ]);
233
+ // Initial state (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):
234
+ // prettier-ignore
235
+ const IV = /* @__PURE__ */ new Uint32Array([
236
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
237
+ ]);
238
+ // Temporary buffer, not used to store anything between runs
239
+ // Named this way because it matches specification.
240
+ const SHA256_W = /* @__PURE__ */ new Uint32Array(64);
241
+ class SHA256 extends SHA2 {
242
+ constructor() {
243
+ super(64, 32, 8, false);
244
+ // We cannot use array here since array allows indexing by variable
245
+ // which means optimizer/compiler cannot use registers.
246
+ this.A = IV[0] | 0;
247
+ this.B = IV[1] | 0;
248
+ this.C = IV[2] | 0;
249
+ this.D = IV[3] | 0;
250
+ this.E = IV[4] | 0;
251
+ this.F = IV[5] | 0;
252
+ this.G = IV[6] | 0;
253
+ this.H = IV[7] | 0;
254
+ }
255
+ get() {
256
+ const { A, B, C, D, E, F, G, H } = this;
257
+ return [A, B, C, D, E, F, G, H];
258
+ }
259
+ // prettier-ignore
260
+ set(A, B, C, D, E, F, G, H) {
261
+ this.A = A | 0;
262
+ this.B = B | 0;
263
+ this.C = C | 0;
264
+ this.D = D | 0;
265
+ this.E = E | 0;
266
+ this.F = F | 0;
267
+ this.G = G | 0;
268
+ this.H = H | 0;
269
+ }
270
+ process(view, offset) {
271
+ // Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array
272
+ for (let i = 0; i < 16; i++, offset += 4)
273
+ SHA256_W[i] = view.getUint32(offset, false);
274
+ for (let i = 16; i < 64; i++) {
275
+ const W15 = SHA256_W[i - 15];
276
+ const W2 = SHA256_W[i - 2];
277
+ const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ (W15 >>> 3);
278
+ const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ (W2 >>> 10);
279
+ SHA256_W[i] = (s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16]) | 0;
280
+ }
281
+ // Compression function main loop, 64 rounds
282
+ let { A, B, C, D, E, F, G, H } = this;
283
+ for (let i = 0; i < 64; i++) {
284
+ const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
285
+ const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;
286
+ const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);
287
+ const T2 = (sigma0 + Maj(A, B, C)) | 0;
288
+ H = G;
289
+ G = F;
290
+ F = E;
291
+ E = (D + T1) | 0;
292
+ D = C;
293
+ C = B;
294
+ B = A;
295
+ A = (T1 + T2) | 0;
296
+ }
297
+ // Add the compressed chunk to the current hash value
298
+ A = (A + this.A) | 0;
299
+ B = (B + this.B) | 0;
300
+ C = (C + this.C) | 0;
301
+ D = (D + this.D) | 0;
302
+ E = (E + this.E) | 0;
303
+ F = (F + this.F) | 0;
304
+ G = (G + this.G) | 0;
305
+ H = (H + this.H) | 0;
306
+ this.set(A, B, C, D, E, F, G, H);
307
+ }
308
+ roundClean() {
309
+ SHA256_W.fill(0);
310
+ }
311
+ destroy() {
312
+ this.set(0, 0, 0, 0, 0, 0, 0, 0);
313
+ this.buffer.fill(0);
314
+ }
315
+ }
316
+ /**
317
+ * SHA2-256 hash function
318
+ * @param message - data that would be hashed
319
+ */
320
+ const sha256 = /* @__PURE__ */ wrapConstructor(() => new SHA256());
321
+
15
322
  var lib = {};
16
323
 
17
324
  var hasRequiredLib;
@@ -679,515 +986,221 @@ function requireBolt11 () {
679
986
  letters: 'ln'
680
987
  });
681
988
 
682
- // "bc" section
683
- const bech32Prefix = prefixMatches[1];
684
- let coinNetwork;
685
- if (!network) {
686
- switch (bech32Prefix) {
687
- case DEFAULTNETWORK.bech32:
688
- coinNetwork = DEFAULTNETWORK;
689
- break
690
- case TESTNETWORK.bech32:
691
- coinNetwork = TESTNETWORK;
692
- break
693
- case SIGNETNETWORK.bech32:
694
- coinNetwork = SIGNETNETWORK;
695
- break
696
- case REGTESTNETWORK.bech32:
697
- coinNetwork = REGTESTNETWORK;
698
- break
699
- case SIMNETWORK.bech32:
700
- coinNetwork = SIMNETWORK;
701
- break
702
- }
703
- } else {
704
- if (
705
- network.bech32 === undefined ||
706
- network.pubKeyHash === undefined ||
707
- network.scriptHash === undefined ||
708
- !Array.isArray(network.validWitnessVersions)
709
- )
710
- throw new Error('Invalid network')
711
- coinNetwork = network;
712
- }
713
- if (!coinNetwork || coinNetwork.bech32 !== bech32Prefix) {
714
- throw new Error('Unknown coin bech32 prefix')
715
- }
716
- sections.push({
717
- name: 'coin_network',
718
- letters: bech32Prefix,
719
- value: coinNetwork
720
- });
721
-
722
- // amount section
723
- const value = prefixMatches[2];
724
- let millisatoshis;
725
- if (value) {
726
- const divisor = prefixMatches[3];
727
- millisatoshis = hrpToMillisat(value + divisor, true);
728
- sections.push({
729
- name: 'amount',
730
- letters: prefixMatches[2] + prefixMatches[3],
731
- value: millisatoshis
732
- });
733
- } else {
734
- millisatoshis = null;
735
- }
736
-
737
- // "1" separator
738
- sections.push({
739
- name: 'separator',
740
- letters: '1'
741
- });
742
-
743
- // timestamp
744
- const timestamp = wordsToIntBE(words.slice(0, 7));
745
- words = words.slice(7); // trim off the left 7 words
746
- sections.push({
747
- name: 'timestamp',
748
- letters: letters.slice(0, 7),
749
- value: timestamp
750
- });
751
- letters = letters.slice(7);
752
-
753
- let tagName, parser, tagLength, tagWords;
754
- // we have no tag count to go on, so just keep hacking off words
755
- // until we have none.
756
- while (words.length > 0) {
757
- const tagCode = words[0].toString();
758
- tagName = TAGNAMES[tagCode] || 'unknown_tag';
759
- parser = TAGPARSERS[tagCode] || getUnknownParser(tagCode);
760
- words = words.slice(1);
761
-
762
- tagLength = wordsToIntBE(words.slice(0, 2));
763
- words = words.slice(2);
764
-
765
- tagWords = words.slice(0, tagLength);
766
- words = words.slice(tagLength);
767
-
768
- sections.push({
769
- name: tagName,
770
- tag: letters[0],
771
- letters: letters.slice(0, 1 + 2 + tagLength),
772
- value: parser(tagWords) // see: parsers for more comments
773
- });
774
- letters = letters.slice(1 + 2 + tagLength);
775
- }
776
-
777
- // signature
778
- sections.push({
779
- name: 'signature',
780
- letters: letters.slice(0, 104),
781
- value: hex.encode(bech32.fromWordsUnsafe(sigWords))
782
- });
783
- letters = letters.slice(104);
784
-
785
- // checksum
786
- sections.push({
787
- name: 'checksum',
788
- letters: letters
789
- });
790
-
791
- let result = {
792
- paymentRequest,
793
- sections,
794
-
795
- get expiry() {
796
- let exp = sections.find(s => s.name === 'expiry');
797
- if (exp) return getValue('timestamp') + exp.value
798
- },
799
-
800
- get route_hints() {
801
- return sections.filter(s => s.name === 'route_hint').map(s => s.value)
802
- }
803
- };
804
-
805
- for (let name in TAGCODES) {
806
- if (name === 'route_hint') {
807
- // route hints can be multiple, so this won't work for them
808
- continue
809
- }
810
-
811
- Object.defineProperty(result, name, {
812
- get() {
813
- return getValue(name)
814
- }
815
- });
816
- }
817
-
818
- return result
819
-
820
- function getValue(name) {
821
- let section = sections.find(s => s.name === name);
822
- return section ? section.value : undefined
823
- }
824
- }
825
-
826
- bolt11 = {
827
- decode,
828
- hrpToMillisat
829
- };
830
- return bolt11;
831
- }
832
-
833
- var bolt11Exports = requireBolt11();
834
-
835
- // from https://stackoverflow.com/a/50868276
836
- const fromHexString = (hexString) => Uint8Array.from(hexString.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
837
- const decodeInvoice = (paymentRequest) => {
838
- if (!paymentRequest)
839
- return null;
840
- try {
841
- const decoded = bolt11Exports.decode(paymentRequest);
842
- if (!decoded || !decoded.sections)
843
- return null;
844
- const hashTag = decoded.sections.find((value) => value.name === "payment_hash");
845
- if (hashTag?.name !== "payment_hash" || !hashTag.value)
846
- return null;
847
- const paymentHash = hashTag.value;
848
- let satoshi = 0;
849
- let millisatoshi = 0;
850
- let amountRaw = "0";
851
- const amountTag = decoded.sections.find((value) => value.name === "amount");
852
- if (amountTag?.name === "amount" && amountTag.value) {
853
- amountRaw = amountTag.value;
854
- millisatoshi = parseInt(amountTag.value);
855
- satoshi = parseInt(amountTag.value) / 1000; // millisats
856
- }
857
- const timestampTag = decoded.sections.find((value) => value.name === "timestamp");
858
- if (timestampTag?.name !== "timestamp" || !timestampTag.value)
859
- return null;
860
- const timestamp = timestampTag.value;
861
- let expiry;
862
- const expiryTag = decoded.sections.find((value) => value.name === "expiry");
863
- if (expiryTag?.name === "expiry") {
864
- expiry = expiryTag.value;
865
- }
866
- const descriptionTag = decoded.sections.find((value) => value.name === "description");
867
- const description = descriptionTag?.name === "description"
868
- ? descriptionTag?.value
869
- : undefined;
870
- return {
871
- paymentHash,
872
- satoshi,
873
- millisatoshi,
874
- amountRaw,
875
- timestamp,
876
- expiry,
877
- description,
878
- };
879
- }
880
- catch {
881
- return null;
882
- }
883
- };
989
+ // "bc" section
990
+ const bech32Prefix = prefixMatches[1];
991
+ let coinNetwork;
992
+ if (!network) {
993
+ switch (bech32Prefix) {
994
+ case DEFAULTNETWORK.bech32:
995
+ coinNetwork = DEFAULTNETWORK;
996
+ break
997
+ case TESTNETWORK.bech32:
998
+ coinNetwork = TESTNETWORK;
999
+ break
1000
+ case SIGNETNETWORK.bech32:
1001
+ coinNetwork = SIGNETNETWORK;
1002
+ break
1003
+ case REGTESTNETWORK.bech32:
1004
+ coinNetwork = REGTESTNETWORK;
1005
+ break
1006
+ case SIMNETWORK.bech32:
1007
+ coinNetwork = SIMNETWORK;
1008
+ break
1009
+ }
1010
+ } else {
1011
+ if (
1012
+ network.bech32 === undefined ||
1013
+ network.pubKeyHash === undefined ||
1014
+ network.scriptHash === undefined ||
1015
+ !Array.isArray(network.validWitnessVersions)
1016
+ )
1017
+ throw new Error('Invalid network')
1018
+ coinNetwork = network;
1019
+ }
1020
+ if (!coinNetwork || coinNetwork.bech32 !== bech32Prefix) {
1021
+ throw new Error('Unknown coin bech32 prefix')
1022
+ }
1023
+ sections.push({
1024
+ name: 'coin_network',
1025
+ letters: bech32Prefix,
1026
+ value: coinNetwork
1027
+ });
884
1028
 
885
- function bytes(b, ...lengths) {
886
- if (!(b instanceof Uint8Array))
887
- throw new Error('Expected Uint8Array');
888
- if (lengths.length > 0 && !lengths.includes(b.length))
889
- throw new Error(`Expected Uint8Array of length ${lengths}, not of length=${b.length}`);
890
- }
891
- function exists(instance, checkFinished = true) {
892
- if (instance.destroyed)
893
- throw new Error('Hash instance has been destroyed');
894
- if (checkFinished && instance.finished)
895
- throw new Error('Hash#digest() has already been called');
896
- }
897
- function output(out, instance) {
898
- bytes(out);
899
- const min = instance.outputLen;
900
- if (out.length < min) {
901
- throw new Error(`digestInto() expects output buffer of length at least ${min}`);
902
- }
903
- }
1029
+ // amount section
1030
+ const value = prefixMatches[2];
1031
+ let millisatoshis;
1032
+ if (value) {
1033
+ const divisor = prefixMatches[3];
1034
+ millisatoshis = hrpToMillisat(value + divisor, true);
1035
+ sections.push({
1036
+ name: 'amount',
1037
+ letters: prefixMatches[2] + prefixMatches[3],
1038
+ value: millisatoshis
1039
+ });
1040
+ } else {
1041
+ millisatoshis = null;
1042
+ }
904
1043
 
905
- /*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */
906
- // We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+.
907
- // node.js versions earlier than v19 don't declare it in global scope.
908
- // For node.js, package.json#exports field mapping rewrites import
909
- // from `crypto` to `cryptoNode`, which imports native module.
910
- // Makes the utils un-importable in browsers without a bundler.
911
- // Once node.js 18 is deprecated, we can just drop the import.
912
- const u8a = (a) => a instanceof Uint8Array;
913
- // Cast array to view
914
- const createView = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
915
- // The rotate right (circular right shift) operation for uint32
916
- const rotr = (word, shift) => (word << (32 - shift)) | (word >>> shift);
917
- // big-endian hardware is rare. Just in case someone still decides to run hashes:
918
- // early-throw an error because we don't support BE yet.
919
- const isLE = new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44;
920
- if (!isLE)
921
- throw new Error('Non little-endian hardware is not supported');
922
- const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'));
923
- /**
924
- * @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'
925
- */
926
- function bytesToHex(bytes) {
927
- if (!u8a(bytes))
928
- throw new Error('Uint8Array expected');
929
- // pre-caching improves the speed 6x
930
- let hex = '';
931
- for (let i = 0; i < bytes.length; i++) {
932
- hex += hexes[bytes[i]];
933
- }
934
- return hex;
935
- }
936
- /**
937
- * @example utf8ToBytes('abc') // new Uint8Array([97, 98, 99])
938
- */
939
- function utf8ToBytes(str) {
940
- if (typeof str !== 'string')
941
- throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
942
- return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809
943
- }
944
- /**
945
- * Normalizes (non-hex) string or Uint8Array to Uint8Array.
946
- * Warning: when Uint8Array is passed, it would NOT get copied.
947
- * Keep in mind for future mutable operations.
948
- */
949
- function toBytes(data) {
950
- if (typeof data === 'string')
951
- data = utf8ToBytes(data);
952
- if (!u8a(data))
953
- throw new Error(`expected Uint8Array, got ${typeof data}`);
954
- return data;
955
- }
956
- // For runtime check if class implements interface
957
- class Hash {
958
- // Safe version that clones internal state
959
- clone() {
960
- return this._cloneInto();
961
- }
962
- }
963
- function wrapConstructor(hashCons) {
964
- const hashC = (msg) => hashCons().update(toBytes(msg)).digest();
965
- const tmp = hashCons();
966
- hashC.outputLen = tmp.outputLen;
967
- hashC.blockLen = tmp.blockLen;
968
- hashC.create = () => hashCons();
969
- return hashC;
970
- }
1044
+ // "1" separator
1045
+ sections.push({
1046
+ name: 'separator',
1047
+ letters: '1'
1048
+ });
971
1049
 
972
- // Polyfill for Safari 14
973
- function setBigUint64(view, byteOffset, value, isLE) {
974
- if (typeof view.setBigUint64 === 'function')
975
- return view.setBigUint64(byteOffset, value, isLE);
976
- const _32n = BigInt(32);
977
- const _u32_max = BigInt(0xffffffff);
978
- const wh = Number((value >> _32n) & _u32_max);
979
- const wl = Number(value & _u32_max);
980
- const h = isLE ? 4 : 0;
981
- const l = isLE ? 0 : 4;
982
- view.setUint32(byteOffset + h, wh, isLE);
983
- view.setUint32(byteOffset + l, wl, isLE);
984
- }
985
- // Base SHA2 class (RFC 6234)
986
- class SHA2 extends Hash {
987
- constructor(blockLen, outputLen, padOffset, isLE) {
988
- super();
989
- this.blockLen = blockLen;
990
- this.outputLen = outputLen;
991
- this.padOffset = padOffset;
992
- this.isLE = isLE;
993
- this.finished = false;
994
- this.length = 0;
995
- this.pos = 0;
996
- this.destroyed = false;
997
- this.buffer = new Uint8Array(blockLen);
998
- this.view = createView(this.buffer);
999
- }
1000
- update(data) {
1001
- exists(this);
1002
- const { view, buffer, blockLen } = this;
1003
- data = toBytes(data);
1004
- const len = data.length;
1005
- for (let pos = 0; pos < len;) {
1006
- const take = Math.min(blockLen - this.pos, len - pos);
1007
- // Fast path: we have at least one block in input, cast it to view and process
1008
- if (take === blockLen) {
1009
- const dataView = createView(data);
1010
- for (; blockLen <= len - pos; pos += blockLen)
1011
- this.process(dataView, pos);
1012
- continue;
1013
- }
1014
- buffer.set(data.subarray(pos, pos + take), this.pos);
1015
- this.pos += take;
1016
- pos += take;
1017
- if (this.pos === blockLen) {
1018
- this.process(view, 0);
1019
- this.pos = 0;
1020
- }
1021
- }
1022
- this.length += data.length;
1023
- this.roundClean();
1024
- return this;
1025
- }
1026
- digestInto(out) {
1027
- exists(this);
1028
- output(out, this);
1029
- this.finished = true;
1030
- // Padding
1031
- // We can avoid allocation of buffer for padding completely if it
1032
- // was previously not allocated here. But it won't change performance.
1033
- const { buffer, view, blockLen, isLE } = this;
1034
- let { pos } = this;
1035
- // append the bit '1' to the message
1036
- buffer[pos++] = 0b10000000;
1037
- this.buffer.subarray(pos).fill(0);
1038
- // we have less than padOffset left in buffer, so we cannot put length in current block, need process it and pad again
1039
- if (this.padOffset > blockLen - pos) {
1040
- this.process(view, 0);
1041
- pos = 0;
1042
- }
1043
- // Pad until full block byte with zeros
1044
- for (let i = pos; i < blockLen; i++)
1045
- buffer[i] = 0;
1046
- // Note: sha512 requires length to be 128bit integer, but length in JS will overflow before that
1047
- // You need to write around 2 exabytes (u64_max / 8 / (1024**6)) for this to happen.
1048
- // So we just write lowest 64 bits of that value.
1049
- setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE);
1050
- this.process(view, 0);
1051
- const oview = createView(out);
1052
- const len = this.outputLen;
1053
- // NOTE: we do division by 4 later, which should be fused in single op with modulo by JIT
1054
- if (len % 4)
1055
- throw new Error('_sha2: outputLen should be aligned to 32bit');
1056
- const outLen = len / 4;
1057
- const state = this.get();
1058
- if (outLen > state.length)
1059
- throw new Error('_sha2: outputLen bigger than state');
1060
- for (let i = 0; i < outLen; i++)
1061
- oview.setUint32(4 * i, state[i], isLE);
1062
- }
1063
- digest() {
1064
- const { buffer, outputLen } = this;
1065
- this.digestInto(buffer);
1066
- const res = buffer.slice(0, outputLen);
1067
- this.destroy();
1068
- return res;
1069
- }
1070
- _cloneInto(to) {
1071
- to || (to = new this.constructor());
1072
- to.set(...this.get());
1073
- const { blockLen, buffer, length, finished, destroyed, pos } = this;
1074
- to.length = length;
1075
- to.pos = pos;
1076
- to.finished = finished;
1077
- to.destroyed = destroyed;
1078
- if (length % blockLen)
1079
- to.buffer.set(buffer);
1080
- return to;
1081
- }
1050
+ // timestamp
1051
+ const timestamp = wordsToIntBE(words.slice(0, 7));
1052
+ words = words.slice(7); // trim off the left 7 words
1053
+ sections.push({
1054
+ name: 'timestamp',
1055
+ letters: letters.slice(0, 7),
1056
+ value: timestamp
1057
+ });
1058
+ letters = letters.slice(7);
1059
+
1060
+ let tagName, parser, tagLength, tagWords;
1061
+ // we have no tag count to go on, so just keep hacking off words
1062
+ // until we have none.
1063
+ while (words.length > 0) {
1064
+ const tagCode = words[0].toString();
1065
+ tagName = TAGNAMES[tagCode] || 'unknown_tag';
1066
+ parser = TAGPARSERS[tagCode] || getUnknownParser(tagCode);
1067
+ words = words.slice(1);
1068
+
1069
+ tagLength = wordsToIntBE(words.slice(0, 2));
1070
+ words = words.slice(2);
1071
+
1072
+ tagWords = words.slice(0, tagLength);
1073
+ words = words.slice(tagLength);
1074
+
1075
+ sections.push({
1076
+ name: tagName,
1077
+ tag: letters[0],
1078
+ letters: letters.slice(0, 1 + 2 + tagLength),
1079
+ value: parser(tagWords) // see: parsers for more comments
1080
+ });
1081
+ letters = letters.slice(1 + 2 + tagLength);
1082
+ }
1083
+
1084
+ // signature
1085
+ sections.push({
1086
+ name: 'signature',
1087
+ letters: letters.slice(0, 104),
1088
+ value: hex.encode(bech32.fromWordsUnsafe(sigWords))
1089
+ });
1090
+ letters = letters.slice(104);
1091
+
1092
+ // checksum
1093
+ sections.push({
1094
+ name: 'checksum',
1095
+ letters: letters
1096
+ });
1097
+
1098
+ let result = {
1099
+ paymentRequest,
1100
+ sections,
1101
+
1102
+ get expiry() {
1103
+ let exp = sections.find(s => s.name === 'expiry');
1104
+ if (exp) return getValue('timestamp') + exp.value
1105
+ },
1106
+
1107
+ get route_hints() {
1108
+ return sections.filter(s => s.name === 'route_hint').map(s => s.value)
1109
+ }
1110
+ };
1111
+
1112
+ for (let name in TAGCODES) {
1113
+ if (name === 'route_hint') {
1114
+ // route hints can be multiple, so this won't work for them
1115
+ continue
1116
+ }
1117
+
1118
+ Object.defineProperty(result, name, {
1119
+ get() {
1120
+ return getValue(name)
1121
+ }
1122
+ });
1123
+ }
1124
+
1125
+ return result
1126
+
1127
+ function getValue(name) {
1128
+ let section = sections.find(s => s.name === name);
1129
+ return section ? section.value : undefined
1130
+ }
1131
+ }
1132
+
1133
+ bolt11 = {
1134
+ decode,
1135
+ hrpToMillisat
1136
+ };
1137
+ return bolt11;
1082
1138
  }
1083
1139
 
1084
- // SHA2-256 need to try 2^128 hashes to execute birthday attack.
1085
- // BTC network is doing 2^67 hashes/sec as per early 2023.
1086
- // Choice: a ? b : c
1087
- const Chi = (a, b, c) => (a & b) ^ (~a & c);
1088
- // Majority function, true if any two inpust is true
1089
- const Maj = (a, b, c) => (a & b) ^ (a & c) ^ (b & c);
1090
- // Round constants:
1091
- // first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311)
1092
- // prettier-ignore
1093
- const SHA256_K = /* @__PURE__ */ new Uint32Array([
1094
- 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
1095
- 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
1096
- 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
1097
- 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
1098
- 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
1099
- 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
1100
- 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
1101
- 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
1102
- ]);
1103
- // Initial state (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):
1104
- // prettier-ignore
1105
- const IV = /* @__PURE__ */ new Uint32Array([
1106
- 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
1107
- ]);
1108
- // Temporary buffer, not used to store anything between runs
1109
- // Named this way because it matches specification.
1110
- const SHA256_W = /* @__PURE__ */ new Uint32Array(64);
1111
- class SHA256 extends SHA2 {
1112
- constructor() {
1113
- super(64, 32, 8, false);
1114
- // We cannot use array here since array allows indexing by variable
1115
- // which means optimizer/compiler cannot use registers.
1116
- this.A = IV[0] | 0;
1117
- this.B = IV[1] | 0;
1118
- this.C = IV[2] | 0;
1119
- this.D = IV[3] | 0;
1120
- this.E = IV[4] | 0;
1121
- this.F = IV[5] | 0;
1122
- this.G = IV[6] | 0;
1123
- this.H = IV[7] | 0;
1124
- }
1125
- get() {
1126
- const { A, B, C, D, E, F, G, H } = this;
1127
- return [A, B, C, D, E, F, G, H];
1128
- }
1129
- // prettier-ignore
1130
- set(A, B, C, D, E, F, G, H) {
1131
- this.A = A | 0;
1132
- this.B = B | 0;
1133
- this.C = C | 0;
1134
- this.D = D | 0;
1135
- this.E = E | 0;
1136
- this.F = F | 0;
1137
- this.G = G | 0;
1138
- this.H = H | 0;
1139
- }
1140
- process(view, offset) {
1141
- // Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array
1142
- for (let i = 0; i < 16; i++, offset += 4)
1143
- SHA256_W[i] = view.getUint32(offset, false);
1144
- for (let i = 16; i < 64; i++) {
1145
- const W15 = SHA256_W[i - 15];
1146
- const W2 = SHA256_W[i - 2];
1147
- const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ (W15 >>> 3);
1148
- const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ (W2 >>> 10);
1149
- SHA256_W[i] = (s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16]) | 0;
1140
+ var bolt11Exports = requireBolt11();
1141
+
1142
+ // from https://stackoverflow.com/a/50868276
1143
+ const fromHexString = (hexString) => Uint8Array.from(hexString.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
1144
+ const decodeInvoice = (paymentRequest) => {
1145
+ if (!paymentRequest)
1146
+ return null;
1147
+ try {
1148
+ const decoded = bolt11Exports.decode(paymentRequest);
1149
+ if (!decoded || !decoded.sections)
1150
+ return null;
1151
+ const hashTag = decoded.sections.find((value) => value.name === "payment_hash");
1152
+ if (hashTag?.name !== "payment_hash" || !hashTag.value)
1153
+ return null;
1154
+ const paymentHash = hashTag.value;
1155
+ let satoshi = 0;
1156
+ let millisatoshi = 0;
1157
+ let amountRaw = "0";
1158
+ const amountTag = decoded.sections.find((value) => value.name === "amount");
1159
+ if (amountTag?.name === "amount" && amountTag.value) {
1160
+ amountRaw = amountTag.value;
1161
+ millisatoshi = parseInt(amountTag.value);
1162
+ satoshi = parseInt(amountTag.value) / 1000; // millisats
1150
1163
  }
1151
- // Compression function main loop, 64 rounds
1152
- let { A, B, C, D, E, F, G, H } = this;
1153
- for (let i = 0; i < 64; i++) {
1154
- const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
1155
- const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;
1156
- const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);
1157
- const T2 = (sigma0 + Maj(A, B, C)) | 0;
1158
- H = G;
1159
- G = F;
1160
- F = E;
1161
- E = (D + T1) | 0;
1162
- D = C;
1163
- C = B;
1164
- B = A;
1165
- A = (T1 + T2) | 0;
1164
+ const timestampTag = decoded.sections.find((value) => value.name === "timestamp");
1165
+ if (timestampTag?.name !== "timestamp" || !timestampTag.value)
1166
+ return null;
1167
+ const timestamp = timestampTag.value;
1168
+ let expiry;
1169
+ const expiryTag = decoded.sections.find((value) => value.name === "expiry");
1170
+ if (expiryTag?.name === "expiry") {
1171
+ expiry = expiryTag.value;
1166
1172
  }
1167
- // Add the compressed chunk to the current hash value
1168
- A = (A + this.A) | 0;
1169
- B = (B + this.B) | 0;
1170
- C = (C + this.C) | 0;
1171
- D = (D + this.D) | 0;
1172
- E = (E + this.E) | 0;
1173
- F = (F + this.F) | 0;
1174
- G = (G + this.G) | 0;
1175
- H = (H + this.H) | 0;
1176
- this.set(A, B, C, D, E, F, G, H);
1173
+ const descriptionTag = decoded.sections.find((value) => value.name === "description");
1174
+ const description = descriptionTag?.name === "description"
1175
+ ? descriptionTag?.value
1176
+ : undefined;
1177
+ return {
1178
+ paymentHash,
1179
+ satoshi,
1180
+ millisatoshi,
1181
+ amountRaw,
1182
+ timestamp,
1183
+ expiry,
1184
+ description,
1185
+ };
1177
1186
  }
1178
- roundClean() {
1179
- SHA256_W.fill(0);
1187
+ catch {
1188
+ return null;
1180
1189
  }
1181
- destroy() {
1182
- this.set(0, 0, 0, 0, 0, 0, 0, 0);
1183
- this.buffer.fill(0);
1190
+ };
1191
+ function validatePreimage(preimage, paymentHash) {
1192
+ try {
1193
+ if (!/^[0-9a-fA-F]{64}$/.test(preimage))
1194
+ return false;
1195
+ if (!/^[0-9a-fA-F]{64}$/.test(paymentHash))
1196
+ return false;
1197
+ const preimageHash = bytesToHex(sha256(fromHexString(preimage)));
1198
+ return paymentHash === preimageHash;
1199
+ }
1200
+ catch {
1201
+ return false;
1184
1202
  }
1185
1203
  }
1186
- /**
1187
- * SHA2-256 hash function
1188
- * @param message - data that would be hashed
1189
- */
1190
- const sha256 = /* @__PURE__ */ wrapConstructor(() => new SHA256());
1191
1204
 
1192
1205
  class Invoice {
1193
1206
  constructor(args) {
@@ -1227,13 +1240,7 @@ class Invoice {
1227
1240
  validatePreimage(preimage) {
1228
1241
  if (!preimage || !this.paymentHash)
1229
1242
  return false;
1230
- try {
1231
- const preimageHash = bytesToHex(sha256(fromHexString(preimage)));
1232
- return this.paymentHash === preimageHash;
1233
- }
1234
- catch {
1235
- return false;
1236
- }
1243
+ return validatePreimage(preimage, this.paymentHash);
1237
1244
  }
1238
1245
  async verifyPayment() {
1239
1246
  try {
@@ -1264,7 +1271,7 @@ class Invoice {
1264
1271
  }
1265
1272
  }
1266
1273
 
1267
- const handleX402Payment = async (x402Header, url, fetchArgs, headers, wallet) => {
1274
+ const decodeX402Header = (x402Header) => {
1268
1275
  let parsed;
1269
1276
  try {
1270
1277
  parsed = JSON.parse(decodeURIComponent(escape(atob(x402Header))));
@@ -1275,9 +1282,31 @@ const handleX402Payment = async (x402Header, url, fetchArgs, headers, wallet) =>
1275
1282
  if (!Array.isArray(parsed.accepts) || parsed.accepts.length === 0) {
1276
1283
  throw new Error("x402: PAYMENT-REQUIRED header contains no payment options");
1277
1284
  }
1278
- const requirements = parsed.accepts.find((e) => {
1279
- return e.extra?.paymentMethod === "lightning";
1280
- });
1285
+ return { accepts: parsed.accepts };
1286
+ };
1287
+ /**
1288
+ * Probe a PAYMENT-REQUIRED header for a lightning-payable offer without
1289
+ * throwing. Returns the matching requirements, or null if the header has no
1290
+ * lightning entry (e.g. USDC-only endpoints) or is malformed. Used by the
1291
+ * top-level fetch402 dispatcher to decide whether to attempt payment or hand
1292
+ * the 402 back to the caller.
1293
+ */
1294
+ const findX402LightningRequirements = (x402Header) => {
1295
+ let accepts;
1296
+ try {
1297
+ ({ accepts } = decodeX402Header(x402Header));
1298
+ }
1299
+ catch (_) {
1300
+ return null;
1301
+ }
1302
+ const requirements = accepts.find((e) => e?.extra?.paymentMethod === "lightning");
1303
+ if (!requirements?.extra?.invoice)
1304
+ return null;
1305
+ return requirements;
1306
+ };
1307
+ const handleX402Payment = async (x402Header, url, fetchArgs, headers, wallet) => {
1308
+ const { accepts } = decodeX402Header(x402Header);
1309
+ const requirements = accepts.find((e) => e?.extra?.paymentMethod === "lightning");
1281
1310
  if (!requirements) {
1282
1311
  throw new Error("x402: unsupported x402 network, only Bitcoin lightning network is supported.");
1283
1312
  }
@@ -1310,4 +1339,5 @@ const fetchWithX402 = async (url, fetchArgs, options) => {
1310
1339
  };
1311
1340
 
1312
1341
  exports.fetchWithX402 = fetchWithX402;
1342
+ exports.findX402LightningRequirements = findX402LightningRequirements;
1313
1343
  //# sourceMappingURL=x402.cjs.map