@bsv/wallet-toolbox-client 2.7.0 → 2.7.1
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/out/index.client.cjs +72 -21
- package/out/index.client.cjs.map +1 -1
- package/out/index.client.mjs +72 -21
- package/out/index.client.mjs.map +1 -1
- package/package.json +1 -1
package/out/index.client.mjs
CHANGED
|
@@ -5938,6 +5938,18 @@ function requireConclusiveUtxo(classification) {
|
|
|
5938
5938
|
}
|
|
5939
5939
|
//#endregion
|
|
5940
5940
|
//#region ../src/storage/methods/reconcileFailedTransactionInputs.ts
|
|
5941
|
+
async function quarantineLocalInputs(localInputs, storage, trx) {
|
|
5942
|
+
const staleOutpoints = /* @__PURE__ */ new Set();
|
|
5943
|
+
for (const { output, outpoint } of localInputs) {
|
|
5944
|
+
await storage.updateOutput(verifyId(output.outputId), { spendable: false }, trx);
|
|
5945
|
+
staleOutpoints.add(`${outpoint.txid}.${outpoint.vout}`);
|
|
5946
|
+
}
|
|
5947
|
+
return {
|
|
5948
|
+
checked: localInputs.length,
|
|
5949
|
+
staleConfirmed: localInputs.length,
|
|
5950
|
+
staleOutpoints: [...staleOutpoints]
|
|
5951
|
+
};
|
|
5952
|
+
}
|
|
5941
5953
|
async function findLocalInputs(req, storage, trx) {
|
|
5942
5954
|
const outpoints = Transaction.fromBinary(req.rawTx).inputs.map((input) => ({
|
|
5943
5955
|
txid: input.sourceTXID ?? "",
|
|
@@ -5977,21 +5989,22 @@ async function findLocalInputs(req, storage, trx) {
|
|
|
5977
5989
|
* able to repair the transaction through the ordinary proof recovery path.
|
|
5978
5990
|
*/
|
|
5979
5991
|
async function quarantineReqInputs(req, storage, trx, logger) {
|
|
5980
|
-
const
|
|
5981
|
-
const staleOutpoints = /* @__PURE__ */ new Set();
|
|
5982
|
-
for (const { output, outpoint } of localInputs) {
|
|
5983
|
-
await storage.updateOutput(verifyId(output.outputId), { spendable: false }, trx);
|
|
5984
|
-
staleOutpoints.add(`${outpoint.txid}.${outpoint.vout}`);
|
|
5985
|
-
}
|
|
5986
|
-
const result = {
|
|
5987
|
-
checked: localInputs.length,
|
|
5988
|
-
staleConfirmed: localInputs.length,
|
|
5989
|
-
staleOutpoints: [...staleOutpoints]
|
|
5990
|
-
};
|
|
5992
|
+
const result = await quarantineLocalInputs(await findLocalInputs(req, storage, trx), storage, trx);
|
|
5991
5993
|
if (result.staleConfirmed > 0) logger?.log(`quarantineReqInputs: ${result.staleConfirmed} local input copy/copies quarantined for txid=${req.txid}`);
|
|
5992
5994
|
return result;
|
|
5993
5995
|
}
|
|
5994
5996
|
/**
|
|
5997
|
+
* Keep only inputs created by a locally terminal parent unavailable after the
|
|
5998
|
+
* failed-child transition releases its allocations. Other inputs may still be
|
|
5999
|
+
* valid UTXOs and must remain reusable.
|
|
6000
|
+
*/
|
|
6001
|
+
async function quarantineReqInputsFromFailedParents(req, failedParentTxids, storage, trx, logger) {
|
|
6002
|
+
const failedParents = new Set(failedParentTxids);
|
|
6003
|
+
const result = await quarantineLocalInputs((await findLocalInputs(req, storage, trx)).filter(({ outpoint }) => failedParents.has(outpoint.txid)), storage, trx);
|
|
6004
|
+
if (result.staleConfirmed > 0) logger?.log(`quarantineReqInputsFromFailedParents: ${result.staleConfirmed} local parent output copy/copies quarantined for txid=${req.txid}`);
|
|
6005
|
+
return result;
|
|
6006
|
+
}
|
|
6007
|
+
/**
|
|
5995
6008
|
* Ask the configured UTXO-provider collection to classify the request's local
|
|
5996
6009
|
* inputs and mark only positively confirmed spent outputs unavailable.
|
|
5997
6010
|
* Provider errors and a collection with no providers are inconclusive and
|
|
@@ -29486,9 +29499,9 @@ var TaskReconcilePendingTransactions = class TaskReconcilePendingTransactions ex
|
|
|
29486
29499
|
}
|
|
29487
29500
|
async runTask() {
|
|
29488
29501
|
const checkpoint = await this.getCheckpoint();
|
|
29489
|
-
const
|
|
29502
|
+
const createdBefore = /* @__PURE__ */ new Date(Date.now() - this.minAgeMinutes * 60 * 1e3);
|
|
29490
29503
|
const reqs = await this.findReqsToReview(checkpoint);
|
|
29491
|
-
const eligible = reqs.filter((req) => req.
|
|
29504
|
+
const eligible = reqs.filter((req) => req.created_at <= createdBefore);
|
|
29492
29505
|
const provider = eligible.length === 0 ? void 0 : await this.monitor.services.getStatusForTxids(eligible.map((req) => req.txid));
|
|
29493
29506
|
const results = new Map(provider?.results.map((result) => [result.txid, result]) ?? []);
|
|
29494
29507
|
let reconciled = 0;
|
|
@@ -29497,17 +29510,18 @@ var TaskReconcilePendingTransactions = class TaskReconcilePendingTransactions ex
|
|
|
29497
29510
|
const retained = [];
|
|
29498
29511
|
for (const req of reqs) {
|
|
29499
29512
|
const result = results.get(req.txid);
|
|
29500
|
-
if (req.
|
|
29513
|
+
if (req.created_at > createdBefore) {
|
|
29501
29514
|
retained.push(req);
|
|
29502
29515
|
continue;
|
|
29503
29516
|
}
|
|
29504
|
-
|
|
29517
|
+
const applied = await this.applyTerminalVerdict(req, provider?.status === "success" ? result : void 0, provider?.status === "success" ? provider.name : void 0, createdBefore);
|
|
29518
|
+
if (!applied) {
|
|
29505
29519
|
retained.push(req);
|
|
29506
29520
|
continue;
|
|
29507
29521
|
}
|
|
29508
29522
|
reconciled++;
|
|
29509
|
-
if (result.inputConflict === true) inputConflicts++;
|
|
29510
|
-
reviewLog += `reconciled ${req.provenTxReqId} ${req.txid} ${result.providerStatus ?? "terminal"} => ${result.inputConflict === true ? "doubleSpend" : "invalid"}\n`;
|
|
29523
|
+
if (applied.result.inputConflict === true) inputConflicts++;
|
|
29524
|
+
reviewLog += `reconciled ${req.provenTxReqId} ${req.txid} ${applied.result.providerStatus ?? "terminal"} => ${applied.result.inputConflict === true ? "doubleSpend" : "invalid"}\n`;
|
|
29511
29525
|
}
|
|
29512
29526
|
const fullPage = reqs.length >= this.reviewLimit;
|
|
29513
29527
|
this.triggerNextMsecs = fullPage ? this.triggerQuickMsecs : this.triggerMsecs;
|
|
@@ -29552,13 +29566,18 @@ var TaskReconcilePendingTransactions = class TaskReconcilePendingTransactions ex
|
|
|
29552
29566
|
reqs.sourceOffset = offset;
|
|
29553
29567
|
return reqs;
|
|
29554
29568
|
}
|
|
29555
|
-
async applyTerminalVerdict(reqApi,
|
|
29556
|
-
let applied
|
|
29569
|
+
async applyTerminalVerdict(reqApi, providerResult, providerName, createdBefore) {
|
|
29570
|
+
let applied;
|
|
29557
29571
|
await this.storage.runAsStorageProvider(async (sp) => {
|
|
29558
29572
|
await sp.transaction(async (trx) => {
|
|
29559
29573
|
const req = new EntityProvenTxReq(reqApi);
|
|
29560
29574
|
await req.refreshFromStorage(sp, trx);
|
|
29561
29575
|
if (!REVIEW_STATUSES.includes(req.status)) return;
|
|
29576
|
+
if (req.created_at > createdBefore) return;
|
|
29577
|
+
const failedParentTxids = await this.findFailedLocalParentTxids(req, sp, trx);
|
|
29578
|
+
const result = providerResult?.terminal === true ? providerResult : providerResult?.status === "known" || providerResult?.status === "mined" ? void 0 : this.failedParentVerdict(req.txid, failedParentTxids);
|
|
29579
|
+
if (result == null) return;
|
|
29580
|
+
const provider = providerResult?.terminal === true ? providerName ?? "transaction-status-provider" : "local-storage";
|
|
29562
29581
|
req.status = result.inputConflict === true ? "doubleSpend" : "invalid";
|
|
29563
29582
|
req.addHistoryNote({
|
|
29564
29583
|
when: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -29582,13 +29601,45 @@ var TaskReconcilePendingTransactions = class TaskReconcilePendingTransactions ex
|
|
|
29582
29601
|
...quarantined.staleOutpoints.length > 0 ? { outpoints: quarantined.staleOutpoints.join(",") } : {}
|
|
29583
29602
|
});
|
|
29584
29603
|
await req.updateStorageDynamicProperties(sp, trx);
|
|
29604
|
+
} else if (failedParentTxids.length > 0) {
|
|
29605
|
+
const quarantined = await quarantineReqInputsFromFailedParents(req, failedParentTxids, sp, trx);
|
|
29606
|
+
req.addHistoryNote({
|
|
29607
|
+
when: (/* @__PURE__ */ new Date()).toISOString(),
|
|
29608
|
+
what: "proactiveFailedParentQuarantine",
|
|
29609
|
+
checked: quarantined.checked,
|
|
29610
|
+
confirmed: quarantined.staleConfirmed,
|
|
29611
|
+
parentTxids: failedParentTxids.join(","),
|
|
29612
|
+
...quarantined.staleOutpoints.length > 0 ? { outpoints: quarantined.staleOutpoints.join(",") } : {}
|
|
29613
|
+
});
|
|
29614
|
+
await req.updateStorageDynamicProperties(sp, trx);
|
|
29585
29615
|
}
|
|
29586
|
-
applied =
|
|
29616
|
+
applied = { result };
|
|
29587
29617
|
});
|
|
29588
29618
|
});
|
|
29589
|
-
if (applied) this.monitor.callOnTransactionStatusChanged(reqApi.txid, result.providerStatus ?? "REJECTED");
|
|
29619
|
+
if (applied) this.monitor.callOnTransactionStatusChanged(reqApi.txid, applied.result.providerStatus ?? "REJECTED");
|
|
29590
29620
|
return applied;
|
|
29591
29621
|
}
|
|
29622
|
+
async findFailedLocalParentTxids(req, storage, trx) {
|
|
29623
|
+
const parentTxids = [...new Set(Transaction.fromBinary(req.rawTx).inputs.map((input) => input.sourceTXID).filter((txid) => txid != null && txid !== ""))];
|
|
29624
|
+
const failed = [];
|
|
29625
|
+
for (const txid of parentTxids) if ((await storage.findProvenTxReqs({
|
|
29626
|
+
partial: { txid },
|
|
29627
|
+
trx
|
|
29628
|
+
})).some((parent) => parent.status === "invalid" || parent.status === "doubleSpend")) failed.push(txid);
|
|
29629
|
+
return failed;
|
|
29630
|
+
}
|
|
29631
|
+
failedParentVerdict(txid, failedParentTxids) {
|
|
29632
|
+
if (failedParentTxids.length === 0) return void 0;
|
|
29633
|
+
return {
|
|
29634
|
+
txid,
|
|
29635
|
+
status: "unknown",
|
|
29636
|
+
depth: void 0,
|
|
29637
|
+
terminal: true,
|
|
29638
|
+
inputConflict: false,
|
|
29639
|
+
providerStatus: "LOCAL_PARENT_FAILED",
|
|
29640
|
+
description: `Local parent request is terminal: ${failedParentTxids.join(",")}`.slice(0, 512)
|
|
29641
|
+
};
|
|
29642
|
+
}
|
|
29592
29643
|
};
|
|
29593
29644
|
//#endregion
|
|
29594
29645
|
//#region ../src/monitor/tasks/TaskReviewProvenTxs.ts
|