@breeztech/breez-sdk-spark 0.15.0 → 0.16.1-dev1
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 +511 -215
- package/bundler/breez_sdk_spark_wasm.js +1 -1
- package/bundler/breez_sdk_spark_wasm_bg.js +567 -414
- package/bundler/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/bundler/breez_sdk_spark_wasm_bg.wasm.d.ts +55 -46
- package/bundler/storage/index.js +205 -15
- package/deno/breez_sdk_spark_wasm.d.ts +511 -215
- package/deno/breez_sdk_spark_wasm.js +567 -414
- package/deno/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/deno/breez_sdk_spark_wasm_bg.wasm.d.ts +55 -46
- package/nodejs/breez_sdk_spark_wasm.d.ts +511 -215
- package/nodejs/breez_sdk_spark_wasm.js +578 -421
- package/nodejs/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/nodejs/breez_sdk_spark_wasm_bg.wasm.d.ts +55 -46
- package/nodejs/index.js +10 -10
- package/nodejs/index.mjs +12 -8
- package/nodejs/mysql-session-store/errors.cjs +13 -0
- package/nodejs/{mysql-session-manager → mysql-session-store}/index.cjs +24 -21
- package/nodejs/{mysql-session-manager → mysql-session-store}/migrations.cjs +17 -11
- package/nodejs/mysql-session-store/package.json +9 -0
- package/nodejs/mysql-storage/index.cjs +229 -111
- package/nodejs/mysql-storage/migrations.cjs +37 -2
- package/nodejs/mysql-token-store/index.cjs +99 -79
- package/nodejs/mysql-token-store/migrations.cjs +59 -2
- package/nodejs/mysql-tree-store/index.cjs +15 -9
- package/nodejs/mysql-tree-store/migrations.cjs +16 -2
- package/nodejs/package.json +2 -2
- package/nodejs/postgres-session-store/errors.cjs +13 -0
- package/nodejs/{postgres-session-manager → postgres-session-store}/index.cjs +23 -23
- package/nodejs/{postgres-session-manager → postgres-session-store}/migrations.cjs +14 -14
- package/nodejs/postgres-session-store/package.json +9 -0
- package/nodejs/postgres-storage/index.cjs +174 -107
- package/nodejs/postgres-storage/migrations.cjs +24 -0
- package/nodejs/postgres-token-store/index.cjs +89 -64
- package/nodejs/postgres-token-store/migrations.cjs +44 -0
- package/nodejs/storage/index.cjs +167 -113
- package/nodejs/storage/migrations.cjs +23 -0
- package/package.json +6 -1
- package/ssr/index.js +52 -28
- package/web/breez_sdk_spark_wasm.d.ts +566 -261
- package/web/breez_sdk_spark_wasm.js +567 -414
- package/web/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/web/breez_sdk_spark_wasm_bg.wasm.d.ts +55 -46
- package/web/passkey-prf-provider/index.d.ts +203 -0
- package/web/passkey-prf-provider/index.js +733 -0
- package/web/storage/index.js +205 -15
- package/nodejs/mysql-session-manager/errors.cjs +0 -13
- package/nodejs/mysql-session-manager/package.json +0 -9
- package/nodejs/postgres-session-manager/errors.cjs +0 -13
- package/nodejs/postgres-session-manager/package.json +0 -9
|
@@ -201,12 +201,14 @@ class PostgresTokenStore {
|
|
|
201
201
|
[this.identity, cleanupCutoff]
|
|
202
202
|
);
|
|
203
203
|
|
|
204
|
-
// Get recent spent
|
|
204
|
+
// Get recent spent outpoints (spent_at >= refresh_timestamp)
|
|
205
205
|
const spentResult = await client.query(
|
|
206
|
-
"SELECT
|
|
206
|
+
"SELECT prev_tx_hash, prev_tx_vout FROM brz_token_spent_outputs WHERE user_id = $1 AND spent_at >= $2",
|
|
207
207
|
[this.identity, refreshTimestamp]
|
|
208
208
|
);
|
|
209
|
-
const
|
|
209
|
+
const spentOutpoints = new Set(
|
|
210
|
+
spentResult.rows.map((r) => `${r.prev_tx_hash}:${r.prev_tx_vout}`)
|
|
211
|
+
);
|
|
210
212
|
|
|
211
213
|
// Delete non-reserved outputs added BEFORE the refresh started
|
|
212
214
|
await client.query(
|
|
@@ -214,17 +216,17 @@ class PostgresTokenStore {
|
|
|
214
216
|
[this.identity, refreshTimestamp]
|
|
215
217
|
);
|
|
216
218
|
|
|
217
|
-
// Build a set of all incoming
|
|
218
|
-
const
|
|
219
|
+
// Build a set of all incoming outpoints for reconciliation
|
|
220
|
+
const incomingOutpoints = new Set();
|
|
219
221
|
for (const to of tokenOutputs) {
|
|
220
222
|
for (const o of to.outputs) {
|
|
221
|
-
|
|
223
|
+
incomingOutpoints.add(`${o.prevTxHash}:${o.prevTxVout}`);
|
|
222
224
|
}
|
|
223
225
|
}
|
|
224
226
|
|
|
225
227
|
// Reconcile reservations: find reserved outputs that no longer exist
|
|
226
228
|
const reservedRows = await client.query(
|
|
227
|
-
`SELECT r.id, o.
|
|
229
|
+
`SELECT r.id, o.prev_tx_hash, o.prev_tx_vout
|
|
228
230
|
FROM brz_token_reservations r
|
|
229
231
|
JOIN brz_token_outputs o
|
|
230
232
|
ON o.reservation_id = r.id AND o.user_id = r.user_id
|
|
@@ -232,28 +234,30 @@ class PostgresTokenStore {
|
|
|
232
234
|
[this.identity]
|
|
233
235
|
);
|
|
234
236
|
|
|
235
|
-
// Group reserved
|
|
237
|
+
// Group reserved outpoints by reservation ID
|
|
236
238
|
const reservationOutputs = new Map();
|
|
237
239
|
for (const row of reservedRows.rows) {
|
|
238
240
|
if (!reservationOutputs.has(row.id)) {
|
|
239
241
|
reservationOutputs.set(row.id, []);
|
|
240
242
|
}
|
|
241
|
-
reservationOutputs.get(row.id).push(row.
|
|
243
|
+
reservationOutputs.get(row.id).push([row.prev_tx_hash, row.prev_tx_vout]);
|
|
242
244
|
}
|
|
243
245
|
|
|
244
246
|
// Find reservations that have no valid outputs after reconciliation
|
|
245
247
|
const reservationsToDelete = [];
|
|
246
|
-
const
|
|
247
|
-
for (const [reservationId,
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
for (const
|
|
253
|
-
if (!
|
|
254
|
-
|
|
248
|
+
const outpointsToRemoveFromReservation = [];
|
|
249
|
+
for (const [reservationId, outpoints] of reservationOutputs) {
|
|
250
|
+
const hasValid = outpoints.some(([h, v]) =>
|
|
251
|
+
incomingOutpoints.has(`${h}:${v}`)
|
|
252
|
+
);
|
|
253
|
+
if (hasValid) {
|
|
254
|
+
for (const [h, v] of outpoints) {
|
|
255
|
+
if (!incomingOutpoints.has(`${h}:${v}`)) {
|
|
256
|
+
outpointsToRemoveFromReservation.push([h, v]);
|
|
255
257
|
}
|
|
256
258
|
}
|
|
259
|
+
} else {
|
|
260
|
+
reservationsToDelete.push(reservationId);
|
|
257
261
|
}
|
|
258
262
|
}
|
|
259
263
|
|
|
@@ -270,10 +274,16 @@ class PostgresTokenStore {
|
|
|
270
274
|
}
|
|
271
275
|
|
|
272
276
|
// Delete individual reserved outputs that no longer exist
|
|
273
|
-
if (
|
|
277
|
+
if (outpointsToRemoveFromReservation.length > 0) {
|
|
278
|
+
const txHashes = outpointsToRemoveFromReservation.map(([h]) => h);
|
|
279
|
+
const vouts = outpointsToRemoveFromReservation.map(([, v]) => v);
|
|
274
280
|
await client.query(
|
|
275
|
-
|
|
276
|
-
|
|
281
|
+
`DELETE FROM brz_token_outputs
|
|
282
|
+
WHERE user_id = $1
|
|
283
|
+
AND (prev_tx_hash, prev_tx_vout) IN (
|
|
284
|
+
SELECT * FROM UNNEST($2::text[], $3::int[])
|
|
285
|
+
)`,
|
|
286
|
+
[this.identity, txHashes, vouts]
|
|
277
287
|
);
|
|
278
288
|
|
|
279
289
|
// Check if any reservations are now empty
|
|
@@ -281,7 +291,7 @@ class PostgresTokenStore {
|
|
|
281
291
|
`SELECT r.id FROM brz_token_reservations r
|
|
282
292
|
LEFT JOIN brz_token_outputs o
|
|
283
293
|
ON o.reservation_id = r.id AND o.user_id = r.user_id
|
|
284
|
-
WHERE r.user_id = $1 AND o.
|
|
294
|
+
WHERE r.user_id = $1 AND o.prev_tx_hash IS NULL`,
|
|
285
295
|
[this.identity]
|
|
286
296
|
);
|
|
287
297
|
const emptyIds = emptyReservations.rows.map((r) => r.id);
|
|
@@ -293,13 +303,15 @@ class PostgresTokenStore {
|
|
|
293
303
|
}
|
|
294
304
|
}
|
|
295
305
|
|
|
296
|
-
// Collect
|
|
297
|
-
const
|
|
298
|
-
"SELECT
|
|
306
|
+
// Collect outpoints of currently reserved outputs (that survived reconciliation)
|
|
307
|
+
const reservedOutpointsResult = await client.query(
|
|
308
|
+
"SELECT prev_tx_hash, prev_tx_vout FROM brz_token_outputs WHERE user_id = $1 AND reservation_id IS NOT NULL",
|
|
299
309
|
[this.identity]
|
|
300
310
|
);
|
|
301
|
-
const
|
|
302
|
-
|
|
311
|
+
const reservedOutpoints = new Set(
|
|
312
|
+
reservedOutpointsResult.rows.map(
|
|
313
|
+
(r) => `${r.prev_tx_hash}:${r.prev_tx_vout}`
|
|
314
|
+
)
|
|
303
315
|
);
|
|
304
316
|
|
|
305
317
|
// Delete orphan metadata (per-tenant)
|
|
@@ -318,7 +330,8 @@ class PostgresTokenStore {
|
|
|
318
330
|
await this._upsertMetadata(client, to.metadata);
|
|
319
331
|
|
|
320
332
|
for (const output of to.outputs) {
|
|
321
|
-
|
|
333
|
+
const outpoint = `${output.prevTxHash}:${output.prevTxVout}`;
|
|
334
|
+
if (reservedOutpoints.has(outpoint) || spentOutpoints.has(outpoint)) {
|
|
322
335
|
continue;
|
|
323
336
|
}
|
|
324
337
|
await this._insertSingleOutput(
|
|
@@ -399,7 +412,7 @@ class PostgresTokenStore {
|
|
|
399
412
|
const result = await this.pool.query(
|
|
400
413
|
`SELECT m.identifier, m.issuer_public_key, m.name, m.ticker, m.decimals,
|
|
401
414
|
m.max_supply, m.is_freezable, m.creation_entity_public_key,
|
|
402
|
-
o.
|
|
415
|
+
o.owner_public_key, o.revocation_commitment,
|
|
403
416
|
o.withdraw_bond_sats, o.withdraw_relative_block_locktime,
|
|
404
417
|
o.token_public_key, o.token_amount, o.token_identifier,
|
|
405
418
|
o.prev_tx_hash, o.prev_tx_vout, o.reservation_id,
|
|
@@ -428,7 +441,7 @@ class PostgresTokenStore {
|
|
|
428
441
|
|
|
429
442
|
const entry = map.get(row.identifier);
|
|
430
443
|
|
|
431
|
-
if (!row.
|
|
444
|
+
if (!row.prev_tx_hash) {
|
|
432
445
|
continue;
|
|
433
446
|
}
|
|
434
447
|
|
|
@@ -476,7 +489,7 @@ class PostgresTokenStore {
|
|
|
476
489
|
const result = await this.pool.query(
|
|
477
490
|
`SELECT m.identifier, m.issuer_public_key, m.name, m.ticker, m.decimals,
|
|
478
491
|
m.max_supply, m.is_freezable, m.creation_entity_public_key,
|
|
479
|
-
o.
|
|
492
|
+
o.owner_public_key, o.revocation_commitment,
|
|
480
493
|
o.withdraw_bond_sats, o.withdraw_relative_block_locktime,
|
|
481
494
|
o.token_public_key, o.token_amount, o.token_identifier,
|
|
482
495
|
o.prev_tx_hash, o.prev_tx_vout, o.reservation_id,
|
|
@@ -504,7 +517,7 @@ class PostgresTokenStore {
|
|
|
504
517
|
};
|
|
505
518
|
|
|
506
519
|
for (const row of result.rows) {
|
|
507
|
-
if (!row.
|
|
520
|
+
if (!row.prev_tx_hash) {
|
|
508
521
|
continue;
|
|
509
522
|
}
|
|
510
523
|
|
|
@@ -541,19 +554,20 @@ class PostgresTokenStore {
|
|
|
541
554
|
*/
|
|
542
555
|
async updateTokenOutputs(outputsToRemove, outputsToAdd) {
|
|
543
556
|
try {
|
|
544
|
-
|
|
557
|
+
// Serialize against the other token-store mutators (refresh, reservation,
|
|
558
|
+
// finalization), which take the same per-user advisory lock.
|
|
559
|
+
await this._withWriteTransaction(async (client) => {
|
|
545
560
|
// 1. Remove spent outputs and mark as spent.
|
|
546
561
|
if (outputsToRemove && outputsToRemove.length > 0) {
|
|
547
562
|
for (const [txHash, vout] of outputsToRemove) {
|
|
548
563
|
const result = await client.query(
|
|
549
|
-
"DELETE FROM brz_token_outputs WHERE user_id = $1 AND prev_tx_hash = $2 AND prev_tx_vout = $3
|
|
564
|
+
"DELETE FROM brz_token_outputs WHERE user_id = $1 AND prev_tx_hash = $2 AND prev_tx_vout = $3",
|
|
550
565
|
[this.identity, txHash, vout]
|
|
551
566
|
);
|
|
552
|
-
if (result.
|
|
553
|
-
const outputId = result.rows[0].id;
|
|
567
|
+
if (result.rowCount > 0) {
|
|
554
568
|
await client.query(
|
|
555
|
-
"INSERT INTO brz_token_spent_outputs (user_id,
|
|
556
|
-
[this.identity,
|
|
569
|
+
"INSERT INTO brz_token_spent_outputs (user_id, prev_tx_hash, prev_tx_vout, spent_at) VALUES ($1, $2, $3, NOW()) ON CONFLICT DO NOTHING",
|
|
570
|
+
[this.identity, txHash, vout]
|
|
557
571
|
);
|
|
558
572
|
}
|
|
559
573
|
}
|
|
@@ -563,11 +577,16 @@ class PostgresTokenStore {
|
|
|
563
577
|
if (outputsToAdd) {
|
|
564
578
|
await this._upsertMetadata(client, outputsToAdd.metadata);
|
|
565
579
|
|
|
566
|
-
|
|
567
|
-
|
|
580
|
+
if (outputsToAdd.outputs.length > 0) {
|
|
581
|
+
const txHashes = outputsToAdd.outputs.map((o) => o.prevTxHash);
|
|
582
|
+
const vouts = outputsToAdd.outputs.map((o) => o.prevTxVout);
|
|
568
583
|
await client.query(
|
|
569
|
-
|
|
570
|
-
|
|
584
|
+
`DELETE FROM brz_token_spent_outputs
|
|
585
|
+
WHERE user_id = $1
|
|
586
|
+
AND (prev_tx_hash, prev_tx_vout) IN (
|
|
587
|
+
SELECT * FROM UNNEST($2::text[], $3::int[])
|
|
588
|
+
)`,
|
|
589
|
+
[this.identity, txHashes, vouts]
|
|
571
590
|
);
|
|
572
591
|
}
|
|
573
592
|
|
|
@@ -635,7 +654,7 @@ class PostgresTokenStore {
|
|
|
635
654
|
|
|
636
655
|
// Get available (non-reserved) outputs
|
|
637
656
|
const outputRows = await client.query(
|
|
638
|
-
`SELECT o.
|
|
657
|
+
`SELECT o.owner_public_key, o.revocation_commitment,
|
|
639
658
|
o.withdraw_bond_sats, o.withdraw_relative_block_locktime,
|
|
640
659
|
o.token_public_key, o.token_amount, o.token_identifier,
|
|
641
660
|
o.prev_tx_hash, o.prev_tx_vout
|
|
@@ -650,10 +669,12 @@ class PostgresTokenStore {
|
|
|
650
669
|
|
|
651
670
|
// Filter by preferred if provided
|
|
652
671
|
if (preferredOutputs && preferredOutputs.length > 0) {
|
|
653
|
-
const
|
|
654
|
-
preferredOutputs.map((p) => p.
|
|
672
|
+
const preferredOutpoints = new Set(
|
|
673
|
+
preferredOutputs.map((p) => `${p.prevTxHash}:${p.prevTxVout}`)
|
|
674
|
+
);
|
|
675
|
+
outputs = outputs.filter((o) =>
|
|
676
|
+
preferredOutpoints.has(`${o.prevTxHash}:${o.prevTxVout}`)
|
|
655
677
|
);
|
|
656
|
-
outputs = outputs.filter((o) => preferredIds.has(o.output.id));
|
|
657
678
|
}
|
|
658
679
|
|
|
659
680
|
// Select outputs based on target
|
|
@@ -733,12 +754,17 @@ class PostgresTokenStore {
|
|
|
733
754
|
[this.identity, reservationId, purpose]
|
|
734
755
|
);
|
|
735
756
|
|
|
736
|
-
// Set reservation_id on selected outputs
|
|
737
|
-
|
|
738
|
-
|
|
757
|
+
// Set reservation_id on selected outputs (by outpoint)
|
|
758
|
+
if (selectedOutputs.length > 0) {
|
|
759
|
+
const selectedTxHashes = selectedOutputs.map((o) => o.prevTxHash);
|
|
760
|
+
const selectedVouts = selectedOutputs.map((o) => o.prevTxVout);
|
|
739
761
|
await client.query(
|
|
740
|
-
|
|
741
|
-
|
|
762
|
+
`UPDATE brz_token_outputs SET reservation_id = $1
|
|
763
|
+
WHERE user_id = $4
|
|
764
|
+
AND (prev_tx_hash, prev_tx_vout) IN (
|
|
765
|
+
SELECT * FROM UNNEST($2::text[], $3::int[])
|
|
766
|
+
)`,
|
|
767
|
+
[reservationId, selectedTxHashes, selectedVouts, this.identity]
|
|
742
768
|
);
|
|
743
769
|
}
|
|
744
770
|
|
|
@@ -810,19 +836,20 @@ class PostgresTokenStore {
|
|
|
810
836
|
}
|
|
811
837
|
const isSwap = reservationResult.rows[0].purpose === "Swap";
|
|
812
838
|
|
|
813
|
-
// Get reserved
|
|
839
|
+
// Get reserved outpoints and mark them as spent
|
|
814
840
|
const reservedOutputsResult = await client.query(
|
|
815
|
-
"SELECT
|
|
841
|
+
"SELECT prev_tx_hash, prev_tx_vout FROM brz_token_outputs WHERE user_id = $1 AND reservation_id = $2",
|
|
816
842
|
[this.identity, id]
|
|
817
843
|
);
|
|
818
|
-
const reservedOutputIds = reservedOutputsResult.rows.map((r) => r.id);
|
|
819
844
|
|
|
820
|
-
if (
|
|
845
|
+
if (reservedOutputsResult.rows.length > 0) {
|
|
846
|
+
const txHashes = reservedOutputsResult.rows.map((r) => r.prev_tx_hash);
|
|
847
|
+
const vouts = reservedOutputsResult.rows.map((r) => r.prev_tx_vout);
|
|
821
848
|
await client.query(
|
|
822
|
-
`INSERT INTO brz_token_spent_outputs (user_id,
|
|
823
|
-
SELECT $
|
|
849
|
+
`INSERT INTO brz_token_spent_outputs (user_id, prev_tx_hash, prev_tx_vout)
|
|
850
|
+
SELECT $3, h, v FROM UNNEST($1::text[], $2::int[]) AS t(h, v)
|
|
824
851
|
ON CONFLICT DO NOTHING`,
|
|
825
|
-
[
|
|
852
|
+
[txHashes, vouts, this.identity]
|
|
826
853
|
);
|
|
827
854
|
}
|
|
828
855
|
|
|
@@ -965,13 +992,13 @@ class PostgresTokenStore {
|
|
|
965
992
|
async _insertSingleOutput(client, tokenIdentifier, output) {
|
|
966
993
|
await client.query(
|
|
967
994
|
`INSERT INTO brz_token_outputs
|
|
968
|
-
(user_id,
|
|
995
|
+
(user_id, token_identifier, owner_public_key, revocation_commitment,
|
|
969
996
|
withdraw_bond_sats, withdraw_relative_block_locktime,
|
|
970
997
|
token_public_key, token_amount, prev_tx_hash, prev_tx_vout, added_at)
|
|
971
|
-
VALUES ($
|
|
972
|
-
ON CONFLICT (user_id,
|
|
998
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, NOW())
|
|
999
|
+
ON CONFLICT (user_id, prev_tx_hash, prev_tx_vout) DO NOTHING`,
|
|
973
1000
|
[
|
|
974
|
-
|
|
1001
|
+
this.identity,
|
|
975
1002
|
tokenIdentifier,
|
|
976
1003
|
output.output.ownerPublicKey,
|
|
977
1004
|
output.output.revocationCommitment,
|
|
@@ -981,7 +1008,6 @@ class PostgresTokenStore {
|
|
|
981
1008
|
output.output.tokenAmount,
|
|
982
1009
|
output.prevTxHash,
|
|
983
1010
|
output.prevTxVout,
|
|
984
|
-
this.identity,
|
|
985
1011
|
]
|
|
986
1012
|
);
|
|
987
1013
|
}
|
|
@@ -1008,7 +1034,6 @@ class PostgresTokenStore {
|
|
|
1008
1034
|
_outputFromRow(row) {
|
|
1009
1035
|
return {
|
|
1010
1036
|
output: {
|
|
1011
|
-
id: row.output_id,
|
|
1012
1037
|
ownerPublicKey: row.owner_public_key,
|
|
1013
1038
|
revocationCommitment: row.revocation_commitment,
|
|
1014
1039
|
withdrawBondSats: Number(row.withdraw_bond_sats),
|
|
@@ -337,6 +337,50 @@ class TokenStoreMigrationManager {
|
|
|
337
337
|
ADD PRIMARY KEY (user_id)`,
|
|
338
338
|
],
|
|
339
339
|
},
|
|
340
|
+
{
|
|
341
|
+
// Mirrors Rust migration 3 in spark-postgres/src/token_store.rs.
|
|
342
|
+
// Re-keys brz_token_spent_outputs by (prev_tx_hash, prev_tx_vout) instead
|
|
343
|
+
// of the operator-issued output id. v3 FinalTokenOutput carries no id
|
|
344
|
+
// field, so post-broadcast spent markers only have an outpoint to work
|
|
345
|
+
// with. Existing output_id-keyed rows can't be backfilled (no outpoint
|
|
346
|
+
// stored alongside them), so the table is wiped on upgrade — spent
|
|
347
|
+
// markers are short-lived (5 minute cleanup window) so wiping is
|
|
348
|
+
// equivalent to letting them age out.
|
|
349
|
+
name: "Re-key spent outputs by (prev_tx_hash, prev_tx_vout)",
|
|
350
|
+
sql: [
|
|
351
|
+
`DROP TABLE IF EXISTS brz_token_spent_outputs`,
|
|
352
|
+
`CREATE TABLE brz_token_spent_outputs (
|
|
353
|
+
user_id BYTEA NOT NULL,
|
|
354
|
+
prev_tx_hash TEXT NOT NULL,
|
|
355
|
+
prev_tx_vout INTEGER NOT NULL,
|
|
356
|
+
spent_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
357
|
+
PRIMARY KEY (user_id, prev_tx_hash, prev_tx_vout)
|
|
358
|
+
)`,
|
|
359
|
+
],
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
// Re-key brz_token_outputs by (prev_tx_hash, prev_tx_vout) and drop the
|
|
363
|
+
// legacy id column. id already held "{prev_tx_hash}:{vout}", so the
|
|
364
|
+
// outpoint is the natural key. Dedup any duplicate-outpoint rows
|
|
365
|
+
// (possible from pre-outpoint code) before adding the composite PK,
|
|
366
|
+
// preferring rows that hold a reservation.
|
|
367
|
+
name: "Re-key token outputs by (prev_tx_hash, prev_tx_vout), drop legacy id",
|
|
368
|
+
sql: [
|
|
369
|
+
`DELETE FROM brz_token_outputs WHERE ctid IN (
|
|
370
|
+
SELECT ctid FROM (
|
|
371
|
+
SELECT ctid, ROW_NUMBER() OVER (
|
|
372
|
+
PARTITION BY user_id, prev_tx_hash, prev_tx_vout
|
|
373
|
+
ORDER BY (reservation_id IS NULL) ASC, id ASC
|
|
374
|
+
) AS rn
|
|
375
|
+
FROM brz_token_outputs
|
|
376
|
+
) t WHERE t.rn > 1
|
|
377
|
+
)`,
|
|
378
|
+
`ALTER TABLE brz_token_outputs
|
|
379
|
+
DROP CONSTRAINT IF EXISTS brz_token_outputs_pkey,
|
|
380
|
+
ADD PRIMARY KEY (user_id, prev_tx_hash, prev_tx_vout)`,
|
|
381
|
+
`ALTER TABLE brz_token_outputs DROP COLUMN id`,
|
|
382
|
+
],
|
|
383
|
+
},
|
|
340
384
|
];
|
|
341
385
|
}
|
|
342
386
|
}
|