@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.
Files changed (155) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +47 -0
  3. package/docs/managed-change-liquidity.md +7 -0
  4. package/docs/no-send-expiry.md +167 -0
  5. package/docs/prepared-beef.md +197 -0
  6. package/docs/setup.md +11 -0
  7. package/docs/storage.md +11 -0
  8. package/out/src/Setup.d.ts +3 -0
  9. package/out/src/Setup.d.ts.map +1 -1
  10. package/out/src/Setup.js +1 -0
  11. package/out/src/Setup.js.map +1 -1
  12. package/out/src/Wallet.d.ts.map +1 -1
  13. package/out/src/Wallet.js +11 -1
  14. package/out/src/Wallet.js.map +1 -1
  15. package/out/src/WalletPermissionsManager.d.ts +30 -6
  16. package/out/src/WalletPermissionsManager.d.ts.map +1 -1
  17. package/out/src/WalletPermissionsManager.js +84 -9
  18. package/out/src/WalletPermissionsManager.js.map +1 -1
  19. package/out/src/mockchain/MockServices.d.ts +2 -0
  20. package/out/src/mockchain/MockServices.d.ts.map +1 -1
  21. package/out/src/mockchain/MockServices.js +20 -0
  22. package/out/src/mockchain/MockServices.js.map +1 -1
  23. package/out/src/monitor/Monitor.d.ts +1 -0
  24. package/out/src/monitor/Monitor.d.ts.map +1 -1
  25. package/out/src/monitor/Monitor.js +19 -3
  26. package/out/src/monitor/Monitor.js.map +1 -1
  27. package/out/src/monitor/tasks/TaskNoSendExpiry.d.ts +19 -0
  28. package/out/src/monitor/tasks/TaskNoSendExpiry.d.ts.map +1 -0
  29. package/out/src/monitor/tasks/TaskNoSendExpiry.js +47 -0
  30. package/out/src/monitor/tasks/TaskNoSendExpiry.js.map +1 -0
  31. package/out/src/monitor/tasks/index.all.d.ts +1 -0
  32. package/out/src/monitor/tasks/index.all.d.ts.map +1 -1
  33. package/out/src/monitor/tasks/index.all.js +1 -0
  34. package/out/src/monitor/tasks/index.all.js.map +1 -1
  35. package/out/src/sdk/ActionBatch.interfaces.d.ts +4 -0
  36. package/out/src/sdk/ActionBatch.interfaces.d.ts.map +1 -1
  37. package/out/src/sdk/WalletStorage.interfaces.d.ts +35 -0
  38. package/out/src/sdk/WalletStorage.interfaces.d.ts.map +1 -1
  39. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageKnex.d.ts +0 -1
  40. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageKnex.d.ts.map +1 -1
  41. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageKnex.js +23 -12
  42. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageKnex.js.map +1 -1
  43. package/out/src/signer/methods/createNoSendExpiryAction.d.ts +7 -0
  44. package/out/src/signer/methods/createNoSendExpiryAction.d.ts.map +1 -0
  45. package/out/src/signer/methods/createNoSendExpiryAction.js +249 -0
  46. package/out/src/signer/methods/createNoSendExpiryAction.js.map +1 -0
  47. package/out/src/signer/methods/signAction.d.ts.map +1 -1
  48. package/out/src/signer/methods/signAction.js +9 -0
  49. package/out/src/signer/methods/signAction.js.map +1 -1
  50. package/out/src/storage/StorageIdb.d.ts +3 -0
  51. package/out/src/storage/StorageIdb.d.ts.map +1 -1
  52. package/out/src/storage/StorageIdb.js +37 -1
  53. package/out/src/storage/StorageIdb.js.map +1 -1
  54. package/out/src/storage/StorageKnex.d.ts +48 -3
  55. package/out/src/storage/StorageKnex.d.ts.map +1 -1
  56. package/out/src/storage/StorageKnex.js +234 -14
  57. package/out/src/storage/StorageKnex.js.map +1 -1
  58. package/out/src/storage/StorageProvider.d.ts +6 -1
  59. package/out/src/storage/StorageProvider.d.ts.map +1 -1
  60. package/out/src/storage/StorageProvider.js +147 -5
  61. package/out/src/storage/StorageProvider.js.map +1 -1
  62. package/out/src/storage/StorageReaderWriter.d.ts +2 -0
  63. package/out/src/storage/StorageReaderWriter.d.ts.map +1 -1
  64. package/out/src/storage/StorageReaderWriter.js +4 -0
  65. package/out/src/storage/StorageReaderWriter.js.map +1 -1
  66. package/out/src/storage/WalletStorageManager.d.ts +11 -0
  67. package/out/src/storage/WalletStorageManager.d.ts.map +1 -1
  68. package/out/src/storage/WalletStorageManager.js +77 -12
  69. package/out/src/storage/WalletStorageManager.js.map +1 -1
  70. package/out/src/storage/idbHelpers.d.ts.map +1 -1
  71. package/out/src/storage/idbHelpers.js +14 -2
  72. package/out/src/storage/idbHelpers.js.map +1 -1
  73. package/out/src/storage/index.all.d.ts +2 -0
  74. package/out/src/storage/index.all.d.ts.map +1 -1
  75. package/out/src/storage/index.all.js +2 -0
  76. package/out/src/storage/index.all.js.map +1 -1
  77. package/out/src/storage/methods/createAction.d.ts +57 -1
  78. package/out/src/storage/methods/createAction.d.ts.map +1 -1
  79. package/out/src/storage/methods/createAction.js +195 -49
  80. package/out/src/storage/methods/createAction.js.map +1 -1
  81. package/out/src/storage/methods/noSendExpiry.d.ts +11 -0
  82. package/out/src/storage/methods/noSendExpiry.d.ts.map +1 -0
  83. package/out/src/storage/methods/noSendExpiry.js +357 -0
  84. package/out/src/storage/methods/noSendExpiry.js.map +1 -0
  85. package/out/src/storage/methods/noSendExpiryLifecycle.d.ts +14 -0
  86. package/out/src/storage/methods/noSendExpiryLifecycle.d.ts.map +1 -0
  87. package/out/src/storage/methods/noSendExpiryLifecycle.js +691 -0
  88. package/out/src/storage/methods/noSendExpiryLifecycle.js.map +1 -0
  89. package/out/src/storage/methods/preparedBeef.d.ts +104 -0
  90. package/out/src/storage/methods/preparedBeef.d.ts.map +1 -0
  91. package/out/src/storage/methods/preparedBeef.js +525 -0
  92. package/out/src/storage/methods/preparedBeef.js.map +1 -0
  93. package/out/src/storage/methods/processAction.d.ts.map +1 -1
  94. package/out/src/storage/methods/processAction.js +109 -28
  95. package/out/src/storage/methods/processAction.js.map +1 -1
  96. package/out/src/storage/methods/purgeData.d.ts.map +1 -1
  97. package/out/src/storage/methods/purgeData.js +9 -0
  98. package/out/src/storage/methods/purgeData.js.map +1 -1
  99. package/out/src/storage/methods/validateSyncProof.d.ts +24 -0
  100. package/out/src/storage/methods/validateSyncProof.d.ts.map +1 -0
  101. package/out/src/storage/methods/validateSyncProof.js +116 -0
  102. package/out/src/storage/methods/validateSyncProof.js.map +1 -0
  103. package/out/src/storage/remoting/StorageClientBase.d.ts +4 -1
  104. package/out/src/storage/remoting/StorageClientBase.d.ts.map +1 -1
  105. package/out/src/storage/remoting/StorageClientBase.js +9 -0
  106. package/out/src/storage/remoting/StorageClientBase.js.map +1 -1
  107. package/out/src/storage/remoting/StorageServer.d.ts.map +1 -1
  108. package/out/src/storage/remoting/StorageServer.js +16 -3
  109. package/out/src/storage/remoting/StorageServer.js.map +1 -1
  110. package/out/src/storage/schema/KnexMigrations.d.ts +2 -0
  111. package/out/src/storage/schema/KnexMigrations.d.ts.map +1 -1
  112. package/out/src/storage/schema/KnexMigrations.js +66 -1
  113. package/out/src/storage/schema/KnexMigrations.js.map +1 -1
  114. package/out/src/storage/schema/StorageIdbSchema.d.ts +2 -0
  115. package/out/src/storage/schema/StorageIdbSchema.d.ts.map +1 -1
  116. package/out/src/storage/schema/entities/EntityOutput.d.ts.map +1 -1
  117. package/out/src/storage/schema/entities/EntityOutput.js +13 -0
  118. package/out/src/storage/schema/entities/EntityOutput.js.map +1 -1
  119. package/out/src/storage/schema/entities/EntityProvenTx.d.ts +2 -0
  120. package/out/src/storage/schema/entities/EntityProvenTx.d.ts.map +1 -1
  121. package/out/src/storage/schema/entities/EntityProvenTx.js +33 -3
  122. package/out/src/storage/schema/entities/EntityProvenTx.js.map +1 -1
  123. package/out/src/storage/schema/entities/EntityTransaction.d.ts +26 -0
  124. package/out/src/storage/schema/entities/EntityTransaction.d.ts.map +1 -1
  125. package/out/src/storage/schema/entities/EntityTransaction.js +221 -21
  126. package/out/src/storage/schema/entities/EntityTransaction.js.map +1 -1
  127. package/out/src/storage/schema/tables/TablePreparedBeef.interfaces.d.ts +23 -0
  128. package/out/src/storage/schema/tables/TablePreparedBeef.interfaces.d.ts.map +1 -0
  129. package/out/src/storage/schema/tables/TablePreparedBeef.interfaces.js +3 -0
  130. package/out/src/storage/schema/tables/TablePreparedBeef.interfaces.js.map +1 -0
  131. package/out/src/storage/schema/tables/TableTransaction.d.ts +16 -0
  132. package/out/src/storage/schema/tables/TableTransaction.d.ts.map +1 -1
  133. package/out/src/storage/schema/tables/TableTransaction.js +14 -1
  134. package/out/src/storage/schema/tables/TableTransaction.js.map +1 -1
  135. package/out/src/utility/Argon2idBackend.d.ts +36 -0
  136. package/out/src/utility/Argon2idBackend.d.ts.map +1 -0
  137. package/out/src/utility/Argon2idBackend.js +60 -0
  138. package/out/src/utility/Argon2idBackend.js.map +1 -0
  139. package/out/src/utility/brc177NoSendExpiry.d.ts +34 -0
  140. package/out/src/utility/brc177NoSendExpiry.d.ts.map +1 -0
  141. package/out/src/utility/brc177NoSendExpiry.js +81 -0
  142. package/out/src/utility/brc177NoSendExpiry.js.map +1 -0
  143. package/out/src/utility/hashWasm.d.ts +2 -1
  144. package/out/src/utility/hashWasm.d.ts.map +1 -1
  145. package/out/src/utility/hashWasm.js +83 -1
  146. package/out/src/utility/hashWasm.js.map +1 -1
  147. package/out/src/utility/index.all.d.ts +2 -0
  148. package/out/src/utility/index.all.d.ts.map +1 -1
  149. package/out/src/utility/index.all.js +2 -0
  150. package/out/src/utility/index.all.js.map +1 -1
  151. package/out/src/utility/index.client.d.ts +2 -0
  152. package/out/src/utility/index.client.d.ts.map +1 -1
  153. package/out/src/utility/index.client.js +2 -0
  154. package/out/src/utility/index.client.js.map +1 -1
  155. 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 satoshis = funded.changeOutputs.reduce((sum, output) => sum + output.satoshis, 0) -
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 inputBeef = await mergeAllocatedChangeBeefs(storage, vargs, allocatedChange, beef, allocatedBeefPrefetch, parent);
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
- const lockingScript = (0, utilityHelpers_noBuffer_1.asArray)(xo.lockingScript);
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
- maxChangeOutputs: storage.managedChangePolicy.maxOutputsPerAction,
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.getBeefForTransactions(missing, options);
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 => ({ beef: prefetched, sourceCount: missing.length, txids: missing }), error => ({ error, sourceCount: missing.length, txids: missing }));
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
- fetched = await storage.getBeefForTransactions(missing, { ...options, mergeToBeef: undefined });
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
- return await traceStorageStep(storage, 'wallet.storage.create_action.beef_trim_serialize', parent, {
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