@breeztech/breez-sdk-spark 0.12.2-dev1 → 0.12.2-dev3
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/README.md +4 -4
- package/breez-sdk-spark.tgz +0 -0
- package/bundler/breez_sdk_spark_wasm.d.ts +731 -601
- package/bundler/breez_sdk_spark_wasm_bg.js +151 -54
- package/bundler/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/bundler/breez_sdk_spark_wasm_bg.wasm.d.ts +7 -5
- package/bundler/storage/index.js +65 -43
- package/deno/breez_sdk_spark_wasm.d.ts +731 -601
- package/deno/breez_sdk_spark_wasm.js +141 -54
- package/deno/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/deno/breez_sdk_spark_wasm_bg.wasm.d.ts +7 -5
- package/nodejs/breez_sdk_spark_wasm.d.ts +731 -601
- package/nodejs/breez_sdk_spark_wasm.js +151 -54
- package/nodejs/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/nodejs/breez_sdk_spark_wasm_bg.wasm.d.ts +7 -5
- package/nodejs/index.js +16 -2
- package/nodejs/package.json +1 -0
- package/nodejs/postgres-storage/index.cjs +42 -31
- package/nodejs/postgres-storage/migrations.cjs +24 -0
- package/nodejs/postgres-token-store/errors.cjs +13 -0
- package/nodejs/postgres-token-store/index.cjs +857 -0
- package/nodejs/postgres-token-store/migrations.cjs +163 -0
- package/nodejs/postgres-token-store/package.json +9 -0
- package/nodejs/postgres-tree-store/index.cjs +12 -2
- package/nodejs/storage/index.cjs +19 -28
- package/nodejs/storage/migrations.cjs +18 -0
- package/package.json +1 -1
- package/web/breez_sdk_spark_wasm.d.ts +738 -606
- package/web/breez_sdk_spark_wasm.js +141 -54
- package/web/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/web/breez_sdk_spark_wasm_bg.wasm.d.ts +7 -5
- package/web/storage/index.js +65 -43
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database Migration Manager for Breez SDK PostgreSQL Token Store
|
|
3
|
+
*
|
|
4
|
+
* Uses a token_schema_migrations table + pg_advisory_xact_lock to safely run
|
|
5
|
+
* migrations from concurrent processes.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { TokenStoreError } = require("./errors.cjs");
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Advisory lock ID for token store migrations.
|
|
12
|
+
* Uses a different lock ID from the storage/tree store migrations to avoid contention.
|
|
13
|
+
* Derived from ASCII bytes of "TOKN" (0x544F4B4E).
|
|
14
|
+
*/
|
|
15
|
+
const MIGRATION_LOCK_ID = "1414022990"; // 0x544F4B4E as decimal string
|
|
16
|
+
|
|
17
|
+
class TokenStoreMigrationManager {
|
|
18
|
+
constructor(logger = null) {
|
|
19
|
+
this.logger = logger;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Run all pending migrations inside a single transaction with an advisory lock.
|
|
24
|
+
* @param {import('pg').Pool} pool
|
|
25
|
+
*/
|
|
26
|
+
async migrate(pool) {
|
|
27
|
+
const client = await pool.connect();
|
|
28
|
+
try {
|
|
29
|
+
await client.query("BEGIN");
|
|
30
|
+
|
|
31
|
+
// Transaction-level advisory lock — automatically released on COMMIT/ROLLBACK
|
|
32
|
+
await client.query(`SELECT pg_advisory_xact_lock(${MIGRATION_LOCK_ID})`);
|
|
33
|
+
|
|
34
|
+
// Create the migrations tracking table if needed
|
|
35
|
+
await client.query(`
|
|
36
|
+
CREATE TABLE IF NOT EXISTS token_schema_migrations (
|
|
37
|
+
version INTEGER PRIMARY KEY,
|
|
38
|
+
applied_at TIMESTAMPTZ DEFAULT NOW()
|
|
39
|
+
)
|
|
40
|
+
`);
|
|
41
|
+
|
|
42
|
+
// Get current version
|
|
43
|
+
const versionResult = await client.query(
|
|
44
|
+
"SELECT COALESCE(MAX(version), 0) AS version FROM token_schema_migrations"
|
|
45
|
+
);
|
|
46
|
+
const currentVersion = versionResult.rows[0].version;
|
|
47
|
+
|
|
48
|
+
const migrations = this._getMigrations();
|
|
49
|
+
|
|
50
|
+
if (currentVersion >= migrations.length) {
|
|
51
|
+
this._log("info", `Token store database is up to date (version ${currentVersion})`);
|
|
52
|
+
await client.query("COMMIT");
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
this._log(
|
|
57
|
+
"info",
|
|
58
|
+
`Migrating token store database from version ${currentVersion} to ${migrations.length}`
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
for (let i = currentVersion; i < migrations.length; i++) {
|
|
62
|
+
const migration = migrations[i];
|
|
63
|
+
const version = i + 1;
|
|
64
|
+
this._log("debug", `Running token store migration ${version}: ${migration.name}`);
|
|
65
|
+
|
|
66
|
+
for (const sql of migration.sql) {
|
|
67
|
+
await client.query(sql);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
await client.query(
|
|
71
|
+
"INSERT INTO token_schema_migrations (version) VALUES ($1)",
|
|
72
|
+
[version]
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
await client.query("COMMIT");
|
|
77
|
+
this._log("info", "Token store database migration completed successfully");
|
|
78
|
+
} catch (error) {
|
|
79
|
+
await client.query("ROLLBACK").catch(() => {});
|
|
80
|
+
throw new TokenStoreError(
|
|
81
|
+
`Token store migration failed: ${error.message}`,
|
|
82
|
+
error
|
|
83
|
+
);
|
|
84
|
+
} finally {
|
|
85
|
+
client.release();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
_log(level, message) {
|
|
90
|
+
if (this.logger && typeof this.logger.log === "function") {
|
|
91
|
+
this.logger.log({ line: message, level });
|
|
92
|
+
} else if (level === "error") {
|
|
93
|
+
console.error(`[TokenStoreMigrationManager] ${message}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Migrations matching the Rust PostgresTokenStore schema exactly.
|
|
99
|
+
*/
|
|
100
|
+
_getMigrations() {
|
|
101
|
+
return [
|
|
102
|
+
{
|
|
103
|
+
name: "Create token store tables with race condition protection",
|
|
104
|
+
sql: [
|
|
105
|
+
`CREATE TABLE IF NOT EXISTS token_metadata (
|
|
106
|
+
identifier TEXT PRIMARY KEY,
|
|
107
|
+
issuer_public_key TEXT NOT NULL,
|
|
108
|
+
name TEXT NOT NULL,
|
|
109
|
+
ticker TEXT NOT NULL,
|
|
110
|
+
decimals INTEGER NOT NULL,
|
|
111
|
+
max_supply TEXT NOT NULL,
|
|
112
|
+
is_freezable BOOLEAN NOT NULL,
|
|
113
|
+
creation_entity_public_key TEXT
|
|
114
|
+
)`,
|
|
115
|
+
|
|
116
|
+
`CREATE INDEX IF NOT EXISTS idx_token_metadata_issuer_pk
|
|
117
|
+
ON token_metadata (issuer_public_key)`,
|
|
118
|
+
|
|
119
|
+
`CREATE TABLE IF NOT EXISTS token_reservations (
|
|
120
|
+
id TEXT PRIMARY KEY,
|
|
121
|
+
purpose TEXT NOT NULL,
|
|
122
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
123
|
+
)`,
|
|
124
|
+
|
|
125
|
+
`CREATE TABLE IF NOT EXISTS token_outputs (
|
|
126
|
+
id TEXT PRIMARY KEY,
|
|
127
|
+
token_identifier TEXT NOT NULL REFERENCES token_metadata(identifier),
|
|
128
|
+
owner_public_key TEXT NOT NULL,
|
|
129
|
+
revocation_commitment TEXT NOT NULL,
|
|
130
|
+
withdraw_bond_sats BIGINT NOT NULL,
|
|
131
|
+
withdraw_relative_block_locktime BIGINT NOT NULL,
|
|
132
|
+
token_public_key TEXT,
|
|
133
|
+
token_amount TEXT NOT NULL,
|
|
134
|
+
prev_tx_hash TEXT NOT NULL,
|
|
135
|
+
prev_tx_vout INTEGER NOT NULL,
|
|
136
|
+
reservation_id TEXT REFERENCES token_reservations(id) ON DELETE SET NULL,
|
|
137
|
+
added_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
138
|
+
)`,
|
|
139
|
+
|
|
140
|
+
`CREATE INDEX IF NOT EXISTS idx_token_outputs_identifier
|
|
141
|
+
ON token_outputs (token_identifier)`,
|
|
142
|
+
|
|
143
|
+
`CREATE INDEX IF NOT EXISTS idx_token_outputs_reservation
|
|
144
|
+
ON token_outputs (reservation_id) WHERE reservation_id IS NOT NULL`,
|
|
145
|
+
|
|
146
|
+
`CREATE TABLE IF NOT EXISTS token_spent_outputs (
|
|
147
|
+
output_id TEXT PRIMARY KEY,
|
|
148
|
+
spent_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
149
|
+
)`,
|
|
150
|
+
|
|
151
|
+
`CREATE TABLE IF NOT EXISTS token_swap_status (
|
|
152
|
+
id INTEGER PRIMARY KEY DEFAULT 1 CHECK (id = 1),
|
|
153
|
+
last_completed_at TIMESTAMPTZ
|
|
154
|
+
)`,
|
|
155
|
+
|
|
156
|
+
`INSERT INTO token_swap_status (id) VALUES (1) ON CONFLICT DO NOTHING`,
|
|
157
|
+
],
|
|
158
|
+
},
|
|
159
|
+
];
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
module.exports = { TokenStoreMigrationManager };
|
|
@@ -28,7 +28,7 @@ const { TreeStoreMigrationManager } = require("./migrations.cjs");
|
|
|
28
28
|
* Advisory lock key for serializing tree store write operations.
|
|
29
29
|
* Matches the Rust constant TREE_STORE_WRITE_LOCK_KEY = 0x7472_6565_5354_4f52
|
|
30
30
|
*/
|
|
31
|
-
const TREE_STORE_WRITE_LOCK_KEY = "
|
|
31
|
+
const TREE_STORE_WRITE_LOCK_KEY = "8390880541608791890"; // 0x7472656553544f52 as decimal string
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* Timeout for reservations in seconds. Reservations older than this are stale.
|
|
@@ -789,10 +789,20 @@ async function createPostgresTreeStore(config, logger = null) {
|
|
|
789
789
|
connectionTimeoutMillis: config.createTimeoutSecs * 1000,
|
|
790
790
|
idleTimeoutMillis: config.recycleTimeoutSecs * 1000,
|
|
791
791
|
});
|
|
792
|
+
return createPostgresTreeStoreWithPool(pool, logger);
|
|
793
|
+
}
|
|
792
794
|
|
|
795
|
+
/**
|
|
796
|
+
* Create a PostgresTreeStore instance from an existing pg.Pool.
|
|
797
|
+
*
|
|
798
|
+
* @param {pg.Pool} pool - An existing connection pool
|
|
799
|
+
* @param {object} [logger] - Optional logger
|
|
800
|
+
* @returns {Promise<PostgresTreeStore>}
|
|
801
|
+
*/
|
|
802
|
+
async function createPostgresTreeStoreWithPool(pool, logger = null) {
|
|
793
803
|
const store = new PostgresTreeStore(pool, logger);
|
|
794
804
|
await store.initialize();
|
|
795
805
|
return store;
|
|
796
806
|
}
|
|
797
807
|
|
|
798
|
-
module.exports = { PostgresTreeStore, createPostgresTreeStore, TreeStoreError };
|
|
808
|
+
module.exports = { PostgresTreeStore, createPostgresTreeStore, createPostgresTreeStoreWithPool, TreeStoreError };
|
package/nodejs/storage/index.cjs
CHANGED
|
@@ -53,6 +53,7 @@ const SELECT_PAYMENT_SQL = `
|
|
|
53
53
|
pm.lnurl_pay_info,
|
|
54
54
|
pm.lnurl_withdraw_info,
|
|
55
55
|
pm.conversion_info,
|
|
56
|
+
pm.conversion_status,
|
|
56
57
|
t.metadata AS token_metadata,
|
|
57
58
|
t.tx_hash AS token_tx_hash,
|
|
58
59
|
t.tx_type AS token_tx_type,
|
|
@@ -260,21 +261,6 @@ class SqliteStorage {
|
|
|
260
261
|
paymentDetailsClauses.push("t.tx_type = ?");
|
|
261
262
|
params.push(paymentDetailsFilter.txType);
|
|
262
263
|
}
|
|
263
|
-
// Filter by LNURL preimage status
|
|
264
|
-
if (
|
|
265
|
-
paymentDetailsFilter.type === "lightning" &&
|
|
266
|
-
paymentDetailsFilter.hasLnurlPreimage !== undefined
|
|
267
|
-
) {
|
|
268
|
-
if (paymentDetailsFilter.hasLnurlPreimage) {
|
|
269
|
-
paymentDetailsClauses.push("lrm.preimage IS NOT NULL");
|
|
270
|
-
} else {
|
|
271
|
-
paymentDetailsClauses.push(
|
|
272
|
-
"lrm.payment_hash IS NOT NULL AND l.preimage IS NOT NULL AND lrm.preimage IS NULL"
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
|
|
278
264
|
if (paymentDetailsClauses.length > 0) {
|
|
279
265
|
allPaymentDetailsClauses.push(`(${paymentDetailsClauses.join(" AND ")})`);
|
|
280
266
|
}
|
|
@@ -560,14 +546,15 @@ class SqliteStorage {
|
|
|
560
546
|
insertPaymentMetadata(paymentId, metadata) {
|
|
561
547
|
try {
|
|
562
548
|
const stmt = this.db.prepare(`
|
|
563
|
-
INSERT INTO payment_metadata (payment_id, parent_payment_id, lnurl_pay_info, lnurl_withdraw_info, lnurl_description, conversion_info)
|
|
564
|
-
VALUES (?, ?, ?, ?, ?, ?)
|
|
549
|
+
INSERT INTO payment_metadata (payment_id, parent_payment_id, lnurl_pay_info, lnurl_withdraw_info, lnurl_description, conversion_info, conversion_status)
|
|
550
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
565
551
|
ON CONFLICT(payment_id) DO UPDATE SET
|
|
566
552
|
parent_payment_id = COALESCE(excluded.parent_payment_id, parent_payment_id),
|
|
567
553
|
lnurl_pay_info = COALESCE(excluded.lnurl_pay_info, lnurl_pay_info),
|
|
568
554
|
lnurl_withdraw_info = COALESCE(excluded.lnurl_withdraw_info, lnurl_withdraw_info),
|
|
569
555
|
lnurl_description = COALESCE(excluded.lnurl_description, lnurl_description),
|
|
570
|
-
conversion_info = COALESCE(excluded.conversion_info, conversion_info)
|
|
556
|
+
conversion_info = COALESCE(excluded.conversion_info, conversion_info),
|
|
557
|
+
conversion_status = COALESCE(excluded.conversion_status, conversion_status)
|
|
571
558
|
`);
|
|
572
559
|
|
|
573
560
|
stmt.run(
|
|
@@ -580,7 +567,8 @@ class SqliteStorage {
|
|
|
580
567
|
metadata.lnurlDescription,
|
|
581
568
|
metadata.conversionInfo
|
|
582
569
|
? JSON.stringify(metadata.conversionInfo)
|
|
583
|
-
: null
|
|
570
|
+
: null,
|
|
571
|
+
metadata.conversionStatus ?? null
|
|
584
572
|
);
|
|
585
573
|
return Promise.resolve();
|
|
586
574
|
} catch (error) {
|
|
@@ -595,14 +583,15 @@ class SqliteStorage {
|
|
|
595
583
|
|
|
596
584
|
// ===== Deposit Operations =====
|
|
597
585
|
|
|
598
|
-
addDeposit(txid, vout, amountSats) {
|
|
586
|
+
addDeposit(txid, vout, amountSats, isMature) {
|
|
599
587
|
try {
|
|
600
588
|
const stmt = this.db.prepare(`
|
|
601
|
-
INSERT
|
|
602
|
-
VALUES (?, ?, ?)
|
|
589
|
+
INSERT INTO unclaimed_deposits (txid, vout, amount_sats, is_mature)
|
|
590
|
+
VALUES (?, ?, ?, ?)
|
|
591
|
+
ON CONFLICT(txid, vout) DO UPDATE SET is_mature = excluded.is_mature, amount_sats = excluded.amount_sats
|
|
603
592
|
`);
|
|
604
593
|
|
|
605
|
-
stmt.run(txid, vout, amountSats);
|
|
594
|
+
stmt.run(txid, vout, amountSats, isMature ? 1 : 0);
|
|
606
595
|
return Promise.resolve();
|
|
607
596
|
} catch (error) {
|
|
608
597
|
return Promise.reject(
|
|
@@ -635,7 +624,7 @@ class SqliteStorage {
|
|
|
635
624
|
listDeposits() {
|
|
636
625
|
try {
|
|
637
626
|
const stmt = this.db.prepare(`
|
|
638
|
-
SELECT txid, vout, amount_sats, claim_error, refund_tx, refund_tx_id
|
|
627
|
+
SELECT txid, vout, amount_sats, is_mature, claim_error, refund_tx, refund_tx_id
|
|
639
628
|
FROM unclaimed_deposits
|
|
640
629
|
`);
|
|
641
630
|
|
|
@@ -645,6 +634,7 @@ class SqliteStorage {
|
|
|
645
634
|
txid: row.txid,
|
|
646
635
|
vout: row.vout,
|
|
647
636
|
amountSats: row.amount_sats,
|
|
637
|
+
isMature: Boolean(row.is_mature ?? 1),
|
|
648
638
|
claimError: row.claim_error ? JSON.parse(row.claim_error) : null,
|
|
649
639
|
refundTx: row.refund_tx,
|
|
650
640
|
refundTxId: row.refund_tx_id,
|
|
@@ -695,7 +685,7 @@ class SqliteStorage {
|
|
|
695
685
|
setLnurlMetadata(metadata) {
|
|
696
686
|
try {
|
|
697
687
|
const stmt = this.db.prepare(
|
|
698
|
-
"INSERT OR REPLACE INTO lnurl_receive_metadata (payment_hash, nostr_zap_request, nostr_zap_receipt, sender_comment
|
|
688
|
+
"INSERT OR REPLACE INTO lnurl_receive_metadata (payment_hash, nostr_zap_request, nostr_zap_receipt, sender_comment) VALUES (?, ?, ?, ?)"
|
|
699
689
|
);
|
|
700
690
|
|
|
701
691
|
const transaction = this.db.transaction(() => {
|
|
@@ -704,8 +694,7 @@ class SqliteStorage {
|
|
|
704
694
|
item.paymentHash,
|
|
705
695
|
item.nostrZapRequest || null,
|
|
706
696
|
item.nostrZapReceipt || null,
|
|
707
|
-
item.senderComment || null
|
|
708
|
-
item.preimage || null
|
|
697
|
+
item.senderComment || null
|
|
709
698
|
);
|
|
710
699
|
}
|
|
711
700
|
});
|
|
@@ -830,7 +819,9 @@ class SqliteStorage {
|
|
|
830
819
|
timestamp: row.timestamp,
|
|
831
820
|
method,
|
|
832
821
|
details,
|
|
833
|
-
conversionDetails:
|
|
822
|
+
conversionDetails: row.conversion_status
|
|
823
|
+
? { status: row.conversion_status, from: null, to: null }
|
|
824
|
+
: null,
|
|
834
825
|
};
|
|
835
826
|
}
|
|
836
827
|
|
|
@@ -392,6 +392,24 @@ class MigrationManager {
|
|
|
392
392
|
updated_at INTEGER NOT NULL
|
|
393
393
|
)`
|
|
394
394
|
},
|
|
395
|
+
{
|
|
396
|
+
name: "Drop preimage column from lnurl_receive_metadata",
|
|
397
|
+
sql: `ALTER TABLE lnurl_receive_metadata DROP COLUMN preimage`
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
name: "Clear cached lightning address for CachedLightningAddress format change",
|
|
401
|
+
sql: `DELETE FROM settings WHERE key = 'lightning_address'`
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
name: "Add is_mature to unclaimed_deposits",
|
|
405
|
+
sql: [
|
|
406
|
+
`ALTER TABLE unclaimed_deposits ADD COLUMN is_mature INTEGER NOT NULL DEFAULT 1`,
|
|
407
|
+
]
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
name: "Add conversion_status to payment_metadata",
|
|
411
|
+
sql: `ALTER TABLE payment_metadata ADD COLUMN conversion_status TEXT`,
|
|
412
|
+
},
|
|
395
413
|
];
|
|
396
414
|
}
|
|
397
415
|
}
|
package/package.json
CHANGED