@bsv/wallet-toolbox 2.10.4 → 2.11.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.
- package/CHANGELOG.md +68 -0
- package/README.md +47 -0
- package/docs/managed-change-liquidity.md +7 -0
- package/docs/no-send-expiry.md +167 -0
- package/docs/prepared-beef.md +197 -0
- package/docs/setup.md +11 -0
- package/docs/storage.md +11 -0
- package/out/src/Setup.d.ts +3 -0
- package/out/src/Setup.d.ts.map +1 -1
- package/out/src/Setup.js +1 -0
- package/out/src/Setup.js.map +1 -1
- package/out/src/Wallet.d.ts.map +1 -1
- package/out/src/Wallet.js +11 -1
- package/out/src/Wallet.js.map +1 -1
- package/out/src/WalletPermissionsManager.d.ts +30 -6
- package/out/src/WalletPermissionsManager.d.ts.map +1 -1
- package/out/src/WalletPermissionsManager.js +84 -9
- package/out/src/WalletPermissionsManager.js.map +1 -1
- package/out/src/mockchain/MockServices.d.ts +2 -0
- package/out/src/mockchain/MockServices.d.ts.map +1 -1
- package/out/src/mockchain/MockServices.js +20 -0
- package/out/src/mockchain/MockServices.js.map +1 -1
- package/out/src/monitor/Monitor.d.ts +1 -0
- package/out/src/monitor/Monitor.d.ts.map +1 -1
- package/out/src/monitor/Monitor.js +19 -3
- package/out/src/monitor/Monitor.js.map +1 -1
- package/out/src/monitor/tasks/TaskNoSendExpiry.d.ts +19 -0
- package/out/src/monitor/tasks/TaskNoSendExpiry.d.ts.map +1 -0
- package/out/src/monitor/tasks/TaskNoSendExpiry.js +47 -0
- package/out/src/monitor/tasks/TaskNoSendExpiry.js.map +1 -0
- package/out/src/monitor/tasks/index.all.d.ts +1 -0
- package/out/src/monitor/tasks/index.all.d.ts.map +1 -1
- package/out/src/monitor/tasks/index.all.js +1 -0
- package/out/src/monitor/tasks/index.all.js.map +1 -1
- package/out/src/sdk/ActionBatch.interfaces.d.ts +4 -0
- package/out/src/sdk/ActionBatch.interfaces.d.ts.map +1 -1
- package/out/src/sdk/WalletStorage.interfaces.d.ts +35 -0
- package/out/src/sdk/WalletStorage.interfaces.d.ts.map +1 -1
- package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageKnex.d.ts +0 -1
- package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageKnex.d.ts.map +1 -1
- package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageKnex.js +23 -12
- package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageKnex.js.map +1 -1
- package/out/src/signer/methods/createNoSendExpiryAction.d.ts +7 -0
- package/out/src/signer/methods/createNoSendExpiryAction.d.ts.map +1 -0
- package/out/src/signer/methods/createNoSendExpiryAction.js +249 -0
- package/out/src/signer/methods/createNoSendExpiryAction.js.map +1 -0
- package/out/src/signer/methods/signAction.d.ts.map +1 -1
- package/out/src/signer/methods/signAction.js +9 -0
- package/out/src/signer/methods/signAction.js.map +1 -1
- package/out/src/storage/StorageIdb.d.ts +3 -0
- package/out/src/storage/StorageIdb.d.ts.map +1 -1
- package/out/src/storage/StorageIdb.js +37 -1
- package/out/src/storage/StorageIdb.js.map +1 -1
- package/out/src/storage/StorageKnex.d.ts +48 -3
- package/out/src/storage/StorageKnex.d.ts.map +1 -1
- package/out/src/storage/StorageKnex.js +234 -14
- package/out/src/storage/StorageKnex.js.map +1 -1
- package/out/src/storage/StorageProvider.d.ts +6 -1
- package/out/src/storage/StorageProvider.d.ts.map +1 -1
- package/out/src/storage/StorageProvider.js +147 -5
- package/out/src/storage/StorageProvider.js.map +1 -1
- package/out/src/storage/StorageReaderWriter.d.ts +2 -0
- package/out/src/storage/StorageReaderWriter.d.ts.map +1 -1
- package/out/src/storage/StorageReaderWriter.js +4 -0
- package/out/src/storage/StorageReaderWriter.js.map +1 -1
- package/out/src/storage/WalletStorageManager.d.ts +11 -0
- package/out/src/storage/WalletStorageManager.d.ts.map +1 -1
- package/out/src/storage/WalletStorageManager.js +77 -12
- package/out/src/storage/WalletStorageManager.js.map +1 -1
- package/out/src/storage/idbHelpers.d.ts.map +1 -1
- package/out/src/storage/idbHelpers.js +14 -2
- package/out/src/storage/idbHelpers.js.map +1 -1
- package/out/src/storage/index.all.d.ts +2 -0
- package/out/src/storage/index.all.d.ts.map +1 -1
- package/out/src/storage/index.all.js +2 -0
- package/out/src/storage/index.all.js.map +1 -1
- package/out/src/storage/methods/createAction.d.ts +57 -1
- package/out/src/storage/methods/createAction.d.ts.map +1 -1
- package/out/src/storage/methods/createAction.js +195 -49
- package/out/src/storage/methods/createAction.js.map +1 -1
- package/out/src/storage/methods/noSendExpiry.d.ts +11 -0
- package/out/src/storage/methods/noSendExpiry.d.ts.map +1 -0
- package/out/src/storage/methods/noSendExpiry.js +357 -0
- package/out/src/storage/methods/noSendExpiry.js.map +1 -0
- package/out/src/storage/methods/noSendExpiryLifecycle.d.ts +14 -0
- package/out/src/storage/methods/noSendExpiryLifecycle.d.ts.map +1 -0
- package/out/src/storage/methods/noSendExpiryLifecycle.js +691 -0
- package/out/src/storage/methods/noSendExpiryLifecycle.js.map +1 -0
- package/out/src/storage/methods/preparedBeef.d.ts +104 -0
- package/out/src/storage/methods/preparedBeef.d.ts.map +1 -0
- package/out/src/storage/methods/preparedBeef.js +525 -0
- package/out/src/storage/methods/preparedBeef.js.map +1 -0
- package/out/src/storage/methods/processAction.d.ts.map +1 -1
- package/out/src/storage/methods/processAction.js +109 -28
- package/out/src/storage/methods/processAction.js.map +1 -1
- package/out/src/storage/methods/purgeData.d.ts.map +1 -1
- package/out/src/storage/methods/purgeData.js +9 -0
- package/out/src/storage/methods/purgeData.js.map +1 -1
- package/out/src/storage/methods/validateSyncProof.d.ts +24 -0
- package/out/src/storage/methods/validateSyncProof.d.ts.map +1 -0
- package/out/src/storage/methods/validateSyncProof.js +116 -0
- package/out/src/storage/methods/validateSyncProof.js.map +1 -0
- package/out/src/storage/remoting/StorageClientBase.d.ts +4 -1
- package/out/src/storage/remoting/StorageClientBase.d.ts.map +1 -1
- package/out/src/storage/remoting/StorageClientBase.js +9 -0
- package/out/src/storage/remoting/StorageClientBase.js.map +1 -1
- package/out/src/storage/remoting/StorageServer.d.ts.map +1 -1
- package/out/src/storage/remoting/StorageServer.js +16 -3
- package/out/src/storage/remoting/StorageServer.js.map +1 -1
- package/out/src/storage/schema/KnexMigrations.d.ts +2 -0
- package/out/src/storage/schema/KnexMigrations.d.ts.map +1 -1
- package/out/src/storage/schema/KnexMigrations.js +66 -1
- package/out/src/storage/schema/KnexMigrations.js.map +1 -1
- package/out/src/storage/schema/StorageIdbSchema.d.ts +2 -0
- package/out/src/storage/schema/StorageIdbSchema.d.ts.map +1 -1
- package/out/src/storage/schema/entities/EntityOutput.d.ts.map +1 -1
- package/out/src/storage/schema/entities/EntityOutput.js +13 -0
- package/out/src/storage/schema/entities/EntityOutput.js.map +1 -1
- package/out/src/storage/schema/entities/EntityProvenTx.d.ts +2 -0
- package/out/src/storage/schema/entities/EntityProvenTx.d.ts.map +1 -1
- package/out/src/storage/schema/entities/EntityProvenTx.js +33 -3
- package/out/src/storage/schema/entities/EntityProvenTx.js.map +1 -1
- package/out/src/storage/schema/entities/EntityTransaction.d.ts +26 -0
- package/out/src/storage/schema/entities/EntityTransaction.d.ts.map +1 -1
- package/out/src/storage/schema/entities/EntityTransaction.js +221 -21
- package/out/src/storage/schema/entities/EntityTransaction.js.map +1 -1
- package/out/src/storage/schema/tables/TablePreparedBeef.interfaces.d.ts +23 -0
- package/out/src/storage/schema/tables/TablePreparedBeef.interfaces.d.ts.map +1 -0
- package/out/src/storage/schema/tables/TablePreparedBeef.interfaces.js +3 -0
- package/out/src/storage/schema/tables/TablePreparedBeef.interfaces.js.map +1 -0
- package/out/src/storage/schema/tables/TableTransaction.d.ts +16 -0
- package/out/src/storage/schema/tables/TableTransaction.d.ts.map +1 -1
- package/out/src/storage/schema/tables/TableTransaction.js +14 -1
- package/out/src/storage/schema/tables/TableTransaction.js.map +1 -1
- package/out/src/utility/Argon2idBackend.d.ts +36 -0
- package/out/src/utility/Argon2idBackend.d.ts.map +1 -0
- package/out/src/utility/Argon2idBackend.js +60 -0
- package/out/src/utility/Argon2idBackend.js.map +1 -0
- package/out/src/utility/brc177NoSendExpiry.d.ts +34 -0
- package/out/src/utility/brc177NoSendExpiry.d.ts.map +1 -0
- package/out/src/utility/brc177NoSendExpiry.js +81 -0
- package/out/src/utility/brc177NoSendExpiry.js.map +1 -0
- package/out/src/utility/hashWasm.d.ts +2 -1
- package/out/src/utility/hashWasm.d.ts.map +1 -1
- package/out/src/utility/hashWasm.js +83 -1
- package/out/src/utility/hashWasm.js.map +1 -1
- package/out/src/utility/index.all.d.ts +2 -0
- package/out/src/utility/index.all.d.ts.map +1 -1
- package/out/src/utility/index.all.js +2 -0
- package/out/src/utility/index.all.js.map +1 -1
- package/out/src/utility/index.client.d.ts +2 -0
- package/out/src/utility/index.client.d.ts.map +1 -1
- package/out/src/utility/index.client.js +2 -0
- package/out/src/utility/index.client.js.map +1 -1
- package/package.json +2 -1
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.setDisableDoubleSpendCheckForTest = setDisableDoubleSpendCheckForTest;
|
|
4
4
|
exports.createAction = createAction;
|
|
5
|
+
exports.validateRequiredOutputs = validateRequiredOutputs;
|
|
6
|
+
exports.validateRequiredInputs = validateRequiredInputs;
|
|
5
7
|
const sdk_1 = require("@bsv/sdk");
|
|
6
8
|
const generateChange_1 = require("./generateChange");
|
|
7
9
|
const StorageProvider_1 = require("../StorageProvider");
|
|
@@ -50,6 +52,9 @@ async function createActionCore(storage, auth, vargs, parent) {
|
|
|
50
52
|
// stampLog(vargs, `start storage createTransactionSdk`)
|
|
51
53
|
if (vargs.isTestWerrReviewActions)
|
|
52
54
|
(0, Wallet_1.throwDummyReviewActions)();
|
|
55
|
+
if (vargs.brc177 != null && auth.isActive !== true) {
|
|
56
|
+
throw new WERR_errors_1.WERR_NOT_ACTIVE('BRC-177 requires the active storage provider');
|
|
57
|
+
}
|
|
53
58
|
if (!vargs.isNewTx)
|
|
54
59
|
// The purpose of this function is to create the initial storage records associated
|
|
55
60
|
// with a new transaction. It's an error if we have no new inputs or outputs...
|
|
@@ -100,12 +105,13 @@ async function createActionCore(storage, auth, vargs, parent) {
|
|
|
100
105
|
const feeModel = (0, StorageProvider_1.validateStorageFeeModel)(storage.feeModel);
|
|
101
106
|
logger?.log(`validated fee model ${JSON.stringify(feeModel)}`);
|
|
102
107
|
const initialFundingPlan = await prepareFundingPlanWithLiquidityPolicy(storage, [userId, vargs, xinputs, xoutputs, changeBasket, noSendChangeIn, feeModel], parent);
|
|
108
|
+
validateBrc177FundingPlan(vargs, initialFundingPlan);
|
|
103
109
|
logger?.log(`planned funding from ${initialFundingPlan.availableChangeCount} change inputs`);
|
|
104
110
|
// The selected source txids are known before the write transaction begins.
|
|
105
111
|
// Start their one batched proof read now so PXC/network wait and BEEF
|
|
106
112
|
// assembly overlap the independent transaction-record/funding/output writes.
|
|
107
113
|
// A funding-claim retry is handled below by fetching only any residual roots.
|
|
108
|
-
const allocatedBeefPrefetch = startAllocatedChangeBeefPrefetch(storage, vargs, initialFundingPlan.selected, beef, parent);
|
|
114
|
+
const allocatedBeefPrefetch = startAllocatedChangeBeefPrefetch(storage, userId, vargs, initialFundingPlan.selected, beef, parent);
|
|
109
115
|
const storageBeefBytes = storageBeef.toBinary();
|
|
110
116
|
let newTx;
|
|
111
117
|
let newTxCommitted = false;
|
|
@@ -138,7 +144,11 @@ async function createActionCore(storage, auth, vargs, parent) {
|
|
|
138
144
|
ctx.xoutputs[adjustment.fixedOutputIndex].satoshis = adjustment.satoshis;
|
|
139
145
|
logger?.log('adjusted change outputs to max possible');
|
|
140
146
|
}
|
|
141
|
-
const
|
|
147
|
+
const fixedManagedChangeSatoshis = ctx.xoutputs
|
|
148
|
+
.filter(output => output.purpose === 'change')
|
|
149
|
+
.reduce((sum, output) => sum + output.satoshis, 0);
|
|
150
|
+
const satoshis = fixedManagedChangeSatoshis +
|
|
151
|
+
funded.changeOutputs.reduce((sum, output) => sum + output.satoshis, 0) -
|
|
142
152
|
funded.allocatedChange.reduce((sum, output) => sum + output.satoshis, 0);
|
|
143
153
|
if (satoshis !== initialSatoshis) {
|
|
144
154
|
await storage.updateTransaction(newTx.transactionId, { satoshis }, trx);
|
|
@@ -158,7 +168,7 @@ async function createActionCore(storage, auth, vargs, parent) {
|
|
|
158
168
|
const committedTx = (0, utilityHelpers_1.verifyTruthy)(newTx);
|
|
159
169
|
const { allocatedChange, derivationPrefix, outputs, changeVouts, ctx } = persisted;
|
|
160
170
|
logger?.log('created new output records');
|
|
161
|
-
const
|
|
171
|
+
const preparedInputBeef = await mergeAllocatedChangeBeefs(storage, userId, vargs, allocatedChange, beef, allocatedBeefPrefetch, parent);
|
|
162
172
|
logger?.log('merged allocated change beefs');
|
|
163
173
|
const inputs = await traceStorageStep(storage, 'wallet.storage.create_action.assemble_inputs', parent, {
|
|
164
174
|
'action.fixed_input_count': ctx.xinputs.length,
|
|
@@ -177,9 +187,13 @@ async function createActionCore(storage, auth, vargs, parent) {
|
|
|
177
187
|
inputs,
|
|
178
188
|
outputs,
|
|
179
189
|
derivationPrefix,
|
|
180
|
-
inputBeef,
|
|
190
|
+
inputBeef: preparedInputBeef.inputBeef,
|
|
181
191
|
noSendChangeOutputVouts: vargs.isNoSend ? changeVouts : undefined
|
|
182
192
|
};
|
|
193
|
+
// This is intentionally the last foreground operation. The coordinator
|
|
194
|
+
// starts on a timer, so the createAction promise and HTTP response can
|
|
195
|
+
// resolve before any COOK verification or persistence begins.
|
|
196
|
+
enqueuePreparedBeefs(storage, preparedInputBeef.preparations);
|
|
183
197
|
logger?.groupEnd();
|
|
184
198
|
return r;
|
|
185
199
|
}
|
|
@@ -379,6 +393,49 @@ async function persistNewOutput(storage, o, tags, txTags, txBaskets, trx) {
|
|
|
379
393
|
}
|
|
380
394
|
return describeNewOutput(o, tags, txBaskets);
|
|
381
395
|
}
|
|
396
|
+
async function createRequiredOutput(storage, userId, xo, ctx, txBaskets, trx) {
|
|
397
|
+
const o = makeDefaultOutput(userId, ctx.transactionId, xo.satoshis, xo.vout);
|
|
398
|
+
if (xo.purpose === 'service-charge') {
|
|
399
|
+
const lockingScript = (0, utilityHelpers_noBuffer_1.asArray)(xo.lockingScript);
|
|
400
|
+
const now = new Date();
|
|
401
|
+
await storage.insertCommission({
|
|
402
|
+
userId,
|
|
403
|
+
transactionId: ctx.transactionId,
|
|
404
|
+
lockingScript,
|
|
405
|
+
satoshis: xo.satoshis,
|
|
406
|
+
isRedeemed: false,
|
|
407
|
+
keyOffset: (0, utilityHelpers_1.verifyTruthy)(xo.keyOffset),
|
|
408
|
+
created_at: now,
|
|
409
|
+
updated_at: now,
|
|
410
|
+
commissionId: 0
|
|
411
|
+
}, trx);
|
|
412
|
+
o.lockingScript = lockingScript;
|
|
413
|
+
o.providedBy = 'storage';
|
|
414
|
+
o.purpose = 'storage-commission';
|
|
415
|
+
o.type = 'custom';
|
|
416
|
+
o.spendable = false;
|
|
417
|
+
return { o, tags: [] };
|
|
418
|
+
}
|
|
419
|
+
if (xo.purpose === 'change') {
|
|
420
|
+
o.basketId = ctx.changeBasket.basketId;
|
|
421
|
+
o.change = true;
|
|
422
|
+
o.derivationPrefix = (0, utilityHelpers_1.verifyTruthy)(ctx.derivationPrefix);
|
|
423
|
+
o.derivationSuffix = (0, utilityHelpers_1.verifyTruthy)(xo.derivationSuffix);
|
|
424
|
+
o.providedBy = 'storage';
|
|
425
|
+
o.purpose = 'change';
|
|
426
|
+
o.type = 'P2PKH';
|
|
427
|
+
o.spendable = true;
|
|
428
|
+
return { o, tags: [] };
|
|
429
|
+
}
|
|
430
|
+
o.lockingScript = (0, utilityHelpers_noBuffer_1.asArray)(xo.lockingScript);
|
|
431
|
+
o.basketId = xo.basket ? txBaskets[xo.basket].basketId : undefined;
|
|
432
|
+
o.customInstructions = xo.customInstructions;
|
|
433
|
+
o.outputDescription = xo.outputDescription;
|
|
434
|
+
o.providedBy = xo.providedBy;
|
|
435
|
+
o.purpose = xo.purpose || '';
|
|
436
|
+
o.type = 'custom';
|
|
437
|
+
return { o, tags: xo.tags };
|
|
438
|
+
}
|
|
382
439
|
async function createNewOutputs(storage, userId, vargs, ctx, changeOutputs, trx) {
|
|
383
440
|
const txBaskets = {};
|
|
384
441
|
const basketNames = [...new Set(ctx.xoutputs.map(x => x.basket).filter((v) => !!v))];
|
|
@@ -388,39 +445,7 @@ async function createNewOutputs(storage, userId, vargs, ctx, changeOutputs, trx)
|
|
|
388
445
|
Object.assign(txTags, await storage.findOrInsertOutputTagsBulk(userId, tagNames, trx));
|
|
389
446
|
const newOutputs = [];
|
|
390
447
|
for (const xo of ctx.xoutputs) {
|
|
391
|
-
|
|
392
|
-
if (xo.purpose === 'service-charge') {
|
|
393
|
-
const now = new Date();
|
|
394
|
-
await storage.insertCommission({
|
|
395
|
-
userId,
|
|
396
|
-
transactionId: ctx.transactionId,
|
|
397
|
-
lockingScript,
|
|
398
|
-
satoshis: xo.satoshis,
|
|
399
|
-
isRedeemed: false,
|
|
400
|
-
keyOffset: (0, utilityHelpers_1.verifyTruthy)(xo.keyOffset),
|
|
401
|
-
created_at: now,
|
|
402
|
-
updated_at: now,
|
|
403
|
-
commissionId: 0
|
|
404
|
-
}, trx);
|
|
405
|
-
const o = makeDefaultOutput(userId, ctx.transactionId, xo.satoshis, xo.vout);
|
|
406
|
-
o.lockingScript = lockingScript;
|
|
407
|
-
o.providedBy = 'storage';
|
|
408
|
-
o.purpose = 'storage-commission';
|
|
409
|
-
o.type = 'custom';
|
|
410
|
-
o.spendable = false;
|
|
411
|
-
newOutputs.push({ o, tags: [] });
|
|
412
|
-
}
|
|
413
|
-
else {
|
|
414
|
-
const o = makeDefaultOutput(userId, ctx.transactionId, xo.satoshis, xo.vout);
|
|
415
|
-
o.lockingScript = lockingScript;
|
|
416
|
-
o.basketId = xo.basket ? txBaskets[xo.basket].basketId : undefined;
|
|
417
|
-
o.customInstructions = xo.customInstructions;
|
|
418
|
-
o.outputDescription = xo.outputDescription;
|
|
419
|
-
o.providedBy = xo.providedBy;
|
|
420
|
-
o.purpose = xo.purpose || '';
|
|
421
|
-
o.type = 'custom';
|
|
422
|
-
newOutputs.push({ o, tags: xo.tags });
|
|
423
|
-
}
|
|
448
|
+
newOutputs.push(await createRequiredOutput(storage, userId, xo, ctx, txBaskets, trx));
|
|
424
449
|
}
|
|
425
450
|
for (const o of changeOutputs) {
|
|
426
451
|
o.spendable = true;
|
|
@@ -464,6 +489,15 @@ async function createNewTxRecord(storage, userId, vargs, storageBeef, satoshis =
|
|
|
464
489
|
txid: undefined,
|
|
465
490
|
rawTx: undefined
|
|
466
491
|
};
|
|
492
|
+
const brc177 = vargs.brc177;
|
|
493
|
+
if (brc177?.kind === 'protected') {
|
|
494
|
+
newTx.noSendExpiryMode = brc177.expiry.mode;
|
|
495
|
+
newTx.noSendExpiryValue = brc177.expiry.value;
|
|
496
|
+
newTx.noSendExpiryDeadline = brc177.deadline;
|
|
497
|
+
newTx.noSendExpiryState = 'preparing';
|
|
498
|
+
newTx.noSendExpiryAnchorTxid = brc177.anchorTxid;
|
|
499
|
+
newTx.noSendExpiryAnchorVout = brc177.anchorVout;
|
|
500
|
+
}
|
|
467
501
|
newTx.transactionId = await storage.insertTransaction(newTx, trx);
|
|
468
502
|
const labelNames = [...new Set(vargs.labels)];
|
|
469
503
|
const labels = await storage.findOrInsertTxLabelsBulk(userId, labelNames, trx);
|
|
@@ -525,6 +559,22 @@ function validateRequiredOutputs(storage, userId, vargs) {
|
|
|
525
559
|
keyOffset
|
|
526
560
|
});
|
|
527
561
|
}
|
|
562
|
+
const brc177 = vargs.brc177;
|
|
563
|
+
if (brc177?.kind === 'funding') {
|
|
564
|
+
vout++;
|
|
565
|
+
xoutputs.push({
|
|
566
|
+
lockingScript: '00'.repeat(25),
|
|
567
|
+
satoshis: brc177.anchorSatoshis,
|
|
568
|
+
outputDescription: '',
|
|
569
|
+
basket: undefined,
|
|
570
|
+
tags: [],
|
|
571
|
+
vout,
|
|
572
|
+
providedBy: 'storage',
|
|
573
|
+
purpose: 'change',
|
|
574
|
+
derivationSuffix: undefined,
|
|
575
|
+
keyOffset: undefined
|
|
576
|
+
});
|
|
577
|
+
}
|
|
528
578
|
return xoutputs;
|
|
529
579
|
}
|
|
530
580
|
/**
|
|
@@ -720,6 +770,7 @@ async function traceStorageStep(storage, name, parent, attributes, callback) {
|
|
|
720
770
|
function makeFundingParams(args) {
|
|
721
771
|
const { storage, vargs, xinputs, xoutputs, changeBasket, feeModel, healthyChangeCount, compatibilityFallback } = args;
|
|
722
772
|
const preferredSatoshis = Math.max(1, changeBasket.minimumDesiredUTXOValue);
|
|
773
|
+
const brc177 = vargs.brc177;
|
|
723
774
|
return {
|
|
724
775
|
fixedInputs: xinputs.map(input => ({
|
|
725
776
|
satoshis: input.satoshis,
|
|
@@ -738,7 +789,9 @@ function makeFundingParams(args) {
|
|
|
738
789
|
changeLockingScriptLength: 25,
|
|
739
790
|
changeUnlockingScriptLength: 107,
|
|
740
791
|
targetNetCount: changeBasket.numberOfDesiredUTXOs - healthyChangeCount,
|
|
741
|
-
|
|
792
|
+
// The planner requires a positive cap. The exact anchor leaves no surplus,
|
|
793
|
+
// and validateBrc177FundingPlan below independently rejects any change.
|
|
794
|
+
maxChangeOutputs: brc177?.kind === 'protected' ? 1 : storage.managedChangePolicy.maxOutputsPerAction,
|
|
742
795
|
surplusPoolShaping: !compatibilityFallback,
|
|
743
796
|
maxMigrationInputs: compatibilityFallback ? 0 : storage.managedChangePolicy.migrationInputsPerAction,
|
|
744
797
|
randomVals: vargs.randomVals
|
|
@@ -747,7 +800,10 @@ function makeFundingParams(args) {
|
|
|
747
800
|
async function buildFundingPlan(storage, context, candidates, eligibleStatuses, policyTier, compatibilityFallback, parent) {
|
|
748
801
|
const [userId, vargs, xinputs, xoutputs, changeBasket, noSendChangeIn, feeModel] = context;
|
|
749
802
|
const noSendIds = new Set(noSendChangeIn.map(output => output.outputId));
|
|
803
|
+
const brc177 = vargs.brc177;
|
|
750
804
|
const available = candidates.filter(output => !noSendIds.has(output.outputId) && eligibleStatuses.includes(output.transactionStatus));
|
|
805
|
+
if (brc177?.kind === 'protected')
|
|
806
|
+
available.length = 0;
|
|
751
807
|
const preferredSatoshis = Math.max(1, changeBasket.minimumDesiredUTXOValue);
|
|
752
808
|
const healthyChangeCount = compatibilityFallback
|
|
753
809
|
? // Preserve the legacy target-count input exactly, including noSendChange
|
|
@@ -1100,6 +1156,12 @@ async function fundNewTransactionSdk(storage, userId, vargs, ctx, initialPlan, p
|
|
|
1100
1156
|
};
|
|
1101
1157
|
// Generate a derivation prefix for the payment
|
|
1102
1158
|
const derivationPrefix = randomDerivation(16);
|
|
1159
|
+
ctx.derivationPrefix = derivationPrefix;
|
|
1160
|
+
for (const output of ctx.xoutputs) {
|
|
1161
|
+
if (output.purpose === 'change' && output.derivationSuffix == null) {
|
|
1162
|
+
output.derivationSuffix = randomDerivation(16);
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1103
1165
|
const r = {
|
|
1104
1166
|
maxPossibleSatoshisAdjustment: gcr.maxPossibleSatoshisAdjustment,
|
|
1105
1167
|
allocatedChange,
|
|
@@ -1134,6 +1196,21 @@ async function fundNewTransactionSdk(storage, userId, vargs, ctx, initialPlan, p
|
|
|
1134
1196
|
};
|
|
1135
1197
|
return r;
|
|
1136
1198
|
}
|
|
1199
|
+
function validateBrc177FundingPlan(vargs, plan) {
|
|
1200
|
+
if (vargs.brc177?.kind !== 'protected')
|
|
1201
|
+
return;
|
|
1202
|
+
if (plan.selected.length !== 1 || vargs.options.noSendChange.length !== 1) {
|
|
1203
|
+
throw new WERR_errors_1.WERR_INVALID_OPERATION('BRC-177 protected action must use exactly one revocation anchor');
|
|
1204
|
+
}
|
|
1205
|
+
const selected = plan.selected[0];
|
|
1206
|
+
const anchor = vargs.options.noSendChange[0];
|
|
1207
|
+
if (selected.txid !== anchor.txid || selected.vout !== anchor.vout) {
|
|
1208
|
+
throw new WERR_errors_1.WERR_INVALID_OPERATION('BRC-177 protected action selected a non-anchor wallet input');
|
|
1209
|
+
}
|
|
1210
|
+
if (plan.result.changeOutputs.length !== 0) {
|
|
1211
|
+
throw new WERR_errors_1.WERR_INVALID_OPERATION('BRC-177 protected action must not create wallet change');
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1137
1214
|
/**
|
|
1138
1215
|
* Avoid returning any known raw transaction data by converting any known transaction
|
|
1139
1216
|
* in the `beef` to txidOnly.
|
|
@@ -1166,19 +1243,77 @@ function makeKnownTxidLookup(knownTxids) {
|
|
|
1166
1243
|
return knownTxids.includes(txid);
|
|
1167
1244
|
};
|
|
1168
1245
|
}
|
|
1246
|
+
function preparedBeefStorage(storage) {
|
|
1247
|
+
return storage;
|
|
1248
|
+
}
|
|
1249
|
+
function appendPreparedBeefPreparation(preparations, writeEnabled, userId, rootTxids) {
|
|
1250
|
+
if (!writeEnabled)
|
|
1251
|
+
return;
|
|
1252
|
+
preparations.push({ userId, rootTxids });
|
|
1253
|
+
}
|
|
1254
|
+
function enqueuePreparedBeefs(storage, preparations) {
|
|
1255
|
+
const extension = preparedBeefStorage(storage);
|
|
1256
|
+
if (typeof extension.enqueuePreparedBeef !== 'function')
|
|
1257
|
+
return;
|
|
1258
|
+
for (const preparation of preparations)
|
|
1259
|
+
extension.enqueuePreparedBeef.call(storage, preparation);
|
|
1260
|
+
}
|
|
1261
|
+
async function loadAllocatedChangeBeef(storage, userId, txids, options, parent) {
|
|
1262
|
+
const extension = preparedBeefStorage(storage);
|
|
1263
|
+
const readEnabled = extension.preparedBeefReadsEnabled?.call(storage) === true;
|
|
1264
|
+
const writeEnabled = extension.preparedBeefWritesEnabled?.call(storage) === true;
|
|
1265
|
+
const lookup = readEnabled && typeof extension.lookupPreparedBeefs === 'function'
|
|
1266
|
+
? await extension.lookupPreparedBeefs.call(storage, userId, txids, parent)
|
|
1267
|
+
: {
|
|
1268
|
+
beef: new sdk_1.Beef(),
|
|
1269
|
+
hitTxids: [],
|
|
1270
|
+
missingTxids: [...new Set(txids)],
|
|
1271
|
+
corruptCount: 0,
|
|
1272
|
+
byteLength: 0
|
|
1273
|
+
};
|
|
1274
|
+
const beef = lookup.beef;
|
|
1275
|
+
const preparations = [];
|
|
1276
|
+
if (lookup.missingTxids.length > 0) {
|
|
1277
|
+
const fetched = await storage.getBeefForTransactions(lookup.missingTxids, {
|
|
1278
|
+
...options,
|
|
1279
|
+
mergeToBeef: undefined
|
|
1280
|
+
});
|
|
1281
|
+
if (lookup.hitTxids.length === 0) {
|
|
1282
|
+
// Preserve the original disabled/cold path without merging into an
|
|
1283
|
+
// otherwise empty BEEF solely for this optimization.
|
|
1284
|
+
appendPreparedBeefPreparation(preparations, writeEnabled, userId, lookup.missingTxids);
|
|
1285
|
+
return {
|
|
1286
|
+
beef: fetched,
|
|
1287
|
+
preparations,
|
|
1288
|
+
preparedHitCount: 0,
|
|
1289
|
+
canonicalFetchCount: lookup.missingTxids.length
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
beef.mergeBeef(fetched);
|
|
1293
|
+
// A caller's knownTxids may leave placeholders which are unsuitable for a
|
|
1294
|
+
// reusable artifact. Rebuild those roots strictly in the worker.
|
|
1295
|
+
appendPreparedBeefPreparation(preparations, writeEnabled, userId, lookup.missingTxids);
|
|
1296
|
+
}
|
|
1297
|
+
return {
|
|
1298
|
+
beef,
|
|
1299
|
+
preparations,
|
|
1300
|
+
preparedHitCount: lookup.hitTxids.length,
|
|
1301
|
+
canonicalFetchCount: lookup.missingTxids.length
|
|
1302
|
+
};
|
|
1303
|
+
}
|
|
1169
1304
|
function missingAllocatedChangeTxids(allocatedChange, beef, knownTxids) {
|
|
1170
1305
|
const hasKnownTxid = makeKnownTxidLookup(knownTxids);
|
|
1171
1306
|
return Array.from(new Set(allocatedChange
|
|
1172
1307
|
.map(output => (0, utilityHelpers_1.verifyTruthy)(output.txid))
|
|
1173
1308
|
.filter(txid => beef.findTxid(txid) == null && !hasKnownTxid(txid))));
|
|
1174
1309
|
}
|
|
1175
|
-
function startAllocatedChangeBeefPrefetch(storage, vargs, allocatedChange, beef, parent) {
|
|
1310
|
+
function startAllocatedChangeBeefPrefetch(storage, userId, vargs, allocatedChange, beef, parent) {
|
|
1176
1311
|
if (vargs.options.returnTXIDOnly)
|
|
1177
|
-
return Promise.resolve({ sourceCount: 0, txids: [] });
|
|
1312
|
+
return Promise.resolve({ sourceCount: 0, txids: [], preparations: [] });
|
|
1178
1313
|
const knownTxids = vargs.options.knownTxids ?? [];
|
|
1179
1314
|
const missing = missingAllocatedChangeTxids(allocatedChange, beef, knownTxids);
|
|
1180
1315
|
if (missing.length === 0)
|
|
1181
|
-
return Promise.resolve({ sourceCount: 0, txids: [] });
|
|
1316
|
+
return Promise.resolve({ sourceCount: 0, txids: [], preparations: [] });
|
|
1182
1317
|
const options = {
|
|
1183
1318
|
trustSelf: undefined,
|
|
1184
1319
|
knownTxids,
|
|
@@ -1192,15 +1327,22 @@ function startAllocatedChangeBeefPrefetch(storage, vargs, allocatedChange, beef,
|
|
|
1192
1327
|
'beef.missing_source_count': missing.length,
|
|
1193
1328
|
'beef.storage_batch_count': missing.length === 0 ? 0 : 1
|
|
1194
1329
|
}, async (span) => {
|
|
1195
|
-
const fetched = await storage
|
|
1330
|
+
const fetched = await loadAllocatedChangeBeef(storage, userId, missing, options, parent);
|
|
1196
1331
|
span?.end({
|
|
1197
1332
|
attributes: {
|
|
1198
|
-
'beef.fetched_tx_count': fetched.txs.length,
|
|
1199
|
-
'beef.fetched_bump_count': fetched.bumps.length
|
|
1333
|
+
'beef.fetched_tx_count': fetched.beef.txs.length,
|
|
1334
|
+
'beef.fetched_bump_count': fetched.beef.bumps.length,
|
|
1335
|
+
'beef.prepared_hit_count': fetched.preparedHitCount,
|
|
1336
|
+
'beef.canonical_fetch_count': fetched.canonicalFetchCount
|
|
1200
1337
|
}
|
|
1201
1338
|
});
|
|
1202
1339
|
return fetched;
|
|
1203
|
-
}).then(prefetched => ({
|
|
1340
|
+
}).then(prefetched => ({
|
|
1341
|
+
beef: prefetched.beef,
|
|
1342
|
+
preparations: prefetched.preparations,
|
|
1343
|
+
sourceCount: missing.length,
|
|
1344
|
+
txids: missing
|
|
1345
|
+
}), error => ({ error, preparations: [], sourceCount: missing.length, txids: missing }));
|
|
1204
1346
|
}
|
|
1205
1347
|
function sameTxids(left, right) {
|
|
1206
1348
|
if (left.length !== right.length)
|
|
@@ -1208,7 +1350,7 @@ function sameTxids(left, right) {
|
|
|
1208
1350
|
const expected = new Set(left);
|
|
1209
1351
|
return right.every(txid => expected.has(txid));
|
|
1210
1352
|
}
|
|
1211
|
-
async function mergeAllocatedChangeBeefs(storage, vargs, allocatedChange, beef, prefetch, parent) {
|
|
1353
|
+
async function mergeAllocatedChangeBeefs(storage, userId, vargs, allocatedChange, beef, prefetch, parent) {
|
|
1212
1354
|
const options = {
|
|
1213
1355
|
trustSelf: undefined,
|
|
1214
1356
|
knownTxids: vargs.options.knownTxids,
|
|
@@ -1219,7 +1361,7 @@ async function mergeAllocatedChangeBeefs(storage, vargs, allocatedChange, beef,
|
|
|
1219
1361
|
minProofLevel: undefined
|
|
1220
1362
|
};
|
|
1221
1363
|
if (vargs.options.returnTXIDOnly)
|
|
1222
|
-
return undefined;
|
|
1364
|
+
return { inputBeef: undefined, preparations: [] };
|
|
1223
1365
|
const knownTxids = vargs.options.knownTxids ?? [];
|
|
1224
1366
|
const requiredBeforePrefetch = missingAllocatedChangeTxids(allocatedChange, beef, knownTxids);
|
|
1225
1367
|
const prefetched = await traceStorageStep(storage, 'wallet.storage.create_action.beef_prefetch_join', parent, { 'beef.prefetch_source_count': 0 }, async (span) => {
|
|
@@ -1235,6 +1377,7 @@ async function mergeAllocatedChangeBeefs(storage, vargs, allocatedChange, beef,
|
|
|
1235
1377
|
throw prefetched.error;
|
|
1236
1378
|
if (usePrefetch && prefetched.beef != null)
|
|
1237
1379
|
beef.mergeBeef(prefetched.beef);
|
|
1380
|
+
const preparations = usePrefetch ? [...prefetched.preparations] : [];
|
|
1238
1381
|
// If a concurrent spender forced the funding claim to be replanned, only
|
|
1239
1382
|
// the newly selected roots remain. The normal uncontended path is empty.
|
|
1240
1383
|
const missing = missingAllocatedChangeTxids(allocatedChange, beef, knownTxids);
|
|
@@ -1248,7 +1391,9 @@ async function mergeAllocatedChangeBeefs(storage, vargs, allocatedChange, beef,
|
|
|
1248
1391
|
'beef.prefetch_reused': usePrefetch
|
|
1249
1392
|
}, async (span) => {
|
|
1250
1393
|
if (missing.length > 0) {
|
|
1251
|
-
|
|
1394
|
+
const loaded = await loadAllocatedChangeBeef(storage, userId, missing, options, parent);
|
|
1395
|
+
fetched = loaded.beef;
|
|
1396
|
+
preparations.push(...loaded.preparations);
|
|
1252
1397
|
}
|
|
1253
1398
|
span?.end({
|
|
1254
1399
|
attributes: {
|
|
@@ -1268,7 +1413,7 @@ async function mergeAllocatedChangeBeefs(storage, vargs, allocatedChange, beef,
|
|
|
1268
1413
|
}
|
|
1269
1414
|
});
|
|
1270
1415
|
});
|
|
1271
|
-
|
|
1416
|
+
const inputBeef = await traceStorageStep(storage, 'wallet.storage.create_action.beef_trim_serialize', parent, {
|
|
1272
1417
|
'beef.tx_count': beef.txs.length,
|
|
1273
1418
|
'beef.bump_count': beef.bumps.length,
|
|
1274
1419
|
'beef.known_txid_count': knownTxids.length
|
|
@@ -1277,5 +1422,6 @@ async function mergeAllocatedChangeBeefs(storage, vargs, allocatedChange, beef,
|
|
|
1277
1422
|
span?.end({ attributes: { 'beef.result_bytes': result?.length ?? 0 } });
|
|
1278
1423
|
return result;
|
|
1279
1424
|
});
|
|
1425
|
+
return { inputBeef, preparations };
|
|
1280
1426
|
}
|
|
1281
1427
|
//# sourceMappingURL=createAction.js.map
|