@breeztech/breez-sdk-spark 0.22.2 → 0.23.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.
Files changed (44) hide show
  1. package/breez-sdk-spark.tgz +0 -0
  2. package/bundler/breez_sdk_spark_wasm.d.ts +65 -5
  3. package/bundler/breez_sdk_spark_wasm_bg.js +66 -27
  4. package/bundler/breez_sdk_spark_wasm_bg.wasm +0 -0
  5. package/bundler/breez_sdk_spark_wasm_bg.wasm.d.ts +9 -6
  6. package/bundler/index.js +15 -2
  7. package/bundler/package.json +5 -0
  8. package/bundler/storage/index.js +6 -0
  9. package/bundler/tree-store/index.js +1518 -0
  10. package/bundler/tree-store/package.json +12 -0
  11. package/deno/breez_sdk_spark_wasm.d.ts +65 -5
  12. package/deno/breez_sdk_spark_wasm.js +66 -27
  13. package/deno/breez_sdk_spark_wasm_bg.wasm +0 -0
  14. package/deno/breez_sdk_spark_wasm_bg.wasm.d.ts +9 -6
  15. package/nodejs/breez_sdk_spark_wasm.d.ts +65 -5
  16. package/nodejs/breez_sdk_spark_wasm.js +66 -27
  17. package/nodejs/breez_sdk_spark_wasm_bg.wasm +0 -0
  18. package/nodejs/breez_sdk_spark_wasm_bg.wasm.d.ts +9 -6
  19. package/nodejs/index.js +11 -0
  20. package/nodejs/mysql-storage/index.cjs +9 -1
  21. package/nodejs/mysql-storage/migrations.cjs +6 -0
  22. package/nodejs/mysql-tree-store/index.cjs +296 -29
  23. package/nodejs/mysql-tree-store/migrations.cjs +198 -34
  24. package/nodejs/package.json +1 -0
  25. package/nodejs/postgres-storage/index.cjs +9 -1
  26. package/nodejs/postgres-storage/migrations.cjs +6 -0
  27. package/nodejs/postgres-tree-store/index.cjs +301 -40
  28. package/nodejs/postgres-tree-store/migrations.cjs +42 -0
  29. package/nodejs/storage/index.cjs +14 -3
  30. package/nodejs/storage/migrations.cjs +6 -0
  31. package/nodejs/tree-store/errors.cjs +13 -0
  32. package/nodejs/tree-store/index.cjs +1185 -0
  33. package/nodejs/tree-store/migrations.cjs +185 -0
  34. package/nodejs/tree-store/package.json +9 -0
  35. package/package.json +1 -1
  36. package/web/breez_sdk_spark_wasm.d.ts +74 -11
  37. package/web/breez_sdk_spark_wasm.js +66 -27
  38. package/web/breez_sdk_spark_wasm_bg.wasm +0 -0
  39. package/web/breez_sdk_spark_wasm_bg.wasm.d.ts +9 -6
  40. package/web/index.js +15 -2
  41. package/web/package.json +5 -0
  42. package/web/storage/index.js +6 -0
  43. package/web/tree-store/index.js +1518 -0
  44. package/web/tree-store/package.json +12 -0
@@ -9,20 +9,52 @@ const MIGRATION_LOCK_NAME = "breez_mysql_tree_store_migration_lock";
9
9
  const MIGRATION_LOCK_TIMEOUT = 60;
10
10
 
11
11
  /**
12
- * Runs a single migration step. Plain strings are run as-is; tagged objects
13
- * (`{ op: 'dropPrimaryKey', table }`, `{ op: 'dropForeignKey', table, name }`)
14
- * are guarded against partial-apply replay (and against the `Disabled`
15
- * foreign-key mode where the FK was never created) by checking
16
- * `information_schema` first. MySQL DDL implicitly commits, so if the
17
- * migration crashes between two DDL statements the version row never gets
18
- * recorded and on retry, an unguarded DROP would fail because the
19
- * constraint is already gone.
12
+ * Runs a single migration step. Plain strings must be idempotent on their own
13
+ * (e.g. `CREATE TABLE IF NOT EXISTS`, or an `ALTER` that re-applies to the same
14
+ * end state); anything whose bare form fails on replay is a tagged object
15
+ * (`dropPrimaryKey`, `dropForeignKey`, `addForeignKey`, `createIndex`,
16
+ * `addColumn`, `dropColumn`, `dropIndex`) guarded by an `information_schema`
17
+ * check (which also covers the `Disabled` foreign-key mode where the FK was
18
+ * never created). MySQL DDL implicitly commits, so if a migration crashes
19
+ * between two DDL statements the version row never gets recorded, and on retry
20
+ * an unguarded DROP/CREATE would fail because the object already exists or is
21
+ * already gone.
20
22
  */
21
23
  async function runMigrationStep(conn, step) {
22
24
  if (typeof step === "string") {
23
25
  await conn.query(step);
24
26
  return;
25
27
  }
28
+ if (step.op === "createIndex") {
29
+ if (!(await _mysqlIndexExists(conn, step.table, step.name))) {
30
+ await conn.query(
31
+ `CREATE INDEX \`${step.name}\` ON \`${step.table}\` ${step.definition}`
32
+ );
33
+ }
34
+ return;
35
+ }
36
+ if (step.op === "addColumn") {
37
+ if (!(await _mysqlColumnExists(conn, step.table, step.name))) {
38
+ await conn.query(
39
+ `ALTER TABLE \`${step.table}\` ADD COLUMN \`${step.name}\` ${step.definition}`
40
+ );
41
+ }
42
+ return;
43
+ }
44
+ if (step.op === "dropColumn") {
45
+ if (await _mysqlColumnExists(conn, step.table, step.name)) {
46
+ await conn.query(
47
+ `ALTER TABLE \`${step.table}\` DROP COLUMN \`${step.name}\``
48
+ );
49
+ }
50
+ return;
51
+ }
52
+ if (step.op === "dropIndex") {
53
+ if (await _mysqlIndexExists(conn, step.table, step.name)) {
54
+ await conn.query(`DROP INDEX \`${step.name}\` ON \`${step.table}\``);
55
+ }
56
+ return;
57
+ }
26
58
  if (step.op === "dropPrimaryKey") {
27
59
  const [rows] = await conn.query(
28
60
  `SELECT COUNT(*) AS c FROM information_schema.table_constraints
@@ -263,10 +295,24 @@ class MysqlTreeStoreMigrationManager {
263
295
  leaf_id VARCHAR(255) NOT NULL PRIMARY KEY,
264
296
  spent_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6)
265
297
  )`,
266
- `CREATE INDEX brz_idx_tree_leaves_available
267
- ON brz_tree_leaves(status, is_missing_from_operators)`,
268
- `CREATE INDEX brz_idx_tree_leaves_reservation ON brz_tree_leaves(reservation_id)`,
269
- `CREATE INDEX brz_idx_tree_leaves_added_at ON brz_tree_leaves(added_at)`,
298
+ {
299
+ op: "createIndex",
300
+ table: "brz_tree_leaves",
301
+ name: "brz_idx_tree_leaves_available",
302
+ definition: "(status, is_missing_from_operators)",
303
+ },
304
+ {
305
+ op: "createIndex",
306
+ table: "brz_tree_leaves",
307
+ name: "brz_idx_tree_leaves_reservation",
308
+ definition: "(reservation_id)",
309
+ },
310
+ {
311
+ op: "createIndex",
312
+ table: "brz_tree_leaves",
313
+ name: "brz_idx_tree_leaves_added_at",
314
+ definition: "(added_at)",
315
+ },
270
316
  ];
271
317
  if (foreignKeyModeEnforced) {
272
318
  initialSql.push({
@@ -297,13 +343,22 @@ class MysqlTreeStoreMigrationManager {
297
343
  {
298
344
  name: "Promote leaf value to BIGINT column with covering index",
299
345
  sql: [
300
- `ALTER TABLE brz_tree_leaves
301
- ADD COLUMN value BIGINT NOT NULL DEFAULT 0`,
346
+ {
347
+ op: "addColumn",
348
+ table: "brz_tree_leaves",
349
+ name: "value",
350
+ definition: "BIGINT NOT NULL DEFAULT 0",
351
+ },
302
352
  `UPDATE brz_tree_leaves
303
353
  SET value = CAST(JSON_UNQUOTE(JSON_EXTRACT(data, '$.value')) AS UNSIGNED)
304
354
  WHERE value = 0`,
305
- `CREATE INDEX brz_idx_tree_leaves_slim
306
- ON brz_tree_leaves(status, is_missing_from_operators, reservation_id, value)`,
355
+ {
356
+ op: "createIndex",
357
+ table: "brz_tree_leaves",
358
+ name: "brz_idx_tree_leaves_slim",
359
+ definition:
360
+ "(status, is_missing_from_operators, reservation_id, value)",
361
+ },
307
362
  ],
308
363
  },
309
364
  {
@@ -318,14 +373,26 @@ class MysqlTreeStoreMigrationManager {
318
373
  name: "brz_fk_tree_leaves_reservation",
319
374
  },
320
375
 
321
- // brz_tree_reservations: scope by user_id.
322
- `ALTER TABLE brz_tree_reservations ADD COLUMN user_id VARBINARY(33) NULL`,
376
+ // brz_tree_reservations: scope by user_id. The combined DROP/ADD
377
+ // PRIMARY KEY re-applies to the same composite key, so it is left raw;
378
+ // ADD COLUMN would fail on replay, so it is guarded.
379
+ {
380
+ op: "addColumn",
381
+ table: "brz_tree_reservations",
382
+ name: "user_id",
383
+ definition: "VARBINARY(33) NULL",
384
+ },
323
385
  `UPDATE brz_tree_reservations SET user_id = ${idLit} WHERE user_id IS NULL`,
324
386
  `ALTER TABLE brz_tree_reservations MODIFY COLUMN user_id VARBINARY(33) NOT NULL`,
325
387
  `ALTER TABLE brz_tree_reservations DROP PRIMARY KEY, ADD PRIMARY KEY (user_id, id)`,
326
388
 
327
389
  // brz_tree_leaves: scope by user_id, rekey, optionally re-add composite FK.
328
- `ALTER TABLE brz_tree_leaves ADD COLUMN user_id VARBINARY(33) NULL`,
390
+ {
391
+ op: "addColumn",
392
+ table: "brz_tree_leaves",
393
+ name: "user_id",
394
+ definition: "VARBINARY(33) NULL",
395
+ },
329
396
  `UPDATE brz_tree_leaves SET user_id = ${idLit} WHERE user_id IS NULL`,
330
397
  `ALTER TABLE brz_tree_leaves MODIFY COLUMN user_id VARBINARY(33) NOT NULL`,
331
398
  `ALTER TABLE brz_tree_leaves DROP PRIMARY KEY, ADD PRIMARY KEY (user_id, id)`,
@@ -339,29 +406,58 @@ class MysqlTreeStoreMigrationManager {
339
406
  },
340
407
  ]
341
408
  : []),
342
- `DROP INDEX brz_idx_tree_leaves_available ON brz_tree_leaves`,
343
- `DROP INDEX brz_idx_tree_leaves_reservation ON brz_tree_leaves`,
344
- `DROP INDEX brz_idx_tree_leaves_added_at ON brz_tree_leaves`,
345
- `DROP INDEX brz_idx_tree_leaves_slim ON brz_tree_leaves`,
346
- `CREATE INDEX brz_idx_tree_leaves_user_available
347
- ON brz_tree_leaves(user_id, status, is_missing_from_operators)`,
348
- `CREATE INDEX brz_idx_tree_leaves_user_reservation
349
- ON brz_tree_leaves(user_id, reservation_id)`,
350
- `CREATE INDEX brz_idx_tree_leaves_user_added_at ON brz_tree_leaves(user_id, added_at)`,
351
- `CREATE INDEX brz_idx_tree_leaves_user_slim
352
- ON brz_tree_leaves(user_id, status, is_missing_from_operators, reservation_id, value)`,
409
+ { op: "dropIndex", table: "brz_tree_leaves", name: "brz_idx_tree_leaves_available" },
410
+ { op: "dropIndex", table: "brz_tree_leaves", name: "brz_idx_tree_leaves_reservation" },
411
+ { op: "dropIndex", table: "brz_tree_leaves", name: "brz_idx_tree_leaves_added_at" },
412
+ { op: "dropIndex", table: "brz_tree_leaves", name: "brz_idx_tree_leaves_slim" },
413
+ {
414
+ op: "createIndex",
415
+ table: "brz_tree_leaves",
416
+ name: "brz_idx_tree_leaves_user_available",
417
+ definition: "(user_id, status, is_missing_from_operators)",
418
+ },
419
+ {
420
+ op: "createIndex",
421
+ table: "brz_tree_leaves",
422
+ name: "brz_idx_tree_leaves_user_reservation",
423
+ definition: "(user_id, reservation_id)",
424
+ },
425
+ {
426
+ op: "createIndex",
427
+ table: "brz_tree_leaves",
428
+ name: "brz_idx_tree_leaves_user_added_at",
429
+ definition: "(user_id, added_at)",
430
+ },
431
+ {
432
+ op: "createIndex",
433
+ table: "brz_tree_leaves",
434
+ name: "brz_idx_tree_leaves_user_slim",
435
+ definition:
436
+ "(user_id, status, is_missing_from_operators, reservation_id, value)",
437
+ },
353
438
 
354
439
  // brz_tree_spent_leaves: scope by user_id.
355
- `ALTER TABLE brz_tree_spent_leaves ADD COLUMN user_id VARBINARY(33) NULL`,
440
+ {
441
+ op: "addColumn",
442
+ table: "brz_tree_spent_leaves",
443
+ name: "user_id",
444
+ definition: "VARBINARY(33) NULL",
445
+ },
356
446
  `UPDATE brz_tree_spent_leaves SET user_id = ${idLit} WHERE user_id IS NULL`,
357
447
  `ALTER TABLE brz_tree_spent_leaves MODIFY COLUMN user_id VARBINARY(33) NOT NULL`,
358
448
  `ALTER TABLE brz_tree_spent_leaves DROP PRIMARY KEY, ADD PRIMARY KEY (user_id, leaf_id)`,
359
449
 
360
450
  // brz_tree_swap_status was a singleton (PK id=1, CHECK id=1). Drop the PK
361
- // and the id column, then re-key by user_id.
451
+ // and the id column, then re-key by user_id. dropPrimaryKey runs first so
452
+ // the trailing ADD PRIMARY KEY re-applies cleanly on replay.
362
453
  { op: "dropPrimaryKey", table: "brz_tree_swap_status" },
363
- `ALTER TABLE brz_tree_swap_status DROP COLUMN id`,
364
- `ALTER TABLE brz_tree_swap_status ADD COLUMN user_id VARBINARY(33) NULL`,
454
+ { op: "dropColumn", table: "brz_tree_swap_status", name: "id" },
455
+ {
456
+ op: "addColumn",
457
+ table: "brz_tree_swap_status",
458
+ name: "user_id",
459
+ definition: "VARBINARY(33) NULL",
460
+ },
365
461
  `UPDATE brz_tree_swap_status SET user_id = ${idLit} WHERE user_id IS NULL`,
366
462
  `ALTER TABLE brz_tree_swap_status MODIFY COLUMN user_id VARBINARY(33) NOT NULL`,
367
463
  `ALTER TABLE brz_tree_swap_status ADD PRIMARY KEY (user_id)`,
@@ -381,6 +477,65 @@ class MysqlTreeStoreMigrationManager {
381
477
  `ALTER TABLE brz_tree_schema_migrations MODIFY COLUMN applied_at DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6))`,
382
478
  ],
383
479
  },
480
+ {
481
+ // Mirrors Rust migration 6 in spark-mysql/src/tree_store.rs. Ancestor
482
+ // chain: intermediate nodes a leaf's exit chain walks through, kept
483
+ // separate from the spendable leaf pool and carrying no pool metadata.
484
+ // Each row is owned by the leaf it belongs to (`leaf_id` is part of the
485
+ // primary key), so a node shared by several leaves' chains stores one
486
+ // row per leaf rather than one deduplicated row. Multi-tenant from
487
+ // creation.
488
+ name: "Add ancestor chain table",
489
+ sql: [
490
+ `CREATE TABLE IF NOT EXISTS brz_tree_ancestors (
491
+ user_id VARBINARY(33) NOT NULL,
492
+ leaf_id VARCHAR(255) NOT NULL,
493
+ id VARCHAR(255) NOT NULL,
494
+ parent_node_id VARCHAR(255) NULL,
495
+ status VARCHAR(64) NOT NULL,
496
+ value BIGINT NOT NULL DEFAULT 0,
497
+ verifying_public_key VARCHAR(255) NOT NULL DEFAULT '',
498
+ data JSON NOT NULL,
499
+ PRIMARY KEY (user_id, leaf_id, id)
500
+ )`,
501
+ ],
502
+ },
503
+ {
504
+ // Promote the remaining JSON fields that queries pull out of `data`
505
+ // into dedicated columns. `value` already got its own column in the
506
+ // "Promote leaf value" migration; this adds parent_node_id,
507
+ // verifying_public_key, and signing_public_key and backfills them.
508
+ // MySQL DDL auto-commits and has no ADD COLUMN IF NOT EXISTS, so the
509
+ // ADD COLUMNs are guarded ops (information_schema-checked) and the
510
+ // backfill is guarded by `WHERE verifying_public_key = ''`, so a replay
511
+ // after a mid-migration crash is a no-op.
512
+ name: "Promote leaf JSON fields to columns",
513
+ sql: [
514
+ {
515
+ op: "addColumn",
516
+ table: "brz_tree_leaves",
517
+ name: "parent_node_id",
518
+ definition: "VARCHAR(255) NULL",
519
+ },
520
+ {
521
+ op: "addColumn",
522
+ table: "brz_tree_leaves",
523
+ name: "verifying_public_key",
524
+ definition: "VARCHAR(255) NOT NULL DEFAULT ''",
525
+ },
526
+ {
527
+ op: "addColumn",
528
+ table: "brz_tree_leaves",
529
+ name: "signing_public_key",
530
+ definition: "VARCHAR(255) NOT NULL DEFAULT ''",
531
+ },
532
+ `UPDATE brz_tree_leaves SET
533
+ parent_node_id = NULLIF(data->>'$.parent_node_id', 'null'),
534
+ verifying_public_key = data->>'$.verifying_public_key',
535
+ signing_public_key = data->>'$.signing_keyshare.public_key'
536
+ WHERE verifying_public_key = ''`,
537
+ ],
538
+ },
384
539
  ];
385
540
  }
386
541
  }
@@ -394,6 +549,15 @@ async function _mysqlTableExists(conn, tableName) {
394
549
  return Number(rows[0].c) > 0;
395
550
  }
396
551
 
552
+ async function _mysqlColumnExists(conn, tableName, columnName) {
553
+ const [rows] = await conn.query(
554
+ `SELECT COUNT(*) AS c FROM information_schema.columns
555
+ WHERE table_schema = DATABASE() AND table_name = ? AND column_name = ?`,
556
+ [tableName, columnName]
557
+ );
558
+ return Number(rows[0].c) > 0;
559
+ }
560
+
397
561
  async function _mysqlIndexExists(conn, tableName, indexName) {
398
562
  const [rows] = await conn.query(
399
563
  `SELECT COUNT(*) AS c FROM information_schema.statistics
@@ -4,6 +4,7 @@
4
4
  "breez_sdk_spark_wasm.js",
5
5
  "breez_sdk_spark_wasm.d.ts",
6
6
  "storage/",
7
+ "tree-store/",
7
8
  "postgres-storage/",
8
9
  "postgres-tree-store/",
9
10
  "postgres-token-store/",
@@ -730,7 +730,7 @@ class PostgresStorage {
730
730
  async listDeposits() {
731
731
  try {
732
732
  const result = await this.pool.query(
733
- "SELECT txid, vout, amount_sats, is_mature, claim_error, refund_tx, refund_tx_id FROM brz_unclaimed_deposits WHERE user_id = $1",
733
+ "SELECT txid, vout, amount_sats, is_mature, claim_error, refund_tx, refund_tx_id, instant_claim_status FROM brz_unclaimed_deposits WHERE user_id = $1",
734
734
  [this.identity]
735
735
  );
736
736
 
@@ -742,6 +742,7 @@ class PostgresStorage {
742
742
  claimError: row.claim_error || null,
743
743
  refundTx: row.refund_tx,
744
744
  refundTxId: row.refund_tx_id,
745
+ instantClaimStatus: row.instant_claim_status || null,
745
746
  }));
746
747
  } catch (error) {
747
748
  throw new StorageError(
@@ -767,6 +768,13 @@ class PostgresStorage {
767
768
  WHERE user_id = $3 AND txid = $4 AND vout = $5`,
768
769
  [payload.refundTx, payload.refundTxid, this.identity, txid, vout]
769
770
  );
771
+ } else if (payload.type === "instantClaim") {
772
+ await this.pool.query(
773
+ `UPDATE brz_unclaimed_deposits
774
+ SET instant_claim_status = $1
775
+ WHERE user_id = $2 AND txid = $3 AND vout = $4`,
776
+ [JSON.stringify(payload.status), this.identity, txid, vout]
777
+ );
770
778
  } else {
771
779
  throw new StorageError(`Unknown payload type: ${payload.type}`);
772
780
  }
@@ -518,6 +518,12 @@ class PostgresMigrationManager {
518
518
  ON brz_cross_chain_swaps(user_id, provider, is_terminal)`,
519
519
  ],
520
520
  },
521
+ {
522
+ name: "Add instant claim status to brz_unclaimed_deposits",
523
+ sql: [
524
+ `ALTER TABLE brz_unclaimed_deposits ADD COLUMN instant_claim_status JSONB`,
525
+ ],
526
+ },
521
527
  ];
522
528
  }
523
529
  }