@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.cjs
CHANGED
|
@@ -5973,6 +5973,18 @@ function requireConclusiveUtxo(classification) {
|
|
|
5973
5973
|
}
|
|
5974
5974
|
//#endregion
|
|
5975
5975
|
//#region ../src/storage/methods/reconcileFailedTransactionInputs.ts
|
|
5976
|
+
async function quarantineLocalInputs(localInputs, storage, trx) {
|
|
5977
|
+
const staleOutpoints = /* @__PURE__ */ new Set();
|
|
5978
|
+
for (const { output, outpoint } of localInputs) {
|
|
5979
|
+
await storage.updateOutput(verifyId(output.outputId), { spendable: false }, trx);
|
|
5980
|
+
staleOutpoints.add(`${outpoint.txid}.${outpoint.vout}`);
|
|
5981
|
+
}
|
|
5982
|
+
return {
|
|
5983
|
+
checked: localInputs.length,
|
|
5984
|
+
staleConfirmed: localInputs.length,
|
|
5985
|
+
staleOutpoints: [...staleOutpoints]
|
|
5986
|
+
};
|
|
5987
|
+
}
|
|
5976
5988
|
async function findLocalInputs(req, storage, trx) {
|
|
5977
5989
|
const outpoints = _bsv_sdk.Transaction.fromBinary(req.rawTx).inputs.map((input) => ({
|
|
5978
5990
|
txid: input.sourceTXID ?? "",
|
|
@@ -6012,21 +6024,22 @@ async function findLocalInputs(req, storage, trx) {
|
|
|
6012
6024
|
* able to repair the transaction through the ordinary proof recovery path.
|
|
6013
6025
|
*/
|
|
6014
6026
|
async function quarantineReqInputs(req, storage, trx, logger) {
|
|
6015
|
-
const
|
|
6016
|
-
const staleOutpoints = /* @__PURE__ */ new Set();
|
|
6017
|
-
for (const { output, outpoint } of localInputs) {
|
|
6018
|
-
await storage.updateOutput(verifyId(output.outputId), { spendable: false }, trx);
|
|
6019
|
-
staleOutpoints.add(`${outpoint.txid}.${outpoint.vout}`);
|
|
6020
|
-
}
|
|
6021
|
-
const result = {
|
|
6022
|
-
checked: localInputs.length,
|
|
6023
|
-
staleConfirmed: localInputs.length,
|
|
6024
|
-
staleOutpoints: [...staleOutpoints]
|
|
6025
|
-
};
|
|
6027
|
+
const result = await quarantineLocalInputs(await findLocalInputs(req, storage, trx), storage, trx);
|
|
6026
6028
|
if (result.staleConfirmed > 0) logger?.log(`quarantineReqInputs: ${result.staleConfirmed} local input copy/copies quarantined for txid=${req.txid}`);
|
|
6027
6029
|
return result;
|
|
6028
6030
|
}
|
|
6029
6031
|
/**
|
|
6032
|
+
* Keep only inputs created by a locally terminal parent unavailable after the
|
|
6033
|
+
* failed-child transition releases its allocations. Other inputs may still be
|
|
6034
|
+
* valid UTXOs and must remain reusable.
|
|
6035
|
+
*/
|
|
6036
|
+
async function quarantineReqInputsFromFailedParents(req, failedParentTxids, storage, trx, logger) {
|
|
6037
|
+
const failedParents = new Set(failedParentTxids);
|
|
6038
|
+
const result = await quarantineLocalInputs((await findLocalInputs(req, storage, trx)).filter(({ outpoint }) => failedParents.has(outpoint.txid)), storage, trx);
|
|
6039
|
+
if (result.staleConfirmed > 0) logger?.log(`quarantineReqInputsFromFailedParents: ${result.staleConfirmed} local parent output copy/copies quarantined for txid=${req.txid}`);
|
|
6040
|
+
return result;
|
|
6041
|
+
}
|
|
6042
|
+
/**
|
|
6030
6043
|
* Ask the configured UTXO-provider collection to classify the request's local
|
|
6031
6044
|
* inputs and mark only positively confirmed spent outputs unavailable.
|
|
6032
6045
|
* Provider errors and a collection with no providers are inconclusive and
|
|
@@ -29521,9 +29534,9 @@ var TaskReconcilePendingTransactions = class TaskReconcilePendingTransactions ex
|
|
|
29521
29534
|
}
|
|
29522
29535
|
async runTask() {
|
|
29523
29536
|
const checkpoint = await this.getCheckpoint();
|
|
29524
|
-
const
|
|
29537
|
+
const createdBefore = /* @__PURE__ */ new Date(Date.now() - this.minAgeMinutes * 60 * 1e3);
|
|
29525
29538
|
const reqs = await this.findReqsToReview(checkpoint);
|
|
29526
|
-
const eligible = reqs.filter((req) => req.
|
|
29539
|
+
const eligible = reqs.filter((req) => req.created_at <= createdBefore);
|
|
29527
29540
|
const provider = eligible.length === 0 ? void 0 : await this.monitor.services.getStatusForTxids(eligible.map((req) => req.txid));
|
|
29528
29541
|
const results = new Map(provider?.results.map((result) => [result.txid, result]) ?? []);
|
|
29529
29542
|
let reconciled = 0;
|
|
@@ -29532,17 +29545,18 @@ var TaskReconcilePendingTransactions = class TaskReconcilePendingTransactions ex
|
|
|
29532
29545
|
const retained = [];
|
|
29533
29546
|
for (const req of reqs) {
|
|
29534
29547
|
const result = results.get(req.txid);
|
|
29535
|
-
if (req.
|
|
29548
|
+
if (req.created_at > createdBefore) {
|
|
29536
29549
|
retained.push(req);
|
|
29537
29550
|
continue;
|
|
29538
29551
|
}
|
|
29539
|
-
|
|
29552
|
+
const applied = await this.applyTerminalVerdict(req, provider?.status === "success" ? result : void 0, provider?.status === "success" ? provider.name : void 0, createdBefore);
|
|
29553
|
+
if (!applied) {
|
|
29540
29554
|
retained.push(req);
|
|
29541
29555
|
continue;
|
|
29542
29556
|
}
|
|
29543
29557
|
reconciled++;
|
|
29544
|
-
if (result.inputConflict === true) inputConflicts++;
|
|
29545
|
-
reviewLog += `reconciled ${req.provenTxReqId} ${req.txid} ${result.providerStatus ?? "terminal"} => ${result.inputConflict === true ? "doubleSpend" : "invalid"}\n`;
|
|
29558
|
+
if (applied.result.inputConflict === true) inputConflicts++;
|
|
29559
|
+
reviewLog += `reconciled ${req.provenTxReqId} ${req.txid} ${applied.result.providerStatus ?? "terminal"} => ${applied.result.inputConflict === true ? "doubleSpend" : "invalid"}\n`;
|
|
29546
29560
|
}
|
|
29547
29561
|
const fullPage = reqs.length >= this.reviewLimit;
|
|
29548
29562
|
this.triggerNextMsecs = fullPage ? this.triggerQuickMsecs : this.triggerMsecs;
|
|
@@ -29587,13 +29601,18 @@ var TaskReconcilePendingTransactions = class TaskReconcilePendingTransactions ex
|
|
|
29587
29601
|
reqs.sourceOffset = offset;
|
|
29588
29602
|
return reqs;
|
|
29589
29603
|
}
|
|
29590
|
-
async applyTerminalVerdict(reqApi,
|
|
29591
|
-
let applied
|
|
29604
|
+
async applyTerminalVerdict(reqApi, providerResult, providerName, createdBefore) {
|
|
29605
|
+
let applied;
|
|
29592
29606
|
await this.storage.runAsStorageProvider(async (sp) => {
|
|
29593
29607
|
await sp.transaction(async (trx) => {
|
|
29594
29608
|
const req = new EntityProvenTxReq(reqApi);
|
|
29595
29609
|
await req.refreshFromStorage(sp, trx);
|
|
29596
29610
|
if (!REVIEW_STATUSES.includes(req.status)) return;
|
|
29611
|
+
if (req.created_at > createdBefore) return;
|
|
29612
|
+
const failedParentTxids = await this.findFailedLocalParentTxids(req, sp, trx);
|
|
29613
|
+
const result = providerResult?.terminal === true ? providerResult : providerResult?.status === "known" || providerResult?.status === "mined" ? void 0 : this.failedParentVerdict(req.txid, failedParentTxids);
|
|
29614
|
+
if (result == null) return;
|
|
29615
|
+
const provider = providerResult?.terminal === true ? providerName ?? "transaction-status-provider" : "local-storage";
|
|
29597
29616
|
req.status = result.inputConflict === true ? "doubleSpend" : "invalid";
|
|
29598
29617
|
req.addHistoryNote({
|
|
29599
29618
|
when: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -29617,13 +29636,45 @@ var TaskReconcilePendingTransactions = class TaskReconcilePendingTransactions ex
|
|
|
29617
29636
|
...quarantined.staleOutpoints.length > 0 ? { outpoints: quarantined.staleOutpoints.join(",") } : {}
|
|
29618
29637
|
});
|
|
29619
29638
|
await req.updateStorageDynamicProperties(sp, trx);
|
|
29639
|
+
} else if (failedParentTxids.length > 0) {
|
|
29640
|
+
const quarantined = await quarantineReqInputsFromFailedParents(req, failedParentTxids, sp, trx);
|
|
29641
|
+
req.addHistoryNote({
|
|
29642
|
+
when: (/* @__PURE__ */ new Date()).toISOString(),
|
|
29643
|
+
what: "proactiveFailedParentQuarantine",
|
|
29644
|
+
checked: quarantined.checked,
|
|
29645
|
+
confirmed: quarantined.staleConfirmed,
|
|
29646
|
+
parentTxids: failedParentTxids.join(","),
|
|
29647
|
+
...quarantined.staleOutpoints.length > 0 ? { outpoints: quarantined.staleOutpoints.join(",") } : {}
|
|
29648
|
+
});
|
|
29649
|
+
await req.updateStorageDynamicProperties(sp, trx);
|
|
29620
29650
|
}
|
|
29621
|
-
applied =
|
|
29651
|
+
applied = { result };
|
|
29622
29652
|
});
|
|
29623
29653
|
});
|
|
29624
|
-
if (applied) this.monitor.callOnTransactionStatusChanged(reqApi.txid, result.providerStatus ?? "REJECTED");
|
|
29654
|
+
if (applied) this.monitor.callOnTransactionStatusChanged(reqApi.txid, applied.result.providerStatus ?? "REJECTED");
|
|
29625
29655
|
return applied;
|
|
29626
29656
|
}
|
|
29657
|
+
async findFailedLocalParentTxids(req, storage, trx) {
|
|
29658
|
+
const parentTxids = [...new Set(_bsv_sdk.Transaction.fromBinary(req.rawTx).inputs.map((input) => input.sourceTXID).filter((txid) => txid != null && txid !== ""))];
|
|
29659
|
+
const failed = [];
|
|
29660
|
+
for (const txid of parentTxids) if ((await storage.findProvenTxReqs({
|
|
29661
|
+
partial: { txid },
|
|
29662
|
+
trx
|
|
29663
|
+
})).some((parent) => parent.status === "invalid" || parent.status === "doubleSpend")) failed.push(txid);
|
|
29664
|
+
return failed;
|
|
29665
|
+
}
|
|
29666
|
+
failedParentVerdict(txid, failedParentTxids) {
|
|
29667
|
+
if (failedParentTxids.length === 0) return void 0;
|
|
29668
|
+
return {
|
|
29669
|
+
txid,
|
|
29670
|
+
status: "unknown",
|
|
29671
|
+
depth: void 0,
|
|
29672
|
+
terminal: true,
|
|
29673
|
+
inputConflict: false,
|
|
29674
|
+
providerStatus: "LOCAL_PARENT_FAILED",
|
|
29675
|
+
description: `Local parent request is terminal: ${failedParentTxids.join(",")}`.slice(0, 512)
|
|
29676
|
+
};
|
|
29677
|
+
}
|
|
29627
29678
|
};
|
|
29628
29679
|
//#endregion
|
|
29629
29680
|
//#region ../src/monitor/tasks/TaskReviewProvenTxs.ts
|