@breeztech/breez-sdk-spark 0.27.0-dev1 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/breez-sdk-spark.tgz +0 -0
- package/bundler/breez_sdk_spark_wasm.d.ts +347 -95
- package/bundler/breez_sdk_spark_wasm_bg.js +210 -92
- package/bundler/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/bundler/breez_sdk_spark_wasm_bg.wasm.d.ts +28 -14
- package/bundler/storage/index.js +251 -20
- package/bundler/tree-store/index.js +39 -6
- package/deno/breez_sdk_spark_wasm.d.ts +347 -95
- package/deno/breez_sdk_spark_wasm.js +210 -92
- package/deno/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/deno/breez_sdk_spark_wasm_bg.wasm.d.ts +28 -14
- package/itest/database-drivers.test.js +45 -0
- package/nodejs/breez_sdk_spark_wasm.d.ts +347 -95
- package/nodejs/breez_sdk_spark_wasm.js +210 -92
- package/nodejs/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/nodejs/breez_sdk_spark_wasm_bg.wasm.d.ts +28 -14
- package/nodejs/index.js +6 -0
- package/nodejs/mysql-session-store/index.cjs +3 -1
- package/nodejs/mysql-storage/index.cjs +238 -20
- package/nodejs/mysql-storage/migrations.cjs +49 -1
- package/nodejs/mysql-token-store/index.cjs +3 -7
- package/nodejs/mysql-tree-store/index.cjs +26 -1
- package/nodejs/postgres-session-store/index.cjs +3 -1
- package/nodejs/postgres-storage/index.cjs +228 -19
- package/nodejs/postgres-storage/migrations.cjs +49 -0
- package/nodejs/postgres-token-store/index.cjs +3 -1
- package/nodejs/postgres-tree-store/index.cjs +25 -1
- package/nodejs/postgres-tree-store/migrations.cjs +10 -0
- package/nodejs/storage/index.cjs +210 -19
- package/nodejs/storage/migrations.cjs +38 -0
- package/nodejs/tree-store/index.cjs +35 -0
- package/package.json +1 -1
- package/web/breez_sdk_spark_wasm.d.ts +375 -109
- package/web/breez_sdk_spark_wasm.js +210 -92
- package/web/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/web/breez_sdk_spark_wasm_bg.wasm.d.ts +28 -14
- package/web/storage/index.js +251 -20
- package/web/tree-store/index.js +39 -6
|
@@ -22,16 +22,20 @@ export const bitcoinchainservicehandle_tipHeight: (a: number) => any;
|
|
|
22
22
|
export const breezsdk_addContact: (a: number, b: any) => any;
|
|
23
23
|
export const breezsdk_addEventListener: (a: number, b: any) => any;
|
|
24
24
|
export const breezsdk_authorizeLightningAddressTransfer: (a: number, b: any) => any;
|
|
25
|
+
export const breezsdk_bridgeFromCashApp: (a: number, b: any) => any;
|
|
26
|
+
export const breezsdk_bridgeToCashApp: (a: number, b: any) => any;
|
|
25
27
|
export const breezsdk_buildUnsignedBatchPackage: (a: number, b: any) => any;
|
|
26
28
|
export const breezsdk_buildUnsignedLnurlPayPackage: (a: number, b: any) => any;
|
|
29
|
+
export const breezsdk_buildUnsignedPullPackage: (a: number, b: any) => any;
|
|
27
30
|
export const breezsdk_buildUnsignedTransferPackage: (a: number, b: any) => any;
|
|
28
31
|
export const breezsdk_buyBitcoin: (a: number, b: any) => any;
|
|
29
32
|
export const breezsdk_checkLightningAddressAvailable: (a: number, b: any) => any;
|
|
30
33
|
export const breezsdk_checkMessage: (a: number, b: any) => any;
|
|
31
|
-
export const
|
|
34
|
+
export const breezsdk_checkRecoverFunds: (a: number, b: any) => any;
|
|
32
35
|
export const breezsdk_claimDeposit: (a: number, b: any) => any;
|
|
33
36
|
export const breezsdk_claimHtlcPayment: (a: number, b: any) => any;
|
|
34
37
|
export const breezsdk_claimLightningAddressTransfer: (a: number, b: any) => any;
|
|
38
|
+
export const breezsdk_createTokenAllowance: (a: number, b: any) => any;
|
|
35
39
|
export const breezsdk_deleteContact: (a: number, b: number, c: number) => any;
|
|
36
40
|
export const breezsdk_deleteLightningAddress: (a: number) => any;
|
|
37
41
|
export const breezsdk_disconnect: (a: number) => any;
|
|
@@ -50,6 +54,7 @@ export const breezsdk_listContacts: (a: number, b: any) => any;
|
|
|
50
54
|
export const breezsdk_listFiatCurrencies: (a: number) => any;
|
|
51
55
|
export const breezsdk_listFiatRates: (a: number) => any;
|
|
52
56
|
export const breezsdk_listPayments: (a: number, b: any) => any;
|
|
57
|
+
export const breezsdk_listTokenAllowances: (a: number, b: any) => any;
|
|
53
58
|
export const breezsdk_listUnclaimedDeposits: (a: number, b: any) => any;
|
|
54
59
|
export const breezsdk_listWebhooks: (a: number) => any;
|
|
55
60
|
export const breezsdk_lnurlAuth: (a: number, b: any) => any;
|
|
@@ -59,23 +64,27 @@ export const breezsdk_optimizeLeaves: (a: number, b: any) => any;
|
|
|
59
64
|
export const breezsdk_parse: (a: number, b: number, c: number) => any;
|
|
60
65
|
export const breezsdk_prepareLnurlPay: (a: number, b: any) => any;
|
|
61
66
|
export const breezsdk_preparePaymentLink: (a: number, b: any) => any;
|
|
67
|
+
export const breezsdk_preparePullPayment: (a: number, b: any) => any;
|
|
68
|
+
export const breezsdk_prepareRecoverFunds: (a: number, b: any) => any;
|
|
62
69
|
export const breezsdk_prepareSendBatch: (a: number, b: any) => any;
|
|
63
70
|
export const breezsdk_prepareSendPayment: (a: number, b: any) => any;
|
|
64
|
-
export const breezsdk_prepareUnilateralExit: (a: number, b: any) => any;
|
|
65
71
|
export const breezsdk_publishSignedLnurlPayPackage: (a: number, b: any) => any;
|
|
72
|
+
export const breezsdk_publishSignedPullPackage: (a: number, b: any) => any;
|
|
66
73
|
export const breezsdk_publishSignedTransferPackage: (a: number, b: any) => any;
|
|
74
|
+
export const breezsdk_pullPayment: (a: number, b: any) => any;
|
|
67
75
|
export const breezsdk_receivePayment: (a: number, b: any) => any;
|
|
68
76
|
export const breezsdk_recommendedFees: (a: number) => any;
|
|
77
|
+
export const breezsdk_recoverFunds: (a: number, b: any, c: number) => any;
|
|
69
78
|
export const breezsdk_refundDeposit: (a: number, b: any) => any;
|
|
70
79
|
export const breezsdk_refundPendingConversions: (a: number) => any;
|
|
71
80
|
export const breezsdk_registerLightningAddress: (a: number, b: any) => any;
|
|
72
81
|
export const breezsdk_registerWebhook: (a: number, b: any) => any;
|
|
73
82
|
export const breezsdk_removeEventListener: (a: number, b: number, c: number) => any;
|
|
83
|
+
export const breezsdk_revokeTokenAllowance: (a: number, b: any) => any;
|
|
74
84
|
export const breezsdk_sendBatch: (a: number, b: any) => any;
|
|
75
85
|
export const breezsdk_sendPayment: (a: number, b: any) => any;
|
|
76
86
|
export const breezsdk_signMessage: (a: number, b: any) => any;
|
|
77
87
|
export const breezsdk_syncWallet: (a: number, b: any) => any;
|
|
78
|
-
export const breezsdk_unilateralExit: (a: number, b: any, c: any) => any;
|
|
79
88
|
export const breezsdk_unregisterWebhook: (a: number, b: any) => any;
|
|
80
89
|
export const breezsdk_updateContact: (a: number, b: any) => any;
|
|
81
90
|
export const breezsdk_updateUserSettings: (a: number, b: any) => any;
|
|
@@ -110,6 +119,7 @@ export const externalsigningsignerhandle_signHashSchnorr: (a: number, b: number,
|
|
|
110
119
|
export const externalsparksignerhandle_getIdentityPublicKey: (a: number) => any;
|
|
111
120
|
export const externalsparksignerhandle_getPublicKeyForLeaf: (a: number, b: any) => any;
|
|
112
121
|
export const externalsparksignerhandle_getStaticDepositPublicKey: (a: number, b: number) => any;
|
|
122
|
+
export const externalsparksignerhandle_isRemote: (a: number) => number;
|
|
113
123
|
export const externalsparksignerhandle_prepareClaim: (a: number, b: any) => any;
|
|
114
124
|
export const externalsparksignerhandle_prepareLightningReceive: (a: number, b: any) => any;
|
|
115
125
|
export const externalsparksignerhandle_prepareStaticDeposit: (a: number, b: any) => any;
|
|
@@ -122,7 +132,9 @@ export const externalsparksignerhandle_signLeafRefundSpend: (a: number, b: any,
|
|
|
122
132
|
export const externalsparksignerhandle_signMessage: (a: number, b: number, c: number) => any;
|
|
123
133
|
export const externalsparksignerhandle_signSparkInvoice: (a: number, b: any) => any;
|
|
124
134
|
export const externalsparksignerhandle_signStaticDepositRefund: (a: number, b: any) => any;
|
|
135
|
+
export const externalsparksignerhandle_signWatchtowerExitRecovery: (a: number, b: any) => any;
|
|
125
136
|
export const externalsparksignerhandle_startStaticDepositRefund: (a: number, b: any) => any;
|
|
137
|
+
export const externalsparksignerhandle_startWatchtowerExitRecovery: (a: number, b: any) => any;
|
|
126
138
|
export const getSparkStatus: () => any;
|
|
127
139
|
export const initLogging: (a: any, b: number, c: number) => any;
|
|
128
140
|
export const mysqlStorage: (a: any) => number;
|
|
@@ -185,21 +197,23 @@ export const intounderlyingsource_cancel: (a: number) => void;
|
|
|
185
197
|
export const intounderlyingsource_pull: (a: number, b: any) => any;
|
|
186
198
|
export const __wbg_externalbreezsignerhandle_free: (a: number, b: number) => void;
|
|
187
199
|
export const __wbg_defaultsessionstore_free: (a: number, b: number) => void;
|
|
188
|
-
export const __wbg_externalsigningsignerhandle_free: (a: number, b: number) => void;
|
|
189
200
|
export const __wbg_externalsparksignerhandle_free: (a: number, b: number) => void;
|
|
201
|
+
export const __wbg_externalsigningsignerhandle_free: (a: number, b: number) => void;
|
|
190
202
|
export const __wbg_signingonlyexternalsigners_free: (a: number, b: number) => void;
|
|
191
203
|
export const signingonlyexternalsigners_breezSigner: (a: number) => number;
|
|
192
204
|
export const signingonlyexternalsigners_sparkSigner: (a: number) => number;
|
|
193
|
-
export const
|
|
194
|
-
export const
|
|
195
|
-
export const
|
|
196
|
-
export const
|
|
197
|
-
export const
|
|
198
|
-
export const
|
|
199
|
-
export const
|
|
200
|
-
export const
|
|
201
|
-
export const
|
|
202
|
-
export const
|
|
205
|
+
export const wasm_bindgen__convert__closures_____invoke__h6a948436da8c8a31: (a: number, b: number, c: any) => [number, number];
|
|
206
|
+
export const wasm_bindgen__convert__closures_____invoke__h6a948436da8c8a31_6: (a: number, b: number, c: any) => [number, number];
|
|
207
|
+
export const wasm_bindgen__convert__closures_____invoke__h6a948436da8c8a31_7: (a: number, b: number, c: any) => [number, number];
|
|
208
|
+
export const wasm_bindgen__convert__closures_____invoke__h6a948436da8c8a31_8: (a: number, b: number, c: any) => [number, number];
|
|
209
|
+
export const wasm_bindgen__convert__closures_____invoke__h6a948436da8c8a31_9: (a: number, b: number, c: any) => [number, number];
|
|
210
|
+
export const wasm_bindgen__convert__closures_____invoke__h118cfc65b353d9fd: (a: number, b: number, c: any, d: any) => void;
|
|
211
|
+
export const wasm_bindgen__convert__closures_____invoke__h06397208c6d2b4ed: (a: number, b: number, c: any) => void;
|
|
212
|
+
export const wasm_bindgen__convert__closures_____invoke__h06397208c6d2b4ed_2: (a: number, b: number, c: any) => void;
|
|
213
|
+
export const wasm_bindgen__convert__closures_____invoke__h06397208c6d2b4ed_3: (a: number, b: number, c: any) => void;
|
|
214
|
+
export const wasm_bindgen__convert__closures_____invoke__h06397208c6d2b4ed_4: (a: number, b: number, c: any) => void;
|
|
215
|
+
export const wasm_bindgen__convert__closures_____invoke__h06397208c6d2b4ed_5: (a: number, b: number, c: any) => void;
|
|
216
|
+
export const wasm_bindgen__convert__closures_____invoke__h02848814aa3937de: (a: number, b: number) => void;
|
|
203
217
|
export const __wbindgen_malloc: (a: number, b: number) => number;
|
|
204
218
|
export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
|
205
219
|
export const __wbindgen_free: (a: number, b: number, c: number) => void;
|
package/web/storage/index.js
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
* This provides an ES6 interface to IndexedDB storage for web browsers
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
// JSON has no BigInt, so u128 amounts are stored as decimal strings, which the
|
|
7
|
+
// SDK reads back as BigInts.
|
|
8
|
+
function toJson(value) {
|
|
9
|
+
return JSON.stringify(value, (_key, v) =>
|
|
10
|
+
typeof v === "bigint" ? v.toString() : v
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
6
14
|
class MigrationManager {
|
|
7
15
|
constructor(db, StorageError, logger = null) {
|
|
8
16
|
this.db = db;
|
|
@@ -151,7 +159,7 @@ class MigrationManager {
|
|
|
151
159
|
// sync_revision: tracks the last committed revision (from server-acknowledged
|
|
152
160
|
// or server-received records). Does NOT include pending outgoing revisions.
|
|
153
161
|
if (!db.objectStoreNames.contains("sync_revision")) {
|
|
154
|
-
|
|
162
|
+
db.createObjectStore("sync_revision", {
|
|
155
163
|
keyPath: "id",
|
|
156
164
|
});
|
|
157
165
|
transaction
|
|
@@ -279,7 +287,7 @@ class MigrationManager {
|
|
|
279
287
|
// Add default txType to token payments
|
|
280
288
|
if (details && details.type === "token" && !details.txType) {
|
|
281
289
|
details.txType = "transfer";
|
|
282
|
-
payment.details =
|
|
290
|
+
payment.details = toJson(details);
|
|
283
291
|
paymentStore.put(payment);
|
|
284
292
|
}
|
|
285
293
|
});
|
|
@@ -300,7 +308,7 @@ class MigrationManager {
|
|
|
300
308
|
syncInfo.last_synced_final_token_payment_id = null;
|
|
301
309
|
settingsStore.put({
|
|
302
310
|
key: "sync_offset",
|
|
303
|
-
value:
|
|
311
|
+
value: toJson(syncInfo),
|
|
304
312
|
});
|
|
305
313
|
} catch (e) {
|
|
306
314
|
// If parsing fails, just continue
|
|
@@ -379,7 +387,7 @@ class MigrationManager {
|
|
|
379
387
|
expiryTime: 0,
|
|
380
388
|
status: htlcStatus,
|
|
381
389
|
};
|
|
382
|
-
payment.details =
|
|
390
|
+
payment.details = toJson(details);
|
|
383
391
|
paymentStore.put(payment);
|
|
384
392
|
}
|
|
385
393
|
});
|
|
@@ -399,7 +407,7 @@ class MigrationManager {
|
|
|
399
407
|
syncInfo.offset = 0;
|
|
400
408
|
settingsStore.put({
|
|
401
409
|
key: "sync_offset",
|
|
402
|
-
value:
|
|
410
|
+
value: toJson(syncInfo),
|
|
403
411
|
});
|
|
404
412
|
} catch (e) {
|
|
405
413
|
// If parsing fails, just continue
|
|
@@ -535,7 +543,7 @@ class MigrationManager {
|
|
|
535
543
|
syncInfo.offset = 0;
|
|
536
544
|
settingsStore.put({
|
|
537
545
|
key: "sync_offset",
|
|
538
|
-
value:
|
|
546
|
+
value: toJson(syncInfo),
|
|
539
547
|
});
|
|
540
548
|
} catch (e) {
|
|
541
549
|
// If parsing fails, just continue
|
|
@@ -562,7 +570,7 @@ class MigrationManager {
|
|
|
562
570
|
: record.conversionInfo;
|
|
563
571
|
if (!ci.type) {
|
|
564
572
|
ci.type = "amm";
|
|
565
|
-
record.conversionInfo =
|
|
573
|
+
record.conversionInfo = toJson(ci);
|
|
566
574
|
cursor.update(record);
|
|
567
575
|
}
|
|
568
576
|
} catch (e) {
|
|
@@ -624,6 +632,27 @@ class MigrationManager {
|
|
|
624
632
|
}
|
|
625
633
|
},
|
|
626
634
|
},
|
|
635
|
+
{
|
|
636
|
+
// Leaf recoveries. What the chain service reported about leaves with
|
|
637
|
+
// funds to recover on-chain, and the recoveries the operators co-signed.
|
|
638
|
+
// Every block height is in chain_transactions, keyed by txid.
|
|
639
|
+
name: "Create leaf recovery stores",
|
|
640
|
+
upgrade: (db) => {
|
|
641
|
+
const keyPaths = {
|
|
642
|
+
leaf_recoveries: "leafId",
|
|
643
|
+
chain_transactions: "txid",
|
|
644
|
+
watchtower_exit_outputs: "leafId",
|
|
645
|
+
watchtower_exit_recoveries: ["leafId", "txid"],
|
|
646
|
+
watchtower_exit_spends: "leafId",
|
|
647
|
+
unilateral_exit_sweeps: "leafId",
|
|
648
|
+
};
|
|
649
|
+
for (const [name, keyPath] of Object.entries(keyPaths)) {
|
|
650
|
+
if (!db.objectStoreNames.contains(name)) {
|
|
651
|
+
db.createObjectStore(name, { keyPath });
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
},
|
|
655
|
+
},
|
|
627
656
|
];
|
|
628
657
|
}
|
|
629
658
|
}
|
|
@@ -652,7 +681,7 @@ class IndexedDBStorage {
|
|
|
652
681
|
// so existing databases depend on indices never shifting. Never insert,
|
|
653
682
|
// reorder, or delete a migration — only append. dbVersion MUST equal the
|
|
654
683
|
// number of migrations (enforced by the guard in initialize()).
|
|
655
|
-
this.dbVersion =
|
|
684
|
+
this.dbVersion = 23; // Current schema version (= migration count)
|
|
656
685
|
}
|
|
657
686
|
|
|
658
687
|
/**
|
|
@@ -976,7 +1005,7 @@ class IndexedDBStorage {
|
|
|
976
1005
|
cursorRequest.onerror = () => {
|
|
977
1006
|
reject(
|
|
978
1007
|
new StorageError(
|
|
979
|
-
`Failed to list payments (request: ${
|
|
1008
|
+
`Failed to list payments (request: ${toJson(request)}: ${cursorRequest.error?.message || "Unknown error"
|
|
980
1009
|
}`,
|
|
981
1010
|
cursorRequest.error
|
|
982
1011
|
)
|
|
@@ -1393,14 +1422,14 @@ class IndexedDBStorage {
|
|
|
1393
1422
|
paymentId,
|
|
1394
1423
|
parentPaymentId: metadata.parentPaymentId ?? existing.parentPaymentId ?? null,
|
|
1395
1424
|
lnurlPayInfo: metadata.lnurlPayInfo
|
|
1396
|
-
?
|
|
1425
|
+
? toJson(metadata.lnurlPayInfo)
|
|
1397
1426
|
: existing.lnurlPayInfo ?? null,
|
|
1398
1427
|
lnurlWithdrawInfo: metadata.lnurlWithdrawInfo
|
|
1399
|
-
?
|
|
1428
|
+
? toJson(metadata.lnurlWithdrawInfo)
|
|
1400
1429
|
: existing.lnurlWithdrawInfo ?? null,
|
|
1401
1430
|
lnurlDescription: metadata.lnurlDescription ?? existing.lnurlDescription ?? null,
|
|
1402
1431
|
conversionInfo: metadata.conversionInfo
|
|
1403
|
-
?
|
|
1432
|
+
? toJson(metadata.conversionInfo)
|
|
1404
1433
|
: existing.conversionInfo ?? null,
|
|
1405
1434
|
conversionStatus: metadata.conversionStatus ?? existing.conversionStatus ?? null,
|
|
1406
1435
|
};
|
|
@@ -1575,25 +1604,25 @@ class IndexedDBStorage {
|
|
|
1575
1604
|
return;
|
|
1576
1605
|
}
|
|
1577
1606
|
|
|
1578
|
-
|
|
1607
|
+
const updatedDeposit = { ...existingDeposit };
|
|
1579
1608
|
|
|
1580
1609
|
if (payload.type === "claimError") {
|
|
1581
|
-
updatedDeposit.claimError =
|
|
1610
|
+
updatedDeposit.claimError = toJson(payload.error);
|
|
1582
1611
|
} else if (payload.type === "refund") {
|
|
1583
1612
|
updatedDeposit.refundTx = payload.refundTx;
|
|
1584
1613
|
updatedDeposit.refundTxId = payload.refundTxid;
|
|
1585
|
-
updatedDeposit.refundState =
|
|
1614
|
+
updatedDeposit.refundState = toJson(payload.state);
|
|
1586
1615
|
updatedDeposit.claimError = null;
|
|
1587
1616
|
} else if (payload.type === "instantClaim") {
|
|
1588
|
-
updatedDeposit.instantClaimStatus =
|
|
1617
|
+
updatedDeposit.instantClaimStatus = toJson(payload.status);
|
|
1589
1618
|
} else if (payload.type === "refundBroadcastState") {
|
|
1590
1619
|
// Only while this is still the stored refund.
|
|
1591
1620
|
if (existingDeposit.refundTxId === payload.refundTxid) {
|
|
1592
|
-
updatedDeposit.refundState =
|
|
1621
|
+
updatedDeposit.refundState = toJson(payload.state);
|
|
1593
1622
|
}
|
|
1594
1623
|
} else if (payload.type === "maxClaimFee") {
|
|
1595
1624
|
updatedDeposit.maxClaimFee = payload.maxFee
|
|
1596
|
-
?
|
|
1625
|
+
? toJson(payload.maxFee)
|
|
1597
1626
|
: null;
|
|
1598
1627
|
} else {
|
|
1599
1628
|
reject(new StorageError(`Unknown payload type: ${payload.type}`));
|
|
@@ -1750,6 +1779,208 @@ class IndexedDBStorage {
|
|
|
1750
1779
|
});
|
|
1751
1780
|
}
|
|
1752
1781
|
|
|
1782
|
+
async listLeafRecoveries() {
|
|
1783
|
+
if (!this.db) {
|
|
1784
|
+
throw new StorageError("Database not initialized");
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
return new Promise((resolve, reject) => {
|
|
1788
|
+
const storeNames = [
|
|
1789
|
+
"leaf_recoveries",
|
|
1790
|
+
"chain_transactions",
|
|
1791
|
+
"watchtower_exit_outputs",
|
|
1792
|
+
"watchtower_exit_recoveries",
|
|
1793
|
+
"watchtower_exit_spends",
|
|
1794
|
+
"unilateral_exit_sweeps",
|
|
1795
|
+
];
|
|
1796
|
+
const transaction = this.db.transaction(storeNames, "readonly");
|
|
1797
|
+
const entries = {};
|
|
1798
|
+
// getAll returns the entries of a store in key order: the leaf recoveries by
|
|
1799
|
+
// leaf id, and the recoveries of a leaf by txid.
|
|
1800
|
+
for (const name of storeNames) {
|
|
1801
|
+
const request = transaction.objectStore(name).getAll();
|
|
1802
|
+
request.onsuccess = () => {
|
|
1803
|
+
entries[name] = request.result;
|
|
1804
|
+
};
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
transaction.oncomplete = () => {
|
|
1808
|
+
const blockHeights = new Map(
|
|
1809
|
+
entries.chain_transactions.map((entry) => [
|
|
1810
|
+
entry.txid,
|
|
1811
|
+
entry.blockHeight,
|
|
1812
|
+
])
|
|
1813
|
+
);
|
|
1814
|
+
const chainTransaction = (entry) =>
|
|
1815
|
+
entry && blockHeights.has(entry.txid)
|
|
1816
|
+
? { txid: entry.txid, blockHeight: blockHeights.get(entry.txid) }
|
|
1817
|
+
: null;
|
|
1818
|
+
const byLeafId = (name) =>
|
|
1819
|
+
new Map(entries[name].map((entry) => [entry.leafId, entry]));
|
|
1820
|
+
const outputs = byLeafId("watchtower_exit_outputs");
|
|
1821
|
+
const spends = byLeafId("watchtower_exit_spends");
|
|
1822
|
+
const sweeps = byLeafId("unilateral_exit_sweeps");
|
|
1823
|
+
const recoveries = new Map();
|
|
1824
|
+
for (const entry of entries.watchtower_exit_recoveries) {
|
|
1825
|
+
if (!recoveries.has(entry.leafId)) {
|
|
1826
|
+
recoveries.set(entry.leafId, []);
|
|
1827
|
+
}
|
|
1828
|
+
recoveries.get(entry.leafId).push({
|
|
1829
|
+
txid: entry.txid,
|
|
1830
|
+
transactionHex: entry.transactionHex,
|
|
1831
|
+
outputAmountSats: entry.outputAmountSats,
|
|
1832
|
+
});
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
resolve(
|
|
1836
|
+
entries.leaf_recoveries.map(({ leafId, chainCheckedAt }) => {
|
|
1837
|
+
const output = outputs.get(leafId);
|
|
1838
|
+
return {
|
|
1839
|
+
leafId,
|
|
1840
|
+
chainCheckedAt,
|
|
1841
|
+
watchtowerExitOutput:
|
|
1842
|
+
output && blockHeights.has(output.txid)
|
|
1843
|
+
? {
|
|
1844
|
+
txid: output.txid,
|
|
1845
|
+
vout: output.vout,
|
|
1846
|
+
amountSats: output.amountSats,
|
|
1847
|
+
scriptPubkey: output.scriptPubkey,
|
|
1848
|
+
blockHeight: blockHeights.get(output.txid),
|
|
1849
|
+
}
|
|
1850
|
+
: null,
|
|
1851
|
+
watchtowerExitRecoveries: recoveries.get(leafId) ?? [],
|
|
1852
|
+
watchtowerExitSpend: chainTransaction(spends.get(leafId)),
|
|
1853
|
+
unilateralExitSweep: chainTransaction(sweeps.get(leafId)),
|
|
1854
|
+
};
|
|
1855
|
+
})
|
|
1856
|
+
);
|
|
1857
|
+
};
|
|
1858
|
+
transaction.onabort = () =>
|
|
1859
|
+
reject(
|
|
1860
|
+
new StorageError(
|
|
1861
|
+
`Failed to list leaf recoveries: ${transaction.error?.message || "Unknown error"}`,
|
|
1862
|
+
transaction.error
|
|
1863
|
+
)
|
|
1864
|
+
);
|
|
1865
|
+
});
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
async updateLeafRecovery(update) {
|
|
1869
|
+
if (!this.db) {
|
|
1870
|
+
throw new StorageError("Database not initialized");
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
return new Promise((resolve, reject) => {
|
|
1874
|
+
const {
|
|
1875
|
+
leafId,
|
|
1876
|
+
chainCheckedAt,
|
|
1877
|
+
watchtowerExitOutput: output,
|
|
1878
|
+
watchtowerExitRecovery: recovery,
|
|
1879
|
+
watchtowerExitSpend: spend,
|
|
1880
|
+
unilateralExitSweep: sweep,
|
|
1881
|
+
} = update;
|
|
1882
|
+
const storeNames = ["leaf_recoveries"];
|
|
1883
|
+
if (output != null) {
|
|
1884
|
+
storeNames.push("watchtower_exit_outputs");
|
|
1885
|
+
}
|
|
1886
|
+
if (output != null || recovery != null) {
|
|
1887
|
+
storeNames.push("watchtower_exit_recoveries");
|
|
1888
|
+
}
|
|
1889
|
+
if (output != null || spend != null) {
|
|
1890
|
+
storeNames.push("watchtower_exit_spends");
|
|
1891
|
+
}
|
|
1892
|
+
if (sweep != null) {
|
|
1893
|
+
storeNames.push("unilateral_exit_sweeps");
|
|
1894
|
+
}
|
|
1895
|
+
if (output != null || spend != null || sweep != null) {
|
|
1896
|
+
storeNames.push("chain_transactions");
|
|
1897
|
+
}
|
|
1898
|
+
const transaction = this.db.transaction(storeNames, "readwrite");
|
|
1899
|
+
const store = (name) => transaction.objectStore(name);
|
|
1900
|
+
|
|
1901
|
+
transaction.oncomplete = () => resolve();
|
|
1902
|
+
transaction.onabort = () =>
|
|
1903
|
+
reject(
|
|
1904
|
+
new StorageError(
|
|
1905
|
+
`Failed to update leaf recovery '${leafId}': ${transaction.error?.message || "Unknown error"}`,
|
|
1906
|
+
transaction.error
|
|
1907
|
+
)
|
|
1908
|
+
);
|
|
1909
|
+
|
|
1910
|
+
const leafRecoveryRequest = store("leaf_recoveries").get(leafId);
|
|
1911
|
+
leafRecoveryRequest.onsuccess = () => {
|
|
1912
|
+
const stored = leafRecoveryRequest.result?.chainCheckedAt ?? null;
|
|
1913
|
+
const later =
|
|
1914
|
+
chainCheckedAt != null && (stored == null || chainCheckedAt > stored)
|
|
1915
|
+
? chainCheckedAt
|
|
1916
|
+
: stored;
|
|
1917
|
+
store("leaf_recoveries").put({ leafId, chainCheckedAt: later });
|
|
1918
|
+
};
|
|
1919
|
+
|
|
1920
|
+
const setChainTransaction = ({ txid, blockHeight }) =>
|
|
1921
|
+
store("chain_transactions").put({ txid, blockHeight });
|
|
1922
|
+
|
|
1923
|
+
const storeRecoverySpendSweep = () => {
|
|
1924
|
+
if (recovery != null) {
|
|
1925
|
+
const recoveries = store("watchtower_exit_recoveries");
|
|
1926
|
+
const keyRequest = recoveries.getKey([leafId, recovery.txid]);
|
|
1927
|
+
keyRequest.onsuccess = () => {
|
|
1928
|
+
if (keyRequest.result === undefined) {
|
|
1929
|
+
recoveries.put({
|
|
1930
|
+
leafId,
|
|
1931
|
+
txid: recovery.txid,
|
|
1932
|
+
transactionHex: recovery.transactionHex,
|
|
1933
|
+
outputAmountSats: recovery.outputAmountSats,
|
|
1934
|
+
});
|
|
1935
|
+
}
|
|
1936
|
+
};
|
|
1937
|
+
}
|
|
1938
|
+
if (spend != null) {
|
|
1939
|
+
setChainTransaction(spend);
|
|
1940
|
+
store("watchtower_exit_spends").put({ leafId, txid: spend.txid });
|
|
1941
|
+
}
|
|
1942
|
+
if (sweep != null) {
|
|
1943
|
+
setChainTransaction(sweep);
|
|
1944
|
+
store("unilateral_exit_sweeps").put({ leafId, txid: sweep.txid });
|
|
1945
|
+
}
|
|
1946
|
+
};
|
|
1947
|
+
|
|
1948
|
+
if (output == null) {
|
|
1949
|
+
storeRecoverySpendSweep();
|
|
1950
|
+
return;
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1953
|
+
const outputs = store("watchtower_exit_outputs");
|
|
1954
|
+
const outputRequest = outputs.get(leafId);
|
|
1955
|
+
outputRequest.onsuccess = () => {
|
|
1956
|
+
const stored = outputRequest.result;
|
|
1957
|
+
if (
|
|
1958
|
+
stored &&
|
|
1959
|
+
(stored.txid !== output.txid || stored.vout !== output.vout)
|
|
1960
|
+
) {
|
|
1961
|
+
// IndexedDB orders an array after every string, so the range holds
|
|
1962
|
+
// every recovery of the leaf.
|
|
1963
|
+
store("watchtower_exit_recoveries").delete(
|
|
1964
|
+
IDBKeyRange.bound([leafId], [leafId, []])
|
|
1965
|
+
);
|
|
1966
|
+
store("watchtower_exit_spends").delete(leafId);
|
|
1967
|
+
}
|
|
1968
|
+
setChainTransaction(output);
|
|
1969
|
+
outputs.put({
|
|
1970
|
+
leafId,
|
|
1971
|
+
txid: output.txid,
|
|
1972
|
+
vout: output.vout,
|
|
1973
|
+
amountSats: output.amountSats,
|
|
1974
|
+
scriptPubkey: output.scriptPubkey,
|
|
1975
|
+
});
|
|
1976
|
+
// The browser runs a transaction's requests in the order this method
|
|
1977
|
+
// makes them, so it stores the recovery and the spend of this update
|
|
1978
|
+
// after it has deleted those of the output replaced.
|
|
1979
|
+
storeRecoverySpendSweep();
|
|
1980
|
+
};
|
|
1981
|
+
});
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1753
1984
|
async setLnurlMetadata(metadata) {
|
|
1754
1985
|
if (!this.db) {
|
|
1755
1986
|
throw new StorageError("Database not initialized");
|
|
@@ -2489,8 +2720,8 @@ class IndexedDBStorage {
|
|
|
2489
2720
|
// Ensure details and method are serialized properly
|
|
2490
2721
|
return {
|
|
2491
2722
|
...payment,
|
|
2492
|
-
details: payment.details ?
|
|
2493
|
-
method: payment.method ?
|
|
2723
|
+
details: payment.details ? toJson(payment.details) : null,
|
|
2724
|
+
method: payment.method ? toJson(payment.method) : null,
|
|
2494
2725
|
};
|
|
2495
2726
|
}
|
|
2496
2727
|
|
package/web/tree-store/index.js
CHANGED
|
@@ -21,7 +21,7 @@ const RESERVATION_TIMEOUT_MS = 300 * 1000; // 5 minutes
|
|
|
21
21
|
/** Spent-leaf markers older than this (relative to a refresh) are pruned. */
|
|
22
22
|
const SPENT_MARKER_CLEANUP_THRESHOLD_MS = 5 * 60 * 1000; // 5 minutes
|
|
23
23
|
|
|
24
|
-
const DB_VERSION =
|
|
24
|
+
const DB_VERSION = 6;
|
|
25
25
|
|
|
26
26
|
const STORE_LEAVES = "leaves";
|
|
27
27
|
const STORE_ANCESTORS = "ancestors";
|
|
@@ -111,6 +111,7 @@ class WebTreeStore {
|
|
|
111
111
|
leaves.createIndex("chain_complete", "chain_complete", {
|
|
112
112
|
unique: false,
|
|
113
113
|
});
|
|
114
|
+
leaves.createIndex("status", "status", { unique: false });
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
// Intermediate exit-chain nodes, kept separate from the leaf pool and
|
|
@@ -134,8 +135,10 @@ class WebTreeStore {
|
|
|
134
135
|
// An existing wallet gets the flag derived from the ancestor rows it
|
|
135
136
|
// already holds, so it does not refetch every chain. The index is
|
|
136
137
|
// created here and populated by the rewrite below, since createIndex
|
|
137
|
-
// cannot run from an async callback.
|
|
138
|
-
|
|
138
|
+
// cannot run from an async callback. The flag has had its current
|
|
139
|
+
// meaning since v5, so a database already at v5 or later keeps the
|
|
140
|
+
// flags it holds.
|
|
141
|
+
if (event.oldVersion > 0 && event.oldVersion < 5) {
|
|
139
142
|
const tx = event.target.transaction;
|
|
140
143
|
const leaves = tx.objectStore(STORE_LEAVES);
|
|
141
144
|
if (!leaves.indexNames.contains("chain_complete")) {
|
|
@@ -168,6 +171,14 @@ class WebTreeStore {
|
|
|
168
171
|
};
|
|
169
172
|
}
|
|
170
173
|
|
|
174
|
+
// Every leaf row already carries its status, and IndexedDB builds a new
|
|
175
|
+
// index from the rows a store holds, so this needs no rewrite.
|
|
176
|
+
if (event.oldVersion > 0 && event.oldVersion < 6) {
|
|
177
|
+
event.target.transaction
|
|
178
|
+
.objectStore(STORE_LEAVES)
|
|
179
|
+
.createIndex("status", "status", { unique: false });
|
|
180
|
+
}
|
|
181
|
+
|
|
171
182
|
if (!db.objectStoreNames.contains(STORE_RESERVATIONS)) {
|
|
172
183
|
db.createObjectStore(STORE_RESERVATIONS, { keyPath: "id" });
|
|
173
184
|
}
|
|
@@ -483,6 +494,31 @@ class WebTreeStore {
|
|
|
483
494
|
}
|
|
484
495
|
}
|
|
485
496
|
|
|
497
|
+
async getLeavesWithStatus(statuses) {
|
|
498
|
+
try {
|
|
499
|
+
const distinct = [...new Set(statuses)];
|
|
500
|
+
if (distinct.length === 0) return [];
|
|
501
|
+
return await this._txRun(
|
|
502
|
+
[STORE_LEAVES],
|
|
503
|
+
"readonly",
|
|
504
|
+
distinct.map((status) => ({
|
|
505
|
+
name: status,
|
|
506
|
+
store: STORE_LEAVES,
|
|
507
|
+
index: "status",
|
|
508
|
+
key: status,
|
|
509
|
+
all: true,
|
|
510
|
+
})),
|
|
511
|
+
(res) => distinct.flatMap((status) => res[status].map((row) => row.data))
|
|
512
|
+
);
|
|
513
|
+
} catch (error) {
|
|
514
|
+
if (error instanceof TreeStoreError) throw error;
|
|
515
|
+
throw new TreeStoreError(
|
|
516
|
+
`Failed to get leaves with status: ${error.message}`,
|
|
517
|
+
error
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
486
522
|
async getAvailableBalance() {
|
|
487
523
|
try {
|
|
488
524
|
return await this._txRun(
|
|
@@ -940,9 +976,6 @@ class WebTreeStore {
|
|
|
940
976
|
}
|
|
941
977
|
}
|
|
942
978
|
|
|
943
|
-
|
|
944
|
-
const leafNodes = change.concat(reserved);
|
|
945
|
-
|
|
946
979
|
// Change leaves go back to the available pool.
|
|
947
980
|
for (const p of change) {
|
|
948
981
|
const row = this._leafRow(p, false, leafMap.get(p.id));
|