thunder-bridge 1.5.0 → 2.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.
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 };
package/openapi.yaml CHANGED
@@ -2,7 +2,7 @@ openapi: 3.1.0
2
2
 
3
3
  info:
4
4
  title: thunder-bridge, the endpoint your own service serves
5
- version: 1.5.0
5
+ version: 2.1.1
6
6
  license:
7
7
  name: MIT
8
8
  identifier: MIT
@@ -71,14 +71,17 @@ paths:
71
71
  summary: Quote the price, or mint the invoice for a quote already given
72
72
  description: |
73
73
  Without `to` this answers the payRequest. It calls your `amountMsat()` once, quotes
74
- your address list through the gateway, and returns the first address that answers and
75
- accepts the amount, along with that wallet's own LUD-06 metadata string byte for byte.
74
+ your address list, through the gateway or by itself when the trigger is blind, and
75
+ returns the first address that answers and accepts the amount, along with that
76
+ wallet's own LUD-06 metadata string byte for byte. A quote naming an address you did
77
+ not list is refused, so a gateway cannot put its own address in front of your payers.
76
78
  `minSendable` and `maxSendable` are both the price, so the payer's wallet offers no
77
79
  amount field and the payment is exactly what you asked for.
78
80
 
79
81
  With `to` this is the callback. The signature is checked first, and a request that was
80
82
  not signed by this service is refused before anything is minted, which is what stops a
81
- stranger making your endpoint mint invoices on wallets of their choosing. The invoice
83
+ stranger making your endpoint mint invoices on wallets of their choosing. An address
84
+ you have since taken off the list is refused too, signed or not. The invoice
82
85
  then comes from the recipient's own wallet, never from this service and never from the
83
86
  gateway, and `verify` on the answer points at that recipient's LUD-21 endpoint.
84
87
 
@@ -200,48 +203,35 @@ paths:
200
203
  operationId: verifyBankTransfer
201
204
  summary: Say whether the transfer landed, and release the preimage when it did
202
205
  description: |
203
- Mounted wherever you passed `verifyUrl` to `bankTransfer`, which puts what to look for
204
- in the query and signs it. The gateway polls this exactly as it polls a wallet's LUD-21
205
- endpoint, and it cannot tell the difference, which is the whole point.
206
+ Mounted wherever you passed `verifyUrl` to `bankTransfer`, which seals what to look for
207
+ into the query. The gateway polls this exactly as it polls a wallet's LUD-21 endpoint,
208
+ and it cannot tell the difference, which is the whole point.
206
209
 
207
- A query this service did not sign is refused. Without that check the endpoint would
208
- answer "did anyone send you this amount with this note" to whoever asked, which is a
209
- bank statement handed out one question at a time.
210
+ A query this service did not seal is refused, and so is one sealed for another
211
+ account. Without that check the endpoint would answer "did anyone send you this amount
212
+ with this note" to whoever asked, which is a bank statement handed out one question at
213
+ a time.
210
214
 
211
- The preimage is an HMAC of the reference, the amount and the currency under a secret
212
- only this service holds, so the gateway can check it hashes to what it was given and
213
- still cannot produce it. Nothing is stored between calls.
215
+ A credit pays when its amount and currency match exactly and the reference stands as a
216
+ whole word in what the payer wrote, across every field the bank puts a note in, so
217
+ noise around it is fine. A credit that also names another reference shaped like it, a
218
+ second order number of the same kind, pays neither.
219
+
220
+ The preimage is an HMAC of the account, the amount, the currency, the expiry and the
221
+ reference under a secret only this service holds, so the gateway can check it hashes to
222
+ what it was given and still cannot produce it, and a second order under the same
223
+ reference is another payment. Nothing is stored between calls.
214
224
  parameters:
215
- - name: ref
225
+ - name: q
216
226
  in: query
217
227
  required: true
218
228
  description: |
219
- What the payer had to leave on the transfer. Matched as a substring against every
220
- field the bank puts a note in, so noise around it is fine.
221
- schema:
222
- type: string
223
- - name: minor
224
- in: query
225
- required: true
226
- description: The price in the smallest unit of the currency, so 48055 is 480.55 CZK.
227
- schema:
228
- type: integer
229
- minimum: 1
230
- - name: cc
231
- in: query
232
- required: true
233
- description: The three letter currency code the credit has to be in.
229
+ One blob sealed by `bankTransfer` under the rail secret, naming the account, the
230
+ amount in minor units, the currency, the expiry and the reference. Padded to one
231
+ size class, so its length says nothing about what it carries.
234
232
  schema:
235
233
  type: string
236
- minLength: 3
237
- maxLength: 3
238
- - name: sig
239
- in: query
240
- required: true
241
- description: Hex HMAC-SHA256 over `<ref>|<minor>|<cc>`, minted by `bankTransfer`.
242
- schema:
243
- type: string
244
- pattern: "^[0-9a-f]{64}$"
234
+ pattern: "^v2\\."
245
235
  responses:
246
236
  "200":
247
237
  description: |
@@ -258,7 +248,7 @@ paths:
258
248
  schema:
259
249
  $ref: "#/components/schemas/unsettled"
260
250
  "403":
261
- description: The signature does not hold, so this service never minted the question.
251
+ description: The blob does not open under this service's secret, or was sealed for another account, so it never asked the question.
262
252
  content:
263
253
  application/json:
264
254
  schema:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thunder-bridge",
3
- "version": "1.5.0",
3
+ "version": "2.1.1",
4
4
  "description": "Trustless JavaScript client for the Thunder Bridge Lightning payment gateway. Proves the invoice came from your own wallet before the payer sees it.",
5
5
  "author": "i-am-fatik",
6
6
  "homepage": "https://agora.gripe/en/tools/thunder-bridge",
@@ -1,92 +0,0 @@
1
- /** One incoming payment as the bank booked it, in the smallest unit of its currency */
2
- interface Credit {
3
- amountMinor: number;
4
- currency: string;
5
- /** Whatever the payer wrote, wherever this bank puts it. Matching is a substring, so noise around it is fine */
6
- reference: string;
7
- /**
8
- * Unix seconds. A bank that books a day rather than an instant, as Fio does,
9
- * gives the day's midnight in its own zone, so rendering this in UTC can show
10
- * the day before. Nothing here matches on it, it is yours to read
11
- */
12
- bookedAt: number;
13
- }
14
- /**
15
- * Recent credits on one account, oldest or newest first, it makes no difference.
16
- * This is the whole plugin seam: a bank is a function of this shape, and
17
- * `fioStatement` is one implementation of it
18
- */
19
- type Statement = (sinceUnix: number) => Promise<Credit[]>;
20
- /** One transfer to ask for: what is owed, where it lands, and where its arrival is read back from */
21
- interface BankTransferParams {
22
- /** Long lived and server side. The preimage is derived from it, so losing it loses every proof */
23
- secret: string;
24
- /** What the payer must leave on the transfer, an order id or a nonce. It is matched, not stored */
25
- reference: string;
26
- /** The price in the smallest unit, so 48055 is 480.55 CZK */
27
- amountMinor: number;
28
- /** The account the money goes to, as an IBAN */
29
- iban: string;
30
- /** Where `bankVerifyEndpoint` is mounted, a public https URL with no query of its own */
31
- verifyUrl: string;
32
- /** When the offer dies, in unix seconds. Money in a bank moves on banking days, so give it days */
33
- expiresAt: number;
34
- /** Defaults to CZK */
35
- currency?: string;
36
- /** Up to ten digits, for accounting systems that still want one */
37
- variableSymbol?: string;
38
- /**
39
- * Groups this transfer with everything else paid to the same secret, so one
40
- * `followTrigger` socket hears about it. Give the Lightning leg of the same
41
- * order the same secret and both rails arrive on one stream
42
- */
43
- trigger?: string;
44
- /** How many settlements of that trigger the gateway keeps replayable past the hour, needs `trigger` */
45
- replay?: number;
46
- /**
47
- * Handed back untouched on that stream, so a watcher learns which order settled
48
- * without asking anyone. `seal` it and the gateway cannot read it either
49
- */
50
- sealed?: string;
51
- /**
52
- * Where the gateway posts once the money lands, a public https URL. Without one
53
- * a transfer is only ever learned by following the trigger or asking
54
- */
55
- webhookUrl?: string;
56
- /**
57
- * Register on a gateway you do not own anyway. The verify URL names the amount
58
- * and the reference, so its operator ends up reading your order book, and the
59
- * URL itself answers whether that order was paid. Say true only when the order
60
- * book is not worth hiding
61
- */
62
- allowPublicGateway?: boolean;
63
- }
64
- /** A transfer the gateway is now watching, and the descriptor the payer scans */
65
- interface BankTransfer {
66
- /** The watched payment's id at the gateway, which is how you read this order back */
67
- id: string;
68
- /** What the gateway was given, and what the preimage has to hash to */
69
- paymentHash: string;
70
- /** The same URL you mounted, carrying what to look for and a signature over it */
71
- verifyUrl: string;
72
- /** The payer scans this, it is a Short Payment Descriptor, the Czech QR platba format */
73
- spd: string;
74
- }
75
- /** The endpoint the gateway polls for a bank transfer, answering off your own statement */
76
- interface BankVerifyConfig {
77
- /** The same secret `bankTransfer` was given */
78
- secret: string;
79
- /** The account to read */
80
- statement: Statement;
81
- /** How far back a credit still counts, seven days by default */
82
- lookBackSecs?: number;
83
- /**
84
- * How often you want the gateway to ask, in seconds. It goes out as
85
- * `Cache-Control: max-age`, so the pace is yours to set rather than the
86
- * gateway's, and a bank that updates once a minute should say so instead of
87
- * being polled every few seconds. Thirty by default, clamped to an hour
88
- */
89
- pollEverySecs?: number;
90
- }
91
-
92
- export type { BankTransfer as B, Credit as C, Statement as S, BankTransferParams as a, BankVerifyConfig as b };
@@ -1,92 +0,0 @@
1
- /** One incoming payment as the bank booked it, in the smallest unit of its currency */
2
- interface Credit {
3
- amountMinor: number;
4
- currency: string;
5
- /** Whatever the payer wrote, wherever this bank puts it. Matching is a substring, so noise around it is fine */
6
- reference: string;
7
- /**
8
- * Unix seconds. A bank that books a day rather than an instant, as Fio does,
9
- * gives the day's midnight in its own zone, so rendering this in UTC can show
10
- * the day before. Nothing here matches on it, it is yours to read
11
- */
12
- bookedAt: number;
13
- }
14
- /**
15
- * Recent credits on one account, oldest or newest first, it makes no difference.
16
- * This is the whole plugin seam: a bank is a function of this shape, and
17
- * `fioStatement` is one implementation of it
18
- */
19
- type Statement = (sinceUnix: number) => Promise<Credit[]>;
20
- /** One transfer to ask for: what is owed, where it lands, and where its arrival is read back from */
21
- interface BankTransferParams {
22
- /** Long lived and server side. The preimage is derived from it, so losing it loses every proof */
23
- secret: string;
24
- /** What the payer must leave on the transfer, an order id or a nonce. It is matched, not stored */
25
- reference: string;
26
- /** The price in the smallest unit, so 48055 is 480.55 CZK */
27
- amountMinor: number;
28
- /** The account the money goes to, as an IBAN */
29
- iban: string;
30
- /** Where `bankVerifyEndpoint` is mounted, a public https URL with no query of its own */
31
- verifyUrl: string;
32
- /** When the offer dies, in unix seconds. Money in a bank moves on banking days, so give it days */
33
- expiresAt: number;
34
- /** Defaults to CZK */
35
- currency?: string;
36
- /** Up to ten digits, for accounting systems that still want one */
37
- variableSymbol?: string;
38
- /**
39
- * Groups this transfer with everything else paid to the same secret, so one
40
- * `followTrigger` socket hears about it. Give the Lightning leg of the same
41
- * order the same secret and both rails arrive on one stream
42
- */
43
- trigger?: string;
44
- /** How many settlements of that trigger the gateway keeps replayable past the hour, needs `trigger` */
45
- replay?: number;
46
- /**
47
- * Handed back untouched on that stream, so a watcher learns which order settled
48
- * without asking anyone. `seal` it and the gateway cannot read it either
49
- */
50
- sealed?: string;
51
- /**
52
- * Where the gateway posts once the money lands, a public https URL. Without one
53
- * a transfer is only ever learned by following the trigger or asking
54
- */
55
- webhookUrl?: string;
56
- /**
57
- * Register on a gateway you do not own anyway. The verify URL names the amount
58
- * and the reference, so its operator ends up reading your order book, and the
59
- * URL itself answers whether that order was paid. Say true only when the order
60
- * book is not worth hiding
61
- */
62
- allowPublicGateway?: boolean;
63
- }
64
- /** A transfer the gateway is now watching, and the descriptor the payer scans */
65
- interface BankTransfer {
66
- /** The watched payment's id at the gateway, which is how you read this order back */
67
- id: string;
68
- /** What the gateway was given, and what the preimage has to hash to */
69
- paymentHash: string;
70
- /** The same URL you mounted, carrying what to look for and a signature over it */
71
- verifyUrl: string;
72
- /** The payer scans this, it is a Short Payment Descriptor, the Czech QR platba format */
73
- spd: string;
74
- }
75
- /** The endpoint the gateway polls for a bank transfer, answering off your own statement */
76
- interface BankVerifyConfig {
77
- /** The same secret `bankTransfer` was given */
78
- secret: string;
79
- /** The account to read */
80
- statement: Statement;
81
- /** How far back a credit still counts, seven days by default */
82
- lookBackSecs?: number;
83
- /**
84
- * How often you want the gateway to ask, in seconds. It goes out as
85
- * `Cache-Control: max-age`, so the pace is yours to set rather than the
86
- * gateway's, and a bank that updates once a minute should say so instead of
87
- * being polled every few seconds. Thirty by default, clamped to an hour
88
- */
89
- pollEverySecs?: number;
90
- }
91
-
92
- export type { BankTransfer as B, Credit as C, Statement as S, BankTransferParams as a, BankVerifyConfig as b };