@breeztech/breez-sdk-spark 0.13.5 → 0.13.6

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.
Binary file
@@ -449,7 +449,7 @@ export interface AesSuccessActionData {
449
449
 
450
450
  export type InputType = ({ type: "bitcoinAddress" } & BitcoinAddressDetails) | ({ type: "bolt11Invoice" } & Bolt11InvoiceDetails) | ({ type: "bolt12Invoice" } & Bolt12InvoiceDetails) | ({ type: "bolt12Offer" } & Bolt12OfferDetails) | ({ type: "lightningAddress" } & LightningAddressDetails) | ({ type: "lnurlPay" } & LnurlPayRequestDetails) | ({ type: "silentPaymentAddress" } & SilentPaymentAddressDetails) | ({ type: "lnurlAuth" } & LnurlAuthRequestDetails) | ({ type: "url" } & string) | ({ type: "bip21" } & Bip21Details) | ({ type: "bolt12InvoiceRequest" } & Bolt12InvoiceRequestDetails) | ({ type: "lnurlWithdraw" } & LnurlWithdrawRequestDetails) | ({ type: "sparkAddress" } & SparkAddressDetails) | ({ type: "sparkInvoice" } & SparkInvoiceDetails);
451
451
 
452
- export type BuyBitcoinRequest = { type: "moonpay"; lockedAmountSat?: number; redirectUrl?: string } | { type: "cashApp"; amountSats?: number };
452
+ export type BuyBitcoinRequest = { type: "moonpay"; lockedAmountSat?: number; redirectUrl?: string } | { type: "cashApp"; amountSats: number };
453
453
 
454
454
  export interface SetLnurlMetadataItem {
455
455
  paymentHash: string;
@@ -449,7 +449,7 @@ export interface AesSuccessActionData {
449
449
 
450
450
  export type InputType = ({ type: "bitcoinAddress" } & BitcoinAddressDetails) | ({ type: "bolt11Invoice" } & Bolt11InvoiceDetails) | ({ type: "bolt12Invoice" } & Bolt12InvoiceDetails) | ({ type: "bolt12Offer" } & Bolt12OfferDetails) | ({ type: "lightningAddress" } & LightningAddressDetails) | ({ type: "lnurlPay" } & LnurlPayRequestDetails) | ({ type: "silentPaymentAddress" } & SilentPaymentAddressDetails) | ({ type: "lnurlAuth" } & LnurlAuthRequestDetails) | ({ type: "url" } & string) | ({ type: "bip21" } & Bip21Details) | ({ type: "bolt12InvoiceRequest" } & Bolt12InvoiceRequestDetails) | ({ type: "lnurlWithdraw" } & LnurlWithdrawRequestDetails) | ({ type: "sparkAddress" } & SparkAddressDetails) | ({ type: "sparkInvoice" } & SparkInvoiceDetails);
451
451
 
452
- export type BuyBitcoinRequest = { type: "moonpay"; lockedAmountSat?: number; redirectUrl?: string } | { type: "cashApp"; amountSats?: number };
452
+ export type BuyBitcoinRequest = { type: "moonpay"; lockedAmountSat?: number; redirectUrl?: string } | { type: "cashApp"; amountSats: number };
453
453
 
454
454
  export interface SetLnurlMetadataItem {
455
455
  paymentHash: string;
Binary file
@@ -449,7 +449,7 @@ export interface AesSuccessActionData {
449
449
 
450
450
  export type InputType = ({ type: "bitcoinAddress" } & BitcoinAddressDetails) | ({ type: "bolt11Invoice" } & Bolt11InvoiceDetails) | ({ type: "bolt12Invoice" } & Bolt12InvoiceDetails) | ({ type: "bolt12Offer" } & Bolt12OfferDetails) | ({ type: "lightningAddress" } & LightningAddressDetails) | ({ type: "lnurlPay" } & LnurlPayRequestDetails) | ({ type: "silentPaymentAddress" } & SilentPaymentAddressDetails) | ({ type: "lnurlAuth" } & LnurlAuthRequestDetails) | ({ type: "url" } & string) | ({ type: "bip21" } & Bip21Details) | ({ type: "bolt12InvoiceRequest" } & Bolt12InvoiceRequestDetails) | ({ type: "lnurlWithdraw" } & LnurlWithdrawRequestDetails) | ({ type: "sparkAddress" } & SparkAddressDetails) | ({ type: "sparkInvoice" } & SparkInvoiceDetails);
451
451
 
452
- export type BuyBitcoinRequest = { type: "moonpay"; lockedAmountSat?: number; redirectUrl?: string } | { type: "cashApp"; amountSats?: number };
452
+ export type BuyBitcoinRequest = { type: "moonpay"; lockedAmountSat?: number; redirectUrl?: string } | { type: "cashApp"; amountSats: number };
453
453
 
454
454
  export interface SetLnurlMetadataItem {
455
455
  paymentHash: string;
Binary file
@@ -36,6 +36,12 @@ const TOKEN_STORE_WRITE_LOCK_KEY = "8390042714201347154"; // 0x746F6B656E535452
36
36
  */
37
37
  const SPENT_MARKER_CLEANUP_THRESHOLD_MS = 5 * 60 * 1000; // 5 minutes
38
38
 
39
+ /**
40
+ * Reservations whose created_at is older than this are considered stale and are
41
+ * dropped at the start of setTokensOutputs. Matches the tree store's timeout.
42
+ */
43
+ const RESERVATION_TIMEOUT_SECS = 300; // 5 minutes
44
+
39
45
  class PostgresTokenStore {
40
46
  constructor(pool, logger = null) {
41
47
  this.pool = pool;
@@ -102,6 +108,13 @@ class PostgresTokenStore {
102
108
  const refreshTimestamp = new Date(refreshStartedAtMs);
103
109
 
104
110
  await this._withWriteTransaction(async (client) => {
111
+ // Drop expired reservations BEFORE evaluating has_active_swap, otherwise a stale
112
+ // Swap reservation (from a crashed client or a swap whose finalize/cancel never
113
+ // ran) keeps has_active_swap true forever, which makes setTokensOutputs
114
+ // early-return and never reach any subsequent reconciliation. The reservation
115
+ // pins itself in place and the local token-output set freezes.
116
+ await this._cleanupStaleReservations(client);
117
+
105
118
  // Skip if swap is active or completed during this refresh
106
119
  const swapCheckResult = await client.query(
107
120
  `SELECT
@@ -725,6 +738,19 @@ class PostgresTokenStore {
725
738
  });
726
739
  }
727
740
 
741
+ /**
742
+ * Delete reservations that have exceeded the timeout.
743
+ * Called during setTokensOutputs to clean up stale reservations from crashed clients.
744
+ * The ON DELETE SET NULL foreign key constraint automatically releases the outputs.
745
+ */
746
+ async _cleanupStaleReservations(client) {
747
+ await client.query(
748
+ `DELETE FROM token_reservations
749
+ WHERE created_at < NOW() - make_interval(secs => $1)`,
750
+ [RESERVATION_TIMEOUT_SECS]
751
+ );
752
+ }
753
+
728
754
  /**
729
755
  * Upsert token metadata.
730
756
  */
@@ -189,6 +189,12 @@ class PostgresTreeStore {
189
189
  await this._withWriteTransaction(async (client) => {
190
190
  const refreshTimestamp = new Date(refreshStartedAtMs);
191
191
 
192
+ // Drop expired reservations BEFORE evaluating has_active_swap, otherwise a stale
193
+ // Swap reservation (from a crashed client or a swap whose finalize/cancel never
194
+ // ran) keeps has_active_swap true forever, which makes set_leaves early-return
195
+ // and never reach the cleanup again. The reservation pins itself in place.
196
+ await this._cleanupStaleReservations(client);
197
+
192
198
  // Check for active swap or swap completed during refresh
193
199
  const swapCheckResult = await client.query(`
194
200
  SELECT
@@ -211,15 +217,15 @@ class PostgresTreeStore {
211
217
  );
212
218
  const spentIds = new Set(spentResult.rows.map((r) => r.leaf_id));
213
219
 
214
- // Delete non-reserved leaves added before refresh started
220
+ // Delete non-reserved leaves added before refresh started.
221
+ // Includes leaves released earlier in this transaction by _cleanupStaleReservations
222
+ // (FK ON DELETE SET NULL) — those rows kept their old added_at, so they are
223
+ // dropped here and re-fetched from the operator response in the upsert below.
215
224
  await client.query(
216
225
  "DELETE FROM tree_leaves WHERE reservation_id IS NULL AND added_at < $1",
217
226
  [refreshTimestamp]
218
227
  );
219
228
 
220
- // Clean up stale reservations (after leaf delete)
221
- await this._cleanupStaleReservations(client);
222
-
223
229
  // Upsert all leaves (filtering spent)
224
230
  await this._batchUpsertLeaves(client, leaves, false, spentIds);
225
231
  await this._batchUpsertLeaves(client, missingLeaves, true, spentIds);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@breeztech/breez-sdk-spark",
3
- "version": "0.13.5",
3
+ "version": "0.13.6",
4
4
  "description": "Breez Spark SDK",
5
5
  "repository": "https://github.com/breez/spark-sdk",
6
6
  "author": "Breez <contact@breez.technology> (https://github.com/breez)",
@@ -449,7 +449,7 @@ export interface AesSuccessActionData {
449
449
 
450
450
  export type InputType = ({ type: "bitcoinAddress" } & BitcoinAddressDetails) | ({ type: "bolt11Invoice" } & Bolt11InvoiceDetails) | ({ type: "bolt12Invoice" } & Bolt12InvoiceDetails) | ({ type: "bolt12Offer" } & Bolt12OfferDetails) | ({ type: "lightningAddress" } & LightningAddressDetails) | ({ type: "lnurlPay" } & LnurlPayRequestDetails) | ({ type: "silentPaymentAddress" } & SilentPaymentAddressDetails) | ({ type: "lnurlAuth" } & LnurlAuthRequestDetails) | ({ type: "url" } & string) | ({ type: "bip21" } & Bip21Details) | ({ type: "bolt12InvoiceRequest" } & Bolt12InvoiceRequestDetails) | ({ type: "lnurlWithdraw" } & LnurlWithdrawRequestDetails) | ({ type: "sparkAddress" } & SparkAddressDetails) | ({ type: "sparkInvoice" } & SparkInvoiceDetails);
451
451
 
452
- export type BuyBitcoinRequest = { type: "moonpay"; lockedAmountSat?: number; redirectUrl?: string } | { type: "cashApp"; amountSats?: number };
452
+ export type BuyBitcoinRequest = { type: "moonpay"; lockedAmountSat?: number; redirectUrl?: string } | { type: "cashApp"; amountSats: number };
453
453
 
454
454
  export interface SetLnurlMetadataItem {
455
455
  paymentHash: string;
Binary file