From aa0fb082aa8a22bb4ce85966266ce390904dc384 Mon Sep 17 00:00:00 2001 From: David Case Date: Sat, 3 Oct 2026 14:24:54 -0400 Subject: [PATCH 01/10] fix(wallet-toolbox): send and return BEEF as bytes so negotiated binary JSON carries it binaryRequests encodes only Uint8Array values, but the toolbox built its largest storage payloads as number[]: StorageClientBase.createAction set inputBEEF from pruned.toBinary(), and the signer's internalizeAction passed ab.toBinaryAtomic(txid). They travelled as JSON number arrays even with binary negotiated, and a BEEF over ~100 KB exceeded the auth middleware's 100,000-value request limit. Use the SDK's toUint8Array() / toUint8ArrayAtomic() for createAction and no-send-expiry inputBEEF and internalizeAction AtomicBEEF, and return listOutputs BEEF, createAction sourceTransaction and competingBeef as Uint8Array from Knex storage. BEEF and AtomicBEEF already allow Uint8Array, and the server's byte validation accepts it. Legacy peers that have not negotiated binary still receive number arrays. --- .../internalizeAction.storageArgs.test.ts | 45 ++++++++++ .../src/signer/methods/internalizeAction.ts | 2 +- .../src/storage/methods/createAction.ts | 5 +- .../src/storage/methods/listOutputsKnex.ts | 2 +- .../src/storage/remoting/StorageClientBase.ts | 17 +++- .../StorageClientBase.inputBeef.test.ts | 89 ++++++++++++++++++- .../test/wallet/list/listOutputs.test.ts | 12 +++ 7 files changed, 162 insertions(+), 10 deletions(-) create mode 100644 packages/wallet/wallet-toolbox/src/signer/methods/__tests/internalizeAction.storageArgs.test.ts diff --git a/packages/wallet/wallet-toolbox/src/signer/methods/__tests/internalizeAction.storageArgs.test.ts b/packages/wallet/wallet-toolbox/src/signer/methods/__tests/internalizeAction.storageArgs.test.ts new file mode 100644 index 0000000000..e100a0b2fd --- /dev/null +++ b/packages/wallet/wallet-toolbox/src/signer/methods/__tests/internalizeAction.storageArgs.test.ts @@ -0,0 +1,45 @@ +import { Beef, MerklePath, Script, Transaction, UnlockingScript } from '@bsv/sdk' +import { internalizeAction } from '../internalizeAction' +import type { Wallet } from '../../../Wallet' +import { stringifyJsonRpc } from '../../../storage/remoting/BinaryJson' + +describe('signer internalizeAction storage arguments', () => { + test('passes the AtomicBEEF to storage as bytes so negotiated binary JSON carries it compactly', async () => { + const tx = new Transaction() + tx.addInput({ sourceTXID: '00'.repeat(32), sourceOutputIndex: 0, unlockingScript: new UnlockingScript() }) + tx.addOutput({ satoshis: 1000, lockingScript: Script.fromHex('51') }) + const txid = tx.id('hex') + const beef = new Beef() + beef.mergeBump(new MerklePath(800000, [[{ offset: 0, hash: txid, txid: true }]])) + beef.mergeTransaction(tx) + + const received: unknown[] = [] + const wallet = { + getServices: () => ({ getChainTracker: async () => ({ isValidRootForHeight: async () => true }) }), + storage: { + internalizeAction: async (args: unknown) => { + received.push(args) + return { accepted: true, isMerge: false, txid, satoshis: 0 } + } + } + } as unknown as Wallet + + await internalizeAction(wallet, { identityKey: `02${'11'.repeat(32)}`, userId: 1 }, { + tx: beef.toBinaryAtomic(txid), + outputs: [ + { + outputIndex: 0, + protocol: 'basket insertion', + insertionRemittance: { basket: 'tokens', tags: [] } + } + ], + description: 'internalize as bytes' + }) + + const sent = received[0] as { tx: unknown } + expect(sent.tx).toBeInstanceOf(Uint8Array) + expect(Array.from(sent.tx as Uint8Array)).toEqual(beef.toBinaryAtomic(txid)) + const wire = JSON.parse(stringifyJsonRpc({ params: [sent] }, true)) + expect(wire.params[0].tx.$bsvBinary).toBe('base64') + }) +}) diff --git a/packages/wallet/wallet-toolbox/src/signer/methods/internalizeAction.ts b/packages/wallet/wallet-toolbox/src/signer/methods/internalizeAction.ts index d4c5a3aba8..28fd7bf912 100644 --- a/packages/wallet/wallet-toolbox/src/signer/methods/internalizeAction.ts +++ b/packages/wallet/wallet-toolbox/src/signer/methods/internalizeAction.ts @@ -71,7 +71,7 @@ export async function internalizeAction( const r: StorageInternalizeActionResult = await wallet.storage.internalizeAction({ ...args, - tx: ab.toBinaryAtomic(txid) + tx: ab.toUint8ArrayAtomic(txid) }) return r diff --git a/packages/wallet/wallet-toolbox/src/storage/methods/createAction.ts b/packages/wallet/wallet-toolbox/src/storage/methods/createAction.ts index 2725c5a98d..274d07b5b1 100644 --- a/packages/wallet/wallet-toolbox/src/storage/methods/createAction.ts +++ b/packages/wallet/wallet-toolbox/src/storage/methods/createAction.ts @@ -445,10 +445,11 @@ async function buildSdkInputFromOutput( unlockLen: number | undefined ): Promise { if (i == null && !unlockLen) throw new WERR_INTERNAL(`vin ${vin} non-fixedInput without unlockLen`) - const sourceTransaction = + const sourceRawTx = vargs.includeAllSourceTransactions && vargs.isSignAction ? await storage.getRawTxOfKnownValidTransaction(o.txid) : undefined + const sourceTransaction = sourceRawTx == null ? undefined : Uint8Array.from(sourceRawTx) return { vin, sourceTxid: o.txid!, @@ -504,7 +505,7 @@ async function createNewInputs( if (doubleSpendTxid) { const beef = await getCompetingBeefForReview(storage, doubleSpendTxid) throw new WERR_REVIEW_ACTIONS( - [{ txid: '', status: 'doubleSpend', competingTxs: [doubleSpendTxid], competingBeef: beef.toBinary() }], + [{ txid: '', status: 'doubleSpend', competingTxs: [doubleSpendTxid], competingBeef: beef.toUint8Array() }], [] ) } diff --git a/packages/wallet/wallet-toolbox/src/storage/methods/listOutputsKnex.ts b/packages/wallet/wallet-toolbox/src/storage/methods/listOutputsKnex.ts index 3dd8d39f99..4f652e1f36 100644 --- a/packages/wallet/wallet-toolbox/src/storage/methods/listOutputsKnex.ts +++ b/packages/wallet/wallet-toolbox/src/storage/methods/listOutputsKnex.ts @@ -334,6 +334,6 @@ export async function listOutputs( await hydrateKnexWalletOutput(dsk, output, vargs, labelsByTransactionId, tagsByOutputId, beef, trx) ) } - if (vargs.includeTransactions) result.BEEF = beef.toBinary() + if (vargs.includeTransactions) result.BEEF = beef.toUint8Array() return result } diff --git a/packages/wallet/wallet-toolbox/src/storage/remoting/StorageClientBase.ts b/packages/wallet/wallet-toolbox/src/storage/remoting/StorageClientBase.ts index bb326b5b10..2f75c0efb1 100644 --- a/packages/wallet/wallet-toolbox/src/storage/remoting/StorageClientBase.ts +++ b/packages/wallet/wallet-toolbox/src/storage/remoting/StorageClientBase.ts @@ -95,6 +95,12 @@ import { pruneBeefForTxids } from '../../utility/beefForTxids' const syncChunkResponseRetryLimit = 4 const minimumSyncChunkRoughSize = 64 * 1024 +/** Hold proof data as bytes so negotiated binary JSON carries it compactly. */ +function withBeefBytes(args: T): T { + if (args.inputBEEF == null || args.inputBEEF instanceof Uint8Array) return args + return { ...args, inputBEEF: Uint8Array.from(args.inputBEEF) } +} + function isSyncChunkResponseTooLarge(error: unknown): boolean { return error instanceof Error && /WalletStorageClient rpcCall: network error 413(?:\s|$)/.test(error.message) } @@ -499,13 +505,13 @@ export abstract class StorageClientBase implements WalletStorageProvider { if (pruned != null) { args = { ...args, - inputBEEF: pruned.toBinary() + inputBEEF: pruned.toUint8Array() } } } } } - return await this.rpcCall('createAction', [auth, args]) + return await this.rpcCall('createAction', [auth, withBeefBytes(args)]) } /** @@ -523,14 +529,17 @@ export abstract class StorageClientBase implements WalletStorageProvider { } async prepareNoSendExpiry(auth: AuthId, args: ValidCreateActionArgs): Promise { - return await this.rpcCall('prepareNoSendExpiry', [auth, args]) + return await this.rpcCall('prepareNoSendExpiry', [auth, withBeefBytes(args)]) } async activateNoSendExpiry( auth: AuthId, args: StorageActivateNoSendExpiryArgs ): Promise { - return await this.rpcCall('activateNoSendExpiry', [auth, args]) + return await this.rpcCall('activateNoSendExpiry', [ + auth, + { ...args, target: withBeefBytes(args.target) } + ]) } async armNoSendExpiry(auth: AuthId, args: StorageArmNoSendExpiryArgs): Promise { diff --git a/packages/wallet/wallet-toolbox/src/storage/remoting/__test/StorageClientBase.inputBeef.test.ts b/packages/wallet/wallet-toolbox/src/storage/remoting/__test/StorageClientBase.inputBeef.test.ts index 4c67dbcbda..abb7358f5a 100644 --- a/packages/wallet/wallet-toolbox/src/storage/remoting/__test/StorageClientBase.inputBeef.test.ts +++ b/packages/wallet/wallet-toolbox/src/storage/remoting/__test/StorageClientBase.inputBeef.test.ts @@ -1,5 +1,6 @@ import { BEEF_V1, Beef, Script, Transaction, UnlockingScript, Validation, type WalletInterface } from '@bsv/sdk' import { StorageClientBase } from '../StorageClientBase' +import { stringifyJsonRpc } from '../BinaryJson' class CapturingStorageClient extends StorageClientBase { calls: Array<{ method: string; params: unknown[] }> = [] @@ -93,8 +94,92 @@ describe('StorageClientBase createAction inputBEEF pruning', () => { await client.createAction(auth, args) const sentArgs = client.calls[0].params[1] as Validation.ValidCreateActionArgs - expect(sentArgs).toBe(args) - expect(sentArgs.inputBEEF).toBe(originalBytes) + expect(sentArgs.inputBEEF).toBeInstanceOf(Uint8Array) + expect(Array.from(sentArgs.inputBEEF!)).toEqual(Array.from(originalBytes!)) + expect(args.inputBEEF).toBe(originalBytes) + }) + + test('passes inputBEEF that is already bytes through unchanged', async () => { + const required = makeTransaction(1000) + const beef = new Beef() + beef.mergeTransaction(required) + const args = Validation.validateCreateActionArgs({ + description: 'forward byte proof data', + inputs: [ + { + outpoint: `${required.id('hex')}.0`, + unlockingScript: '00', + inputDescription: 'declared byte input' + } + ], + inputBEEF: beef.toUint8Array(), + outputs: [{ satoshis: 1, lockingScript: '51', outputDescription: 'replacement output' }] + }) + const client = new CapturingStorageClient({} as WalletInterface, 'https://storage.example.test') + + await client.createAction(auth, args) + + expect(client.calls[0].params[1]).toBe(args) + }) + + test('sends pruned inputBEEF as bytes that negotiated binary JSON tags', async () => { + const required = makeTransaction(1000) + const beef = new Beef() + beef.mergeTransaction(required) + beef.mergeTransaction(makeTransaction(2000)) + const args = Validation.validateCreateActionArgs({ + description: 'prune and send bytes', + inputs: [ + { + outpoint: `${required.id('hex')}.0`, + unlockingScript: '00', + inputDescription: 'declared input' + } + ], + inputBEEF: beef.toBinary(), + outputs: [{ satoshis: 1, lockingScript: '51', outputDescription: 'replacement output' }] + }) + const client = new CapturingStorageClient({} as WalletInterface, 'https://storage.example.test') + + await client.createAction(auth, args) + + const sentArgs = client.calls[0].params[1] as Validation.ValidCreateActionArgs + expect(sentArgs.inputBEEF).toBeInstanceOf(Uint8Array) + const wire = JSON.parse(stringifyJsonRpc({ params: client.calls[0].params }, true)) + expect(wire.params[1].inputBEEF.$bsvBinary).toBe('base64') + }) + + test('sends no-send expiry inputBEEF as bytes', async () => { + const required = makeTransaction(1000) + const beef = new Beef() + beef.mergeTransaction(required) + const target = Validation.validateCreateActionArgs({ + description: 'expiry target with proof data', + inputs: [ + { + outpoint: `${required.id('hex')}.0`, + unlockingScript: '00', + inputDescription: 'declared input' + } + ], + inputBEEF: beef.toBinary(), + outputs: [{ satoshis: 1, lockingScript: '51', outputDescription: 'replacement output' }] + }) + const client = new CapturingStorageClient({} as WalletInterface, 'https://storage.example.test') + + await client.prepareNoSendExpiry(auth, target) + await client.activateNoSendExpiry(auth, { + target, + fundingReference: 'ref', + fundingTxid: '33'.repeat(32), + anchorVout: 0 + }) + + expect((client.calls[0].params[1] as Validation.ValidCreateActionArgs).inputBEEF).toBeInstanceOf(Uint8Array) + expect( + (client.calls[1].params[1] as { target: Validation.ValidCreateActionArgs }).target.inputBEEF + ).toBeInstanceOf(Uint8Array) + expect(target.inputBEEF).not.toBeInstanceOf(Uint8Array) }) test('preserves BEEF V1 when client-side pruning is required', async () => { diff --git a/packages/wallet/wallet-toolbox/test/wallet/list/listOutputs.test.ts b/packages/wallet/wallet-toolbox/test/wallet/list/listOutputs.test.ts index 22206d3144..872752e06a 100644 --- a/packages/wallet/wallet-toolbox/test/wallet/list/listOutputs.test.ts +++ b/packages/wallet/wallet-toolbox/test/wallet/list/listOutputs.test.ts @@ -4,8 +4,10 @@ import { ListOutputsArgs, OriginatorDomainNameStringUnder250Bytes, OutputTagStringUnder300Bytes, + Validation, WalletOutput } from '@bsv/sdk' +import { StorageKnex } from '../../../src/storage/StorageKnex' import { _tu, TestWalletProviderNoSetup } from '../../utils/TestUtilsWalletStorage' import path from 'node:path' @@ -257,6 +259,16 @@ describe('listOutputs test', () => { } }) + test('8b_BEEF from Knex storage is bytes so negotiated binary JSON carries it compactly', async () => { + for (const { storage } of ctxs) { + if (!(storage.getActive() instanceof StorageKnex)) continue + const vargs = Validation.validateListOutputsArgs({ basket: 'default', include: 'entire transactions' }) + const r = await storage.listOutputs(vargs) + expect(r.BEEF).toBeInstanceOf(Uint8Array) + expect(Beef.fromBinary(r.BEEF!).txs.length).toBeGreaterThan(0) + } + }) + test('9_labels for babbage_protocol_perm', async () => { for (const { wallet } of ctxs) { { From 3c6cdacf1420699aabefe87e127fae31cc4aef2a Mon Sep 17 00:00:00 2001 From: David Case Date: Sat, 3 Oct 2026 15:21:48 -0400 Subject: [PATCH 02/10] fix(wallet-toolbox): size the BRC-177 anchor to cover the reclaim BRC-177 Prefunding step 2 requires the anchor to be large enough both to fund the protected action and to be reclaimed, net of a reclaim fee. The anchor was sized for the protected action only, so value-neutral actions (e.g. a 1-sat token transfer) were refused because the anchor could not pay for its own reclaim. Size the anchor as the larger of the protected action's need and the reclaim floor (reclaim fee plus minimum reclaim output). The protected action carries no change, so the funding planner now runs with a zero change cap and pays any surplus as fee. generateChangeSdk accepts maxChangeOutputs: 0 to mean "create no change". Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01R53QzSzGv4LkfmxBd4URiD --- .../GenerateChange/generateChangeSdk.test.ts | 24 +++ .../src/storage/methods/createAction.ts | 7 +- .../src/storage/methods/generateChange.ts | 10 +- .../src/storage/methods/noSendExpiry.ts | 25 ++- .../test/Wallet/action/noSendExpiry.test.ts | 144 ++++++++++++++++-- 5 files changed, 183 insertions(+), 27 deletions(-) diff --git a/packages/wallet/wallet-toolbox/src/storage/methods/__test/GenerateChange/generateChangeSdk.test.ts b/packages/wallet/wallet-toolbox/src/storage/methods/__test/GenerateChange/generateChangeSdk.test.ts index f70e19119c..1a93b9d944 100644 --- a/packages/wallet/wallet-toolbox/src/storage/methods/__test/GenerateChange/generateChangeSdk.test.ts +++ b/packages/wallet/wallet-toolbox/src/storage/methods/__test/GenerateChange/generateChangeSdk.test.ts @@ -1087,6 +1087,30 @@ describe('generateChange tests', () => { expectTransactionSize(params, r) }) + test.each([true, false])( + '9a2 maxChangeOutputs 0 creates no change and pays the surplus as fee (surplusPoolShaping=%s)', + async surplusPoolShaping => { + const params: GenerateChangeSdkParams = { + ...defParams, + fixedInputs: [{ satoshis: 1, unlockingScriptLength: 1 }], + fixedOutputs: [{ satoshis: 1, lockingScriptLength: 1 }], + feeModel: { model: 'sat/kb', value: 1 }, + targetNetCount: 8, + maxChangeOutputs: 0, + surplusPoolShaping + } + const { allocateChangeInput, releaseChangeInput } = generateChangeSdkMakeStorage([{ satoshis: 194, outputId: 1 }]) + + const r = await generateChangeSdk(params, allocateChangeInput, releaseChangeInput) + + expect(r.allocatedChangeInputs).toMatchObject([{ satoshis: 194, outputId: 1 }]) + expect(r.changeOutputs).toHaveLength(0) + expect(r.fee).toBe(194) + expect(r.fee).toBeGreaterThan(Math.ceil((r.size / 1000) * 1)) + expectTransactionSize(params, r) + } + ) + test('9b maxChangeOutputs cap: gradual pool build-up over multiple transactions', async () => { // Simulates a user importing one large UTXO and making sequential actions. // Each action should add at most maxChangeOutputsPerTransaction net new outputs. diff --git a/packages/wallet/wallet-toolbox/src/storage/methods/createAction.ts b/packages/wallet/wallet-toolbox/src/storage/methods/createAction.ts index 2725c5a98d..9a09c30e3a 100644 --- a/packages/wallet/wallet-toolbox/src/storage/methods/createAction.ts +++ b/packages/wallet/wallet-toolbox/src/storage/methods/createAction.ts @@ -1178,9 +1178,10 @@ function makeFundingParams(args: MakeFundingParamsArgs): GenerateChangeSdkParams changeLockingScriptLength: 25, changeUnlockingScriptLength: 107, targetNetCount: changeBasket.numberOfDesiredUTXOs - healthyChangeCount, - // The planner requires a positive cap. The exact anchor leaves no surplus, - // and validateBrc177FundingPlan below independently rejects any change. - maxChangeOutputs: brc177?.kind === 'protected' ? 1 : storage.managedChangePolicy.maxOutputsPerAction, + // The anchor is sized to cover the reclaim (BRC-177 Prefunding §2); the + // protected action carries no change, so any surplus is fee. + // validateBrc177FundingPlan below independently rejects any change. + maxChangeOutputs: brc177?.kind === 'protected' ? 0 : storage.managedChangePolicy.maxOutputsPerAction, surplusPoolShaping: !compatibilityFallback, maxMigrationInputs: compatibilityFallback ? 0 : storage.managedChangePolicy.migrationInputsPerAction, randomVals: vargs.randomVals diff --git a/packages/wallet/wallet-toolbox/src/storage/methods/generateChange.ts b/packages/wallet/wallet-toolbox/src/storage/methods/generateChange.ts index 2c39746675..ebc19fee61 100644 --- a/packages/wallet/wallet-toolbox/src/storage/methods/generateChange.ts +++ b/packages/wallet/wallet-toolbox/src/storage/methods/generateChange.ts @@ -166,7 +166,7 @@ interface ChangeRecaptureRequest extends SurplusChangeMaterializationRequest { */ function materializeSurplusChangeOutput(request: SurplusChangeMaterializationRequest): void { const { params, result } = request - if (!params.surplusPoolShaping || result.changeOutputs.length > 0) return + if (!params.surplusPoolShaping || result.changeOutputs.length > 0 || params.maxChangeOutputs === 0) return const availableAfterOutputFee = request.feeExcess(0, 1) if (availableAfterOutputFee < request.dustFloor) return @@ -187,6 +187,8 @@ async function requireViableChangeOrRetainBoundedFee(request: ChangeRecaptureReq const { params, result } = request const feeExcessNow = request.feeExcess() if (result.changeOutputs.length > 0 || feeExcessNow <= 0) return + // A zero change cap leaves any surplus in the fee by request. + if (params.maxChangeOutputs === 0) return const hasOnlyUnreturnableShapingSurplus = params.surplusPoolShaping === true && request.feeExcess(0, 1) < request.dustFloor @@ -741,7 +743,8 @@ export function validateGenerateChangeSdkResult( r.changeOutputs.length === 0 && r.fee > feeRequired && r.fee - feeWithChangeOutput < dustFloor - if (feeRequired !== r.fee && !isBoundedUnreturnableShapingSurplus) { + const isNoChangeSurplus = params.maxChangeOutputs === 0 && r.changeOutputs.length === 0 && r.fee > feeRequired + if (feeRequired !== r.fee && !isBoundedUnreturnableShapingSurplus && !isNoChangeSurplus) { log += `required fee error ${feeRequired} !== ${r.fee};` ok = false } @@ -799,6 +802,7 @@ export interface GenerateChangeSdkParams { * Maximum number of change outputs to create in this transaction. * Defaults to `maxChangeOutputsPerTransaction` (8). Set to -1 only when an * operator deliberately wants the basket target to be the sole bound. + * Set to 0 to create no change: any surplus is paid as fee. * * Callers may override this to allow more outputs in special cases (e.g. * consolidation transactions) or fewer outputs when a compact transaction @@ -880,7 +884,7 @@ export function validateGenerateChangeSdkParams( validateOptionalInteger(params.targetNetCount, 'targetNetCount') if (params.maxChangeOutputs !== -1) { - validateOptionalInteger(params.maxChangeOutputs, 'maxChangeOutputs', 1) + validateOptionalInteger(params.maxChangeOutputs, 'maxChangeOutputs', 0) } if (params.maxMigrationInputs !== -1) { validateOptionalInteger(params.maxMigrationInputs, 'maxMigrationInputs', 0) diff --git a/packages/wallet/wallet-toolbox/src/storage/methods/noSendExpiry.ts b/packages/wallet/wallet-toolbox/src/storage/methods/noSendExpiry.ts index 4de4c4359e..b9c6c7c9a6 100644 --- a/packages/wallet/wallet-toolbox/src/storage/methods/noSendExpiry.ts +++ b/packages/wallet/wallet-toolbox/src/storage/methods/noSendExpiry.ts @@ -97,14 +97,19 @@ function feeForSize(storage: StorageProvider, size: number, minimumSatsPerKb = 0 return Math.ceil((size / 1000) * satsPerKb) } -function reclaimValues( - storage: StorageProvider, - anchorSatoshis: number -): { reclaimFee: number; reclaimSatoshis: number } { +function reclaimFloor(storage: StorageProvider): { reclaimFee: number; minimumOutput: number } { const reclaimSize = transactionSize([MANAGED_INPUT_UNLOCKING_SCRIPT_LENGTH], [MANAGED_OUTPUT_LOCKING_SCRIPT_LENGTH]) const reclaimFee = feeForSize(storage, reclaimSize, CONSERVATIVE_RECLAIM_SATS_PER_KB) const currentFee = feeForSize(storage, reclaimSize) const minimumOutput = Math.max(1, currentFee * 2) + return { reclaimFee, minimumOutput } +} + +function reclaimValues( + storage: StorageProvider, + anchorSatoshis: number +): { reclaimFee: number; reclaimSatoshis: number } { + const { reclaimFee, minimumOutput } = reclaimFloor(storage) const reclaimSatoshis = anchorSatoshis - reclaimFee if (reclaimSatoshis < minimumOutput) { throw new WERR_INVALID_PARAMETER( @@ -128,11 +133,17 @@ async function estimateAnchorSatoshis( ) const inputSatoshis = xinputs.reduce((sum, input) => sum + input.satoshis, 0) const outputSatoshis = xoutputs.reduce((sum, output) => sum + output.satoshis, 0) - const anchorSatoshis = outputSatoshis + feeForSize(storage, size) - inputSatoshis - if (!Number.isSafeInteger(anchorSatoshis) || anchorSatoshis <= 0) { + const actionSatoshis = outputSatoshis + feeForSize(storage, size) - inputSatoshis + if (!Number.isSafeInteger(actionSatoshis) || actionSatoshis <= 0) { throw new WERR_INVALID_PARAMETER('inputs', 'a BRC-177 action requiring a positive, exactly sized revocation anchor') } - return anchorSatoshis + // BRC-177 Prefunding step 2: the anchor MUST both fund the protected action + // and be reclaimable net of the reclaim fee. When the reclaim floor is the + // larger need (e.g. a value-neutral token transfer), that MUST outweighs the + // spec's SHOULD against material overpayment: the protected action creates + // no change, so the surplus is paid as fee if the action is broadcast. + const { reclaimFee, minimumOutput } = reclaimFloor(storage) + return Math.max(actionSatoshis, reclaimFee + minimumOutput) } export async function prepareNoSendExpiry( diff --git a/packages/wallet/wallet-toolbox/test/Wallet/action/noSendExpiry.test.ts b/packages/wallet/wallet-toolbox/test/Wallet/action/noSendExpiry.test.ts index 524fbcf026..ff7f3413a1 100644 --- a/packages/wallet/wallet-toolbox/test/Wallet/action/noSendExpiry.test.ts +++ b/packages/wallet/wallet-toolbox/test/Wallet/action/noSendExpiry.test.ts @@ -21,6 +21,7 @@ import { ScriptTemplateBRC29 } from '../../../src/utility/ScriptTemplateBRC29' import { randomBytesHex, verifyOne } from '../../../src/utility/utilityHelpers' import { getExactActionSpend } from '../../../src/utility/exactActionSpend' import { asArray } from '../../../src/utility/utilityHelpers.noBuffer' +import { transactionSize } from '../../../src/storage/methods/utils' function memoryKnex(_name: string): Knex { return makeKnex({ @@ -208,6 +209,48 @@ describe('BRC-177 noSend expiry reference implementation', () => { } as const } + // Mirrors noSendExpiry.ts: P2PKH reclaim (107-byte unlock, 25-byte lock) + // priced at no less than 1000 sat/kB, leaving at least twice the reclaim's + // fee at the storage's own rate. + function reclaimFloor(ctx: WalletHarness): { reclaimFee: number; minimumOutput: number } { + const satsPerKb = ctx.active.feeModel.value! + const size = transactionSize([107], [25]) + const reclaimFee = Math.ceil((size / 1000) * Math.max(satsPerKb, 1000)) + const minimumOutput = Math.max(1, Math.ceil((size / 1000) * satsPerKb) * 2) + return { reclaimFee, minimumOutput } + } + + function reclaimFloorSatoshis(ctx: WalletHarness): number { + const { reclaimFee, minimumOutput } = reclaimFloor(ctx) + return reclaimFee + minimumOutput + } + + function protectedActionNeed( + ctx: WalletHarness, + inputs: Array<{ satoshis: number; unlockingScriptLength: number }>, + outputs: Array<{ satoshis: number; lockingScriptLength: number }> + ): number { + const size = transactionSize( + [...inputs.map(input => input.unlockingScriptLength), 107], + outputs.map(output => output.lockingScriptLength) + ) + const fee = Math.ceil((size / 1000) * ctx.active.feeModel.value!) + return ( + outputs.reduce((sum, output) => sum + output.satoshis, 0) + + fee - + inputs.reduce((sum, input) => sum + input.satoshis, 0) + ) + } + + async function anchorOf(ctx: WalletHarness, txid: string) { + const target = verifyOne(await ctx.active.findTransactions({ partial: { txid } })) + return verifyOne( + await ctx.active.findOutputs({ + partial: { txid: target.noSendExpiryAnchorTxid, vout: target.noSendExpiryAnchorVout } + }) + ) + } + test.each([false, true])('binary BRC-100 returns the protected action (signAndProcess=%s)', async signAndProcess => { const ctx = await createHarness() try { @@ -1090,6 +1133,93 @@ describe('BRC-177 noSend expiry reference implementation', () => { }, 'changed before signature release') }) + test('accepts a 1-sat protected output with an anchor sized for the reclaim', async () => { + const ctx = await createHarness() + try { + const created = await ctx.wallet.createAction({ + ...protectedArgs(3600), + outputs: [ + { + satoshis: 1, + lockingScript: '51', + outputDescription: 'Small protected recipient output' + } + ] + }) + expect(protectedActionNeed(ctx, [], [{ satoshis: 1, lockingScriptLength: 1 }])).toBeLessThan( + reclaimFloorSatoshis(ctx) + ) + expect((await anchorOf(ctx, created.txid!)).satoshis).toBe(reclaimFloorSatoshis(ctx)) + const targetTx = Transaction.fromAtomicBEEF(created.tx!) + expect(targetTx.inputs).toHaveLength(1) + expect(targetTx.outputs).toHaveLength(1) + } finally { + await ctx.destroy() + } + }) + + test('sizes the anchor to cover the reclaim for a value-neutral protected action', async () => { + const ctx = await createHarness() + try { + // A 1-sat output the caller can later spend: OP_DROP OP_1, unlocked by OP_1. + const source = await ctx.wallet.createAction({ + description: 'BRC-177 value-neutral source', + outputs: [{ satoshis: 1, lockingScript: '7551', outputDescription: 'Caller token' }], + options: { randomizeOutputs: false, acceptDelayedBroadcast: false } + }) + const created = await ctx.wallet.createAction({ + description: 'BRC-177 value-neutral transfer', + labels: ['p nosend expiry seconds 3600'], + inputBEEF: source.tx, + inputs: [{ outpoint: `${source.txid}.0`, unlockingScript: '51', inputDescription: 'Caller token' }], + outputs: [{ satoshis: 1, lockingScript: '51', outputDescription: 'Transferred token' }], + options: { noSend: true, randomizeOutputs: false } + }) + + const need = protectedActionNeed( + ctx, + [{ satoshis: 1, unlockingScriptLength: 1 }], + [{ satoshis: 1, lockingScriptLength: 1 }] + ) + const anchor = await anchorOf(ctx, created.txid!) + expect(need).toBeLessThan(reclaimFloorSatoshis(ctx)) + expect(anchor.satoshis).toBe(reclaimFloorSatoshis(ctx)) + + const targetTx = Transaction.fromAtomicBEEF(created.tx!) + expect(targetTx.inputs).toHaveLength(2) + expect(targetTx.inputs.map(input => `${input.sourceTXID}.${input.sourceOutputIndex}`).sort()).toEqual( + [`${source.txid}.0`, `${anchor.txid}.${anchor.vout}`].sort() + ) + expect(targetTx.outputs).toHaveLength(1) + expect(targetTx.outputs[0].satoshis).toBe(1) + const target = verifyOne(await ctx.active.findTransactions({ partial: { txid: created.txid } })) + const outputs = await ctx.active.findOutputs({ partial: { transactionId: target.transactionId } }) + expect(outputs.filter(output => output.change)).toHaveLength(0) + + // The armed reclaim is accepted by the processor once the deadline passes. + expect(target.noSendExpiryState).toBe('signed') + expect(target.noSendExpiryReclaimSatoshis).toBe(reclaimFloor(ctx).minimumOutput) + await ctx.active.updateTransaction(target.transactionId, { noSendExpiryDeadline: 0 }) + const run = await processNoSendExpiryLifecycle(ctx.active) + expect(run.reclaimActivated).toBe(1) + expect(await services.storage.getTransaction(target.noSendExpiryReclaimTxid!)).toBeDefined() + } finally { + await ctx.destroy() + } + }) + + test('keeps the exact anchor when the protected action needs more than the reclaim floor', async () => { + const ctx = await createHarness() + try { + const created = await ctx.wallet.createAction(protectedArgs(3600)) + const need = protectedActionNeed(ctx, [], [{ satoshis: 5_000, lockingScriptLength: 1 }]) + expect(need).toBeGreaterThan(reclaimFloorSatoshis(ctx)) + expect((await anchorOf(ctx, created.txid!)).satoshis).toBe(need) + } finally { + await ctx.destroy() + } + }) + test('preserves exact-anchor semantics when the active storage charges a commission', async () => { const ctx = await createHarness() try { @@ -1321,20 +1451,6 @@ describe('BRC-177 noSend expiry reference implementation', () => { ).rejects.toThrow('safely schedulable') expect(await services.storage.getUnminedTransactions()).toHaveLength(unminedBefore) - await expect( - ctx.wallet.createAction({ - ...protectedArgs(3600), - outputs: [ - { - satoshis: 1, - lockingScript: '51', - outputDescription: 'Too-small protected recipient output' - } - ] - }) - ).rejects.toThrow('leaves at least') - expect(await services.storage.getUnminedTransactions()).toHaveLength(unminedBefore) - const created = await ctx.wallet.createAction(protectedArgs(3600)) const target = verifyOne(await ctx.active.findTransactions({ partial: { txid: created.txid } })) await ctx.active.updateTransaction(target.transactionId, { noSendExpiryDeadline: 0 }) From ae6923a936e206037f2be4955205e6a5607d8a42 Mon Sep 17 00:00:00 2001 From: David Case Date: Sat, 3 Oct 2026 17:35:30 -0400 Subject: [PATCH 03/10] refactor(wallet-toolbox): use an explicit surplusToFee planner flag Replace the "maxChangeOutputs: 0 means no change" overload with a dedicated GenerateChangeSdkParams.surplusToFee flag. When set, the planner creates no change output and pays any surplus beyond the required fee as fee. maxChangeOutputs keeps its original rule (minimum 1, default 8); the BRC-177 protected branch goes back to a cap of 1 and sets surplusToFee. The protected funding plan is the only setter. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01R53QzSzGv4LkfmxBd4URiD --- .../GenerateChange/generateChangeSdk.test.ts | 16 +++++++++-- .../src/storage/methods/createAction.ts | 5 +++- .../src/storage/methods/generateChange.ts | 28 ++++++++++++------- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/packages/wallet/wallet-toolbox/src/storage/methods/__test/GenerateChange/generateChangeSdk.test.ts b/packages/wallet/wallet-toolbox/src/storage/methods/__test/GenerateChange/generateChangeSdk.test.ts index 1a93b9d944..9c55778755 100644 --- a/packages/wallet/wallet-toolbox/src/storage/methods/__test/GenerateChange/generateChangeSdk.test.ts +++ b/packages/wallet/wallet-toolbox/src/storage/methods/__test/GenerateChange/generateChangeSdk.test.ts @@ -1088,7 +1088,7 @@ describe('generateChange tests', () => { }) test.each([true, false])( - '9a2 maxChangeOutputs 0 creates no change and pays the surplus as fee (surplusPoolShaping=%s)', + '9a2 surplusToFee creates no change and pays the surplus as fee (surplusPoolShaping=%s)', async surplusPoolShaping => { const params: GenerateChangeSdkParams = { ...defParams, @@ -1096,7 +1096,8 @@ describe('generateChange tests', () => { fixedOutputs: [{ satoshis: 1, lockingScriptLength: 1 }], feeModel: { model: 'sat/kb', value: 1 }, targetNetCount: 8, - maxChangeOutputs: 0, + maxChangeOutputs: 1, + surplusToFee: true, surplusPoolShaping } const { allocateChangeInput, releaseChangeInput } = generateChangeSdkMakeStorage([{ satoshis: 194, outputId: 1 }]) @@ -1111,6 +1112,17 @@ describe('generateChange tests', () => { } ) + test('9a3 maxChangeOutputs 0 is still rejected', async () => { + const params: GenerateChangeSdkParams = { + ...defParams, + fixedOutputs: [{ satoshis: 1, lockingScriptLength: 1 }], + maxChangeOutputs: 0 + } + const { allocateChangeInput, releaseChangeInput } = generateChangeSdkMakeStorage([{ satoshis: 194, outputId: 1 }]) + + await expect(generateChangeSdk(params, allocateChangeInput, releaseChangeInput)).rejects.toThrow('maxChangeOutputs') + }) + test('9b maxChangeOutputs cap: gradual pool build-up over multiple transactions', async () => { // Simulates a user importing one large UTXO and making sequential actions. // Each action should add at most maxChangeOutputsPerTransaction net new outputs. diff --git a/packages/wallet/wallet-toolbox/src/storage/methods/createAction.ts b/packages/wallet/wallet-toolbox/src/storage/methods/createAction.ts index 9a09c30e3a..fd15d728f2 100644 --- a/packages/wallet/wallet-toolbox/src/storage/methods/createAction.ts +++ b/packages/wallet/wallet-toolbox/src/storage/methods/createAction.ts @@ -1178,10 +1178,13 @@ function makeFundingParams(args: MakeFundingParamsArgs): GenerateChangeSdkParams changeLockingScriptLength: 25, changeUnlockingScriptLength: 107, targetNetCount: changeBasket.numberOfDesiredUTXOs - healthyChangeCount, + // The planner requires a positive cap; surplusToFee below keeps the + // protected action change-free regardless. + maxChangeOutputs: brc177?.kind === 'protected' ? 1 : storage.managedChangePolicy.maxOutputsPerAction, // The anchor is sized to cover the reclaim (BRC-177 Prefunding §2); the // protected action carries no change, so any surplus is fee. // validateBrc177FundingPlan below independently rejects any change. - maxChangeOutputs: brc177?.kind === 'protected' ? 0 : storage.managedChangePolicy.maxOutputsPerAction, + surplusToFee: brc177?.kind === 'protected', surplusPoolShaping: !compatibilityFallback, maxMigrationInputs: compatibilityFallback ? 0 : storage.managedChangePolicy.migrationInputsPerAction, randomVals: vargs.randomVals diff --git a/packages/wallet/wallet-toolbox/src/storage/methods/generateChange.ts b/packages/wallet/wallet-toolbox/src/storage/methods/generateChange.ts index ebc19fee61..0f5f579d74 100644 --- a/packages/wallet/wallet-toolbox/src/storage/methods/generateChange.ts +++ b/packages/wallet/wallet-toolbox/src/storage/methods/generateChange.ts @@ -166,7 +166,7 @@ interface ChangeRecaptureRequest extends SurplusChangeMaterializationRequest { */ function materializeSurplusChangeOutput(request: SurplusChangeMaterializationRequest): void { const { params, result } = request - if (!params.surplusPoolShaping || result.changeOutputs.length > 0 || params.maxChangeOutputs === 0) return + if (!params.surplusPoolShaping || result.changeOutputs.length > 0 || params.surplusToFee === true) return const availableAfterOutputFee = request.feeExcess(0, 1) if (availableAfterOutputFee < request.dustFloor) return @@ -187,8 +187,8 @@ async function requireViableChangeOrRetainBoundedFee(request: ChangeRecaptureReq const { params, result } = request const feeExcessNow = request.feeExcess() if (result.changeOutputs.length > 0 || feeExcessNow <= 0) return - // A zero change cap leaves any surplus in the fee by request. - if (params.maxChangeOutputs === 0) return + // The caller sized its funding exactly and asked for any surplus as fee. + if (params.surplusToFee === true) return const hasOnlyUnreturnableShapingSurplus = params.surplusPoolShaping === true && request.feeExcess(0, 1) < request.dustFloor @@ -357,9 +357,11 @@ async function generateChangeSdkCore( * gradually rather than all at once. */ const maxChangeOutputs = - params.maxChangeOutputs === -1 - ? Number.MAX_SAFE_INTEGER - : (params.maxChangeOutputs ?? maxChangeOutputsPerTransaction) + params.surplusToFee === true + ? 0 // no change output is ever added; surplus stays in the fee + : params.maxChangeOutputs === -1 + ? Number.MAX_SAFE_INTEGER + : (params.maxChangeOutputs ?? maxChangeOutputsPerTransaction) const surplusPoolShaping = params.surplusPoolShaping === true const randomVals = [...(params.randomVals || [])] @@ -743,8 +745,8 @@ export function validateGenerateChangeSdkResult( r.changeOutputs.length === 0 && r.fee > feeRequired && r.fee - feeWithChangeOutput < dustFloor - const isNoChangeSurplus = params.maxChangeOutputs === 0 && r.changeOutputs.length === 0 && r.fee > feeRequired - if (feeRequired !== r.fee && !isBoundedUnreturnableShapingSurplus && !isNoChangeSurplus) { + const isSurplusToFee = params.surplusToFee === true && r.changeOutputs.length === 0 && r.fee > feeRequired + if (feeRequired !== r.fee && !isBoundedUnreturnableShapingSurplus && !isSurplusToFee) { log += `required fee error ${feeRequired} !== ${r.fee};` ok = false } @@ -802,7 +804,6 @@ export interface GenerateChangeSdkParams { * Maximum number of change outputs to create in this transaction. * Defaults to `maxChangeOutputsPerTransaction` (8). Set to -1 only when an * operator deliberately wants the basket target to be the sole bound. - * Set to 0 to create no change: any surplus is paid as fee. * * Callers may override this to allow more outputs in special cases (e.g. * consolidation transactions) or fewer outputs when a compact transaction @@ -810,6 +811,13 @@ export interface GenerateChangeSdkParams { */ maxChangeOutputs?: number + /** + * When true, no change outputs are created and any surplus beyond the + * required fee is paid as fee. Set only by callers that have already sized + * their funding exactly, e.g. a BRC-177 protected action. + */ + surplusToFee?: boolean + /** * When true, targetNetCount shapes only genuine post-funding surplus. The * planner will not add inputs merely to reach the desired pool count. @@ -884,7 +892,7 @@ export function validateGenerateChangeSdkParams( validateOptionalInteger(params.targetNetCount, 'targetNetCount') if (params.maxChangeOutputs !== -1) { - validateOptionalInteger(params.maxChangeOutputs, 'maxChangeOutputs', 0) + validateOptionalInteger(params.maxChangeOutputs, 'maxChangeOutputs', 1) } if (params.maxMigrationInputs !== -1) { validateOptionalInteger(params.maxMigrationInputs, 'maxMigrationInputs', 0) From ad4920c8587fb0f70be8b18cbcd0059be1388974 Mon Sep 17 00:00:00 2001 From: David Case Date: Sat, 3 Oct 2026 18:02:03 -0400 Subject: [PATCH 04/10] refactor(wallet-toolbox): replace nested ternary in the change cap Compute the change-output cap as before, then zero it when surplusToFee is set. No behaviour change (SonarCloud typescript:S3358). Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01R53QzSzGv4LkfmxBd4URiD --- .../src/storage/methods/generateChange.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/wallet/wallet-toolbox/src/storage/methods/generateChange.ts b/packages/wallet/wallet-toolbox/src/storage/methods/generateChange.ts index 0f5f579d74..f0d3ffde17 100644 --- a/packages/wallet/wallet-toolbox/src/storage/methods/generateChange.ts +++ b/packages/wallet/wallet-toolbox/src/storage/methods/generateChange.ts @@ -356,12 +356,12 @@ async function generateChangeSdkCore( * Applies the per-transaction limit so that the UTXO pool grows * gradually rather than all at once. */ - const maxChangeOutputs = - params.surplusToFee === true - ? 0 // no change output is ever added; surplus stays in the fee - : params.maxChangeOutputs === -1 - ? Number.MAX_SAFE_INTEGER - : (params.maxChangeOutputs ?? maxChangeOutputsPerTransaction) + let maxChangeOutputs = + params.maxChangeOutputs === -1 + ? Number.MAX_SAFE_INTEGER + : (params.maxChangeOutputs ?? maxChangeOutputsPerTransaction) + // No change output is ever added; surplus stays in the fee. + if (params.surplusToFee === true) maxChangeOutputs = 0 const surplusPoolShaping = params.surplusPoolShaping === true const randomVals = [...(params.randomVals || [])] From 9e2fe18d53109070c27541a32c402452cd9ac80f Mon Sep 17 00:00:00 2001 From: David Case Date: Sat, 3 Oct 2026 18:32:45 -0400 Subject: [PATCH 05/10] fix(wallet-toolbox): return find rows as bytes over negotiated binary JSON StorageServer converts the byte columns of findOutputsAuth and findProvenTxReqs results when the client negotiated binary JSON, as it already does for getSyncChunk. Clients convert them back to number[] in validateEntities. Restore competingBeef to number[] in createAction's WERR_REVIEW_ACTIONS. Thrown errors are serialized with stringifyBRC100, so it never reached the binary codec. --- .../src/storage/methods/createAction.ts | 2 +- .../src/storage/remoting/StorageServer.ts | 17 +++++++++-- .../remoting/__test/StorageServerRpc.test.ts | 28 ++++++++++++++++++- .../src/storage/remoting/syncChunkBinary.ts | 25 +++++++++++------ 4 files changed, 58 insertions(+), 14 deletions(-) diff --git a/packages/wallet/wallet-toolbox/src/storage/methods/createAction.ts b/packages/wallet/wallet-toolbox/src/storage/methods/createAction.ts index 274d07b5b1..7b09279277 100644 --- a/packages/wallet/wallet-toolbox/src/storage/methods/createAction.ts +++ b/packages/wallet/wallet-toolbox/src/storage/methods/createAction.ts @@ -505,7 +505,7 @@ async function createNewInputs( if (doubleSpendTxid) { const beef = await getCompetingBeefForReview(storage, doubleSpendTxid) throw new WERR_REVIEW_ACTIONS( - [{ txid: '', status: 'doubleSpend', competingTxs: [doubleSpendTxid], competingBeef: beef.toUint8Array() }], + [{ txid: '', status: 'doubleSpend', competingTxs: [doubleSpendTxid], competingBeef: beef.toBinary() }], [] ) } diff --git a/packages/wallet/wallet-toolbox/src/storage/remoting/StorageServer.ts b/packages/wallet/wallet-toolbox/src/storage/remoting/StorageServer.ts index 8ff0ceeb89..51306cd04a 100644 --- a/packages/wallet/wallet-toolbox/src/storage/remoting/StorageServer.ts +++ b/packages/wallet/wallet-toolbox/src/storage/remoting/StorageServer.ts @@ -7,7 +7,7 @@ import { SYNC_TRANSFER_MAX_BYTES, SYNC_TRANSFER_PART_BYTES } from './SyncTransfer' -import { syncChunkBinary } from './syncChunkBinary' +import { syncChunkBinary, tableRowsBinary } from './syncChunkBinary' /** * StorageServer.ts * @@ -186,11 +186,23 @@ const pagedLimitArgument = new Map([ ['findProvenTxReqs', 0] ]) +const binaryRowResults = new Map([ + ['findOutputsAuth', 'outputs'], + ['findProvenTxReqs', 'provenTxReqs'] +]) + interface RpcDispatchResult { found: boolean result?: unknown } +/** Copy a result's schema-defined byte fields for the already negotiated binary JSON codec. */ +function binaryRpcResult(method: string, result: unknown): unknown { + if (method === 'getSyncChunk') return syncChunkBinary(result as SyncChunk) + const table = binaryRowResults.get(method) + return table != null && Array.isArray(result) ? tableRowsBinary(table, result) : result +} + function requiredAuthenticatedIdentityKey(req: Request): string { const identityKey = (req as AuthRequest).auth?.identityKey if (typeof identityKey !== 'string' || identityKey.trim() === '' || identityKey === 'unknown') { @@ -656,8 +668,7 @@ export class StorageServer { 400 ) } - const result = - useBinary && method === 'getSyncChunk' ? syncChunkBinary(dispatch.result as SyncChunk) : dispatch.result + const result = useBinary ? binaryRpcResult(method, dispatch.result) : dispatch.result // JSON-RPC success responses must carry a result member. Preserve an // explicit null for void storage methods rather than letting // JSON.stringify silently omit an undefined result. diff --git a/packages/wallet/wallet-toolbox/src/storage/remoting/__test/StorageServerRpc.test.ts b/packages/wallet/wallet-toolbox/src/storage/remoting/__test/StorageServerRpc.test.ts index f39dc49649..d7c1bb6faa 100644 --- a/packages/wallet/wallet-toolbox/src/storage/remoting/__test/StorageServerRpc.test.ts +++ b/packages/wallet/wallet-toolbox/src/storage/remoting/__test/StorageServerRpc.test.ts @@ -1,5 +1,5 @@ import { parseJsonRpc } from '../BinaryJson' -import { validateSyncChunkEntities } from '../entityValidationHelpers' +import { validateEntities, validateSyncChunkEntities } from '../entityValidationHelpers' import { type Request, type Response } from 'express' import { TelemetryEvent, WalletLoggerInterface, Transaction, Script, MerklePath } from '@bsv/sdk' import { toBinaryBaseBlockHeader } from '../../../services/Services' @@ -178,6 +178,32 @@ describe('StorageServer JSON-RPC boundary', () => { expect(chunk.provenTxs[0].rawTx).toBe(bytes) }) + test.each([false, true])('encodes only declared row bytes when binary is negotiated: %s', async binary => { + const bytes = Array.from({ length: 2048 }, (_, i) => i % 256) + const time = { created_at: new Date(), updated_at: new Date() } + const outputs = [{ ...time, outputId: 1, lockingScript: bytes, extraNumbers: bytes }] + const reqs = [{ ...time, provenTxReqId: 1, rawTx: bytes, inputBEEF: bytes, extraNumbers: bytes }] + const server = makeServer({ findOutputsAuth: async () => outputs, findProvenTxReqsAuth: async () => reqs }) + for (const { method, params, rows, fields } of [ + { method: 'findOutputsAuth', params: [{ identityKey: 'alice' }, { partial: {} }], rows: outputs, + fields: ['lockingScript'] }, + { method: 'findProvenTxReqs', params: [{ partial: {} }], rows: reqs, fields: ['rawTx', 'inputBEEF'] } + ]) { + const captured = makeResponse() + await invoke(server, 'handleRpcRequest', makeRequest({ jsonrpc: '2.0', method, params, id: 1 }, + binary ? { [BINARY_ENCODING_HEADER]: BINARY_ENCODING } : {}), captured.response) + expect(captured.statusCode).toBe(200) + for (const field of fields) { + if (binary) expect(captured.body.result[0][field].$bsvBinary).toBe('base64') + else expect(captured.body.result[0][field]).toEqual(bytes) + } + expect(captured.body.result[0].extraNumbers).toEqual(bytes) + expect(validateEntities(parseJsonRpc(JSON.stringify(captured.body), binary).result)).toEqual(rows) + } + expect(outputs[0].lockingScript).toBe(bytes) + expect(reqs[0].rawTx).toBe(bytes) + }) + test('accounts for HTML escaping when enforcing the response-size ceiling', async () => { const server = makeServer({ getSettings: () => ({ value: '<'.repeat(25) }) }, { maxRpcResponseBytes: 100 }) const captured = makeResponse() diff --git a/packages/wallet/wallet-toolbox/src/storage/remoting/syncChunkBinary.ts b/packages/wallet/wallet-toolbox/src/storage/remoting/syncChunkBinary.ts index b632d31b89..728e42abc8 100644 --- a/packages/wallet/wallet-toolbox/src/storage/remoting/syncChunkBinary.ts +++ b/packages/wallet/wallet-toolbox/src/storage/remoting/syncChunkBinary.ts @@ -16,19 +16,26 @@ function isLargeByteArray(value: unknown): value is number[] { return true } +/** Copy one table's schema-defined byte fields for the already negotiated binary JSON codec. */ +export function tableRowsBinary( + table: keyof typeof binaryFields, + rows: Array> +): Array> { + return rows.map(row => { + const copy = { ...row } + for (const field of binaryFields[table]) { + if (isLargeByteArray(copy[field])) copy[field] = Uint8Array.from(copy[field]) + } + return copy + }) +} + /** Copy schema-defined byte fields for the already negotiated binary JSON codec. */ export function syncChunkBinary(chunk: SyncChunk): Record { const result: Record = { ...chunk } - for (const [name, fields] of Object.entries(binaryFields)) { + for (const name of Object.keys(binaryFields) as Array) { const rows: unknown = Reflect.get(chunk, name) - if (!Array.isArray(rows)) continue - result[name] = rows.map((row: Record) => { - const copy = { ...row } - for (const field of fields) { - if (isLargeByteArray(copy[field])) copy[field] = Uint8Array.from(copy[field]) - } - return copy - }) + if (Array.isArray(rows)) result[name] = tableRowsBinary(name, rows) } return result } From 92733717bf0f5f74d5092f6d49bd4d1cdf6759d6 Mon Sep 17 00:00:00 2001 From: David Case Date: Sun, 4 Oct 2026 03:57:55 -0400 Subject: [PATCH 06/10] fix(wallet-toolbox): StorageIdb treats empty array filters as no filter filterCertificates (certifiers, types), filterOutputTagMaps (tagIds) and filterTransactions (status) rejected every row when the array was empty. StorageKnex and the other StorageIdb filters skip an empty array. Guard the four checks with `.length > 0`. Fixes #791 Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01R53QzSzGv4LkfmxBd4URiD --- packages/wallet/wallet-toolbox/src/storage/StorageIdb.ts | 8 ++++---- .../wallet/wallet-toolbox/test/storage/idb/find.test.ts | 9 +++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/wallet/wallet-toolbox/src/storage/StorageIdb.ts b/packages/wallet/wallet-toolbox/src/storage/StorageIdb.ts index 5251d16d75..9bb31ff40b 100644 --- a/packages/wallet/wallet-toolbox/src/storage/StorageIdb.ts +++ b/packages/wallet/wallet-toolbox/src/storage/StorageIdb.ts @@ -658,7 +658,7 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider args.paged?.offset ?? 0, args.paged?.limit, async r => { - if (args.tagIds != null && !args.tagIds.includes(r.outputTagId)) return false + if (args.tagIds != null && args.tagIds.length > 0 && !args.tagIds.includes(r.outputTagId)) return false if (!matchesOutputTagMapPartial(r, args.partial)) return false if (userId !== undefined) { const tagsForUser = await this.countOutputTags({ @@ -1614,8 +1614,8 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider args.paged?.offset ?? 0, args.paged?.limit, r => { - if (args.certifiers != null && !args.certifiers.includes(r.certifier)) return false - if (args.types != null && !args.types.includes(r.type)) return false + if (args.certifiers != null && args.certifiers.length > 0 && !args.certifiers.includes(r.certifier)) return false + if (args.types != null && args.types.length > 0 && !args.types.includes(r.type)) return false return matchesCertificatePartial(r, args.partial) }, filtered @@ -2014,7 +2014,7 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider async r => { if (args.from != null && r.created_at.getTime() < args.from.getTime()) return false if (args.to != null && r.created_at.getTime() >= args.to.getTime()) return false - if (args.status != null && !args.status.includes(r.status)) return false + if (args.status != null && args.status.length > 0 && !args.status.includes(r.status)) return false if (!matchesTransactionPartial(r, args.partial)) return false if ( labelIds != null && diff --git a/packages/wallet/wallet-toolbox/test/storage/idb/find.test.ts b/packages/wallet/wallet-toolbox/test/storage/idb/find.test.ts index 6e664c9d96..5273bac729 100644 --- a/packages/wallet/wallet-toolbox/test/storage/idb/find.test.ts +++ b/packages/wallet/wallet-toolbox/test/storage/idb/find.test.ts @@ -63,6 +63,15 @@ describe('idb find tests', () => { }) ).toHaveLength(1) expect(await storage.findCertificates({ partial: {}, types: ['oblongata'] })).toHaveLength(0) + // Empty arrays mean "no filter", as in StorageKnex and as every listCertificates caller relies on. + expect(await storage.findCertificates({ partial: {}, certifiers: [], types: [] })).toHaveLength(3) + expect( + await storage.findCertificates({ + partial: {}, + certifiers: [], + types: [setup.u1cert2.type] + }) + ).toHaveLength(1) } }) From 06a6df4736cfbce9b3b6aabbaaf1ca0ff891e7e5 Mon Sep 17 00:00:00 2001 From: Ty J Everett Date: Sat, 10 Oct 2026 16:01:51 -0700 Subject: [PATCH 07/10] fix(wallet-toolbox): qualify BRC-177 reclaim funding release --- docs/packages/wallet/wallet-toolbox-client.md | 11 ++++++++--- docs/packages/wallet/wallet-toolbox-mobile.md | 11 ++++++++--- docs/packages/wallet/wallet-toolbox.md | 11 ++++++++--- docs/reference/package-api-migrations.md | 18 +++++++++--------- docs/reference/stack-facts.md | 6 +++--- governance/package-release-notes.json | 12 ++++++------ governance/repository-health/baselines.json | 6 +++--- packages/wallet/wallet-toolbox/CHANGELOG.md | 8 ++++++++ .../wallet/wallet-toolbox/client/package.json | 2 +- .../wallet-toolbox/docs/no-send-expiry.md | 9 +++++++-- .../wallet/wallet-toolbox/mobile/package.json | 2 +- packages/wallet/wallet-toolbox/package.json | 2 +- .../GenerateChange/generateChangeSdk.test.ts | 12 +++++++++++- 13 files changed, 74 insertions(+), 36 deletions(-) diff --git a/docs/packages/wallet/wallet-toolbox-client.md b/docs/packages/wallet/wallet-toolbox-client.md index 039d6ccaf5..3394e1d8fa 100644 --- a/docs/packages/wallet/wallet-toolbox-client.md +++ b/docs/packages/wallet/wallet-toolbox-client.md @@ -3,9 +3,9 @@ id: pkg-wallet-toolbox-client title: '@bsv/wallet-toolbox-client' kind: package domain: wallet -version: '2.14.6' -last_updated: '2026-10-05' -last_verified: '2026-10-05' +version: '2.14.7' +last_updated: '2026-10-10' +last_verified: '2026-10-10' review_cadence_days: 30 npm: 'https://www.npmjs.com/package/@bsv/wallet-toolbox-client' repo: 'https://github.com/bsv-blockchain/ts-stack/tree/main/packages/wallet/wallet-toolbox/client' @@ -24,6 +24,11 @@ expiry monitoring, synchronized lifecycle state, and pre-signed reclaim submission. IndexedDB `listOutputs` results keep `totalOutputs` equal to the full matching count across short final and out-of-range pages. +BRC-177 anchors cover both the delivery and an economic reclaim. When the +reclaim floor is larger, the protected action pays the bounded surplus as +miner fee while retaining no wallet change. Upgrade the active storage +implementation to receive this funding fix; no wire or database migration is required. + Prepared BEEF persistence remains a server-side Knex capability. Browser IndexedDB uses the canonical path, and compatible remote servers can enable the optimization without a browser configuration or wire change. diff --git a/docs/packages/wallet/wallet-toolbox-mobile.md b/docs/packages/wallet/wallet-toolbox-mobile.md index 0f082aa253..ccd68a5f05 100644 --- a/docs/packages/wallet/wallet-toolbox-mobile.md +++ b/docs/packages/wallet/wallet-toolbox-mobile.md @@ -3,9 +3,9 @@ id: pkg-wallet-toolbox-mobile title: '@bsv/wallet-toolbox-mobile' kind: package domain: wallet -version: '2.14.6' -last_updated: '2026-10-05' -last_verified: '2026-10-05' +version: '2.14.7' +last_updated: '2026-10-10' +last_verified: '2026-10-10' review_cadence_days: 30 npm: 'https://www.npmjs.com/package/@bsv/wallet-toolbox-mobile' repo: 'https://github.com/bsv-blockchain/ts-stack/tree/main/packages/wallet/wallet-toolbox/mobile' @@ -18,6 +18,11 @@ tags: [wallet, react-native, mobile, storage, brc-100] `@bsv/wallet-toolbox-mobile` is the React Native and mobile-safe Wallet Toolbox distribution. It includes wallet, signer, services, monitoring, and remote storage surfaces without Knex, SQLite/MySQL, IndexedDB, or Node-only IO. +BRC-177 anchors cover both the delivery and an economic reclaim. When the +reclaim floor is larger, the protected action pays the bounded surplus as +miner fee while retaining no wallet change. Upgrade the active storage +implementation to receive this funding fix; no wire or database migration is required. + Prepared BEEF persistence remains a server-side Knex capability. Mobile remote storage uses the canonical path, and compatible remote servers can enable the optimization without a mobile configuration or wire change. diff --git a/docs/packages/wallet/wallet-toolbox.md b/docs/packages/wallet/wallet-toolbox.md index ab5007f725..7b245db059 100644 --- a/docs/packages/wallet/wallet-toolbox.md +++ b/docs/packages/wallet/wallet-toolbox.md @@ -4,9 +4,9 @@ title: '@bsv/wallet-toolbox' kind: package domain: wallet npm: '@bsv/wallet-toolbox' -version: '2.14.6' -last_updated: '2026-10-05' -last_verified: '2026-10-05' +version: '2.14.7' +last_updated: '2026-10-10' +last_verified: '2026-10-10' review_cadence_days: 30 status: stable tags: ['wallet', 'brc100'] @@ -44,6 +44,11 @@ then use [BRC-38/39 integration](../../guides/wallet-data-portability.md), the The integration guide documents concrete-provider requirements, explicit restore/merge modes and the limits of the current in-memory export helpers. +BRC-177 anchors cover both the delivery and an economic reclaim. When the +reclaim floor is larger, the protected action pays the bounded surplus as +miner fee while retaining no wallet change. Upgrade the active storage +implementation to receive this funding fix; no wire or database migration is required. + ## Current capabilities Wallet Toolbox 2.11 adds the built-in BRC-177 `p nosend expiry` module. It diff --git a/docs/reference/package-api-migrations.md b/docs/reference/package-api-migrations.md index d7663a0155..4798f0bf2c 100644 --- a/docs/reference/package-api-migrations.md +++ b/docs/reference/package-api-migrations.md @@ -54,9 +54,9 @@ and clean-consumer tests remain the executable type authority. | `@bsv/verifast` | `0.3.6` | `0.3.7` | patch | [API and usage](../packages/sdk/verifast.md) | Valid typed verification calls and worker protocols remain compatible. Custom module factories and WASM adapters must return the exact documented binary and boolean shapes; coercive network, height, flag, byte, batch, verdict, lifecycle, or disposed-instance values now fail closed. Keep THIRD_PARTY_NOTICES.md and LICENSES/ with every JavaScript and WebAssembly distribution. SDK consumers may retain ^2.1.8 or install the proposed SDK3 candidate. The immutable SDK2.8.11 reference has nine pre-existing SDK cold-leaf/UMD import defects; all historical floor versions are not qualified by this matrix. SDK3 removes the obsolete DID token API; use the identity-key DID and optional BRC52 adapters described in docs/guides/identity-did-vc-migration.md. No other public API or wire migration is introduced by the peer alternative. | | `@bsv/wallet-helper` | `0.1.8` | `0.1.9` | patch | [API and usage](../packages/helpers/wallet-helper.md) | getAddress now derives with forSelf: true and returns the caller-owned side of the bilateral relationship. Applications that stored or coordinated the previous peer-owned result must regenerate and exchange the corrected address before sending value. Amount must be an integer from 1 through 1,000 and counterparties must be valid public keys. Valid canonical transaction-builder flows remain supported; malformed, ambiguous, or wallet-mutated results now fail closed. Distributors must retain THIRD_PARTY_NOTICES.md and LICENSES/ with the package. SDK consumers may retain ^2.1.6 or install the proposed SDK3 candidate. The immutable SDK2.8.11 reference has nine pre-existing SDK cold-leaf/UMD import defects; all historical floor versions are not qualified by this matrix. SDK3 removes the obsolete DID token API; use the identity-key DID and optional BRC52 adapters described in docs/guides/identity-did-vc-migration.md. No other public API or wire migration is introduced by the peer alternative. | | `@bsv/wallet-relay` | `0.5.1` | `0.5.2` | patch | [API and usage](../packages/wallet/wallet-relay.md) | Enable signed QR codes and distribute only HTTPS pairing origins and relay API URLs. Root-relative API paths and loopback HTTP remain supported. Configure onApprovalRequired for every method not deliberately listed in autoApproveMethods; unsigned pairing URIs and implicit approval are no longer accepted. Existing relay sessions, custom RPC method names, and supported wallet RPC byte encodings remain valid, and host applications continue to provide their matching Express runtime and type graph. Version 0.5 defaults to two missed pongs; set maxMissedHeartbeats to 1 to retain the prior heartbeat policy. Heartbeat intervals must fit the Node timer range. Preserve Cache-Control: no-store at proxies, prefer the stable bsv-wallet-relay plus token WebSocket subprotocols over the legacy token query parameter, and configure edge rate limiting in addition to the bounded in-process defaults. The 24-hour connected-session lifetime no longer renews on reconnect, malformed wallet calls fail with code 400, and WalletRelayClient retains at most 100 log entries unless maxLogEntries is configured. The QR remains a short-lived bearer invitation: keep it private and require explicit operation approval. Relay envelopes, cryptographic framing, and wallet RPC encodings remain unchanged. SDK consumers may retain ^2.4.1 or install the proposed SDK3 candidate. The immutable SDK2.8.11 reference has nine pre-existing SDK cold-leaf/UMD import defects; all historical floor versions are not qualified by this matrix. SDK3 removes the obsolete DID token API; use the identity-key DID and optional BRC52 adapters described in docs/guides/identity-did-vc-migration.md. No other public API or wire migration is introduced by the peer alternative. | -| `@bsv/wallet-toolbox` | `2.14.5` | `2.14.6` | patch | [API and usage](../packages/wallet/wallet-toolbox.md) | No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. | -| `@bsv/wallet-toolbox-client` | `2.14.5` | `2.14.6` | patch | [API and usage](../packages/wallet/wallet-toolbox-client.md) | No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. | -| `@bsv/wallet-toolbox-mobile` | `2.14.5` | `2.14.6` | patch | [API and usage](../packages/wallet/wallet-toolbox-mobile.md) | No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. | +| `@bsv/wallet-toolbox` | `2.14.5` | `2.14.7` | patch | [API and usage](../packages/wallet/wallet-toolbox.md) | No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. | +| `@bsv/wallet-toolbox-client` | `2.14.5` | `2.14.7` | patch | [API and usage](../packages/wallet/wallet-toolbox-client.md) | No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. | +| `@bsv/wallet-toolbox-mobile` | `2.14.5` | `2.14.7` | patch | [API and usage](../packages/wallet/wallet-toolbox-mobile.md) | No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. | | `create-bsv-app` | `1.1.2` | `1.1.2` | none | [API and usage](../packages/helpers/create-bsv-app.md) | Existing CLI flags, network choices, and generated project structure are unchanged. Regenerate or update dependencies after the patched packages are published. Distributors must retain THIRD_PARTY_NOTICES.md and LICENSES/ with the package. | `none` means the source manifest matches the recorded npm baseline. Any other @@ -515,8 +515,8 @@ CLI entry points: `{"wallet-relay":"./bin/init.mjs"}`. - Package documentation: [docs/packages/wallet/wallet-toolbox.md](../packages/wallet/wallet-toolbox.md) - Source: [packages/wallet/wallet-toolbox](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/wallet/wallet-toolbox) -- Release note: WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Adds an SDK3 peer alternative while retaining the existing SDK2.8.0 floor. Public Argon2id declarations reference the typed hash-wasm package while retaining the same generic binary/string contract and emitted runtime bytes. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. -- Migration: No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. +- Release note: WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Adds an SDK3 peer alternative while retaining the existing SDK2.8.0 floor. Public Argon2id declarations reference the typed hash-wasm package while retaining the same generic binary/string contract and emitted runtime bytes. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged. +- Migration: No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. | Public subpath | Runtime target(s) | Declaration target(s) | | ---------------- | ---------------------------------------------------- | -------------------------- | @@ -529,8 +529,8 @@ CLI entry points: `{"wallet-relay":"./bin/init.mjs"}`. - Package documentation: [docs/packages/wallet/wallet-toolbox-client.md](../packages/wallet/wallet-toolbox-client.md) - Source: [packages/wallet/wallet-toolbox/client](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/wallet/wallet-toolbox/client) -- Release note: WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. -- Migration: No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. +- Release note: WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged. +- Migration: No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. | Public subpath | Runtime target(s) | Declaration target(s) | | ---------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | @@ -541,8 +541,8 @@ CLI entry points: `{"wallet-relay":"./bin/init.mjs"}`. - Package documentation: [docs/packages/wallet/wallet-toolbox-mobile.md](../packages/wallet/wallet-toolbox-mobile.md) - Source: [packages/wallet/wallet-toolbox/mobile](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/wallet/wallet-toolbox/mobile) -- Release note: WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Adds an SDK3 peer alternative while retaining the existing SDK2.8.0 floor. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. -- Migration: No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. +- Release note: WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Adds an SDK3 peer alternative while retaining the existing SDK2.8.0 floor. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged. +- Migration: No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. | Public subpath | Runtime target(s) | Declaration target(s) | | ---------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | diff --git a/docs/reference/stack-facts.md b/docs/reference/stack-facts.md index e08a574a52..6ba72451fb 100644 --- a/docs/reference/stack-facts.md +++ b/docs/reference/stack-facts.md @@ -67,9 +67,9 @@ authorized release action. | wallet | `@bsv/btms-permission-module` | `1.2.2` | node-library | node-esm | node | `>=22` | [packages/wallet/btms-permission-module](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/wallet/btms-permission-module) | | wallet | `@bsv/ecpm-permission-module` | `0.1.2` | browser-library | browser-bundler, browser-esm, node-esm | browser, node | `>=22` | [packages/wallet/ecpm-permission-module](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/wallet/ecpm-permission-module) | | wallet | `@bsv/wallet-relay` | `0.5.2` | cli-library | browser-bundler, browser-esm, cli, node-cjs, node-esm | browser, node | `>=22` | [packages/wallet/ts-wallet-relay](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/wallet/ts-wallet-relay) | -| wallet | `@bsv/wallet-toolbox` | `2.14.6` | node-library | node-cjs | node | `>=22` | [packages/wallet/wallet-toolbox](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/wallet/wallet-toolbox) | -| wallet | `@bsv/wallet-toolbox-client` | `2.14.6` | browser-library | browser-bundler, browser-esm, node-cjs, node-esm | browser, node | `>=22` | [packages/wallet/wallet-toolbox/client](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/wallet/wallet-toolbox/client) | -| wallet | `@bsv/wallet-toolbox-mobile` | `2.14.6` | react-native-library | react-native-metro | react-native | `>=22` | [packages/wallet/wallet-toolbox/mobile](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/wallet/wallet-toolbox/mobile) | +| wallet | `@bsv/wallet-toolbox` | `2.14.7` | node-library | node-cjs | node | `>=22` | [packages/wallet/wallet-toolbox](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/wallet/wallet-toolbox) | +| wallet | `@bsv/wallet-toolbox-client` | `2.14.7` | browser-library | browser-bundler, browser-esm, node-cjs, node-esm | browser, node | `>=22` | [packages/wallet/wallet-toolbox/client](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/wallet/wallet-toolbox/client) | +| wallet | `@bsv/wallet-toolbox-mobile` | `2.14.7` | react-native-library | react-native-metro | react-native | `>=22` | [packages/wallet/wallet-toolbox/mobile](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/wallet/wallet-toolbox/mobile) | ## Standalone infrastructure manifests diff --git a/governance/package-release-notes.json b/governance/package-release-notes.json index 18a26d5ee5..dc417956d6 100644 --- a/governance/package-release-notes.json +++ b/governance/package-release-notes.json @@ -210,22 +210,22 @@ "name": "@bsv/wallet-toolbox", "publishedVersion": "2.14.5", "releaseType": "patch", - "summary": "WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Adds an SDK3 peer alternative while retaining the existing SDK2.8.0 floor. Public Argon2id declarations reference the typed hash-wasm package while retaining the same generic binary/string contract and emitted runtime bytes. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage.", - "migration": "No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite." + "summary": "WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Adds an SDK3 peer alternative while retaining the existing SDK2.8.0 floor. Public Argon2id declarations reference the typed hash-wasm package while retaining the same generic binary/string contract and emitted runtime bytes. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged.", + "migration": "No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages." }, { "name": "@bsv/wallet-toolbox-client", "publishedVersion": "2.14.5", "releaseType": "patch", - "summary": "WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage.", - "migration": "No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite." + "summary": "WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged.", + "migration": "No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages." }, { "name": "@bsv/wallet-toolbox-mobile", "publishedVersion": "2.14.5", "releaseType": "patch", - "summary": "WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Adds an SDK3 peer alternative while retaining the existing SDK2.8.0 floor. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage.", - "migration": "No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite." + "summary": "WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Adds an SDK3 peer alternative while retaining the existing SDK2.8.0 floor. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged.", + "migration": "No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages." }, { "name": "create-bsv-app", diff --git a/governance/repository-health/baselines.json b/governance/repository-health/baselines.json index b9732fed5c..f40fee2166 100644 --- a/governance/repository-health/baselines.json +++ b/governance/repository-health/baselines.json @@ -327,8 +327,8 @@ "@bsv/btms-permission-module": "1.2.2", "@bsv/ecpm-permission-module": "0.1.2", "@bsv/wallet-relay": "0.5.2", - "@bsv/wallet-toolbox-client": "2.14.6", - "@bsv/wallet-toolbox-mobile": "2.14.6", - "@bsv/wallet-toolbox": "2.14.6" + "@bsv/wallet-toolbox-client": "2.14.7", + "@bsv/wallet-toolbox-mobile": "2.14.7", + "@bsv/wallet-toolbox": "2.14.7" } } diff --git a/packages/wallet/wallet-toolbox/CHANGELOG.md b/packages/wallet/wallet-toolbox/CHANGELOG.md index eb1946f69c..efb9ca8d76 100644 --- a/packages/wallet/wallet-toolbox/CHANGELOG.md +++ b/packages/wallet/wallet-toolbox/CHANGELOG.md @@ -4,6 +4,14 @@ This document captures the history of significant changes to the wallet-toolbox The git commit history contains the details but is unable to draw attention to changes that materially alter behavior or extend functionality. +## wallet-toolbox 2.14.7 + +- BRC-177 anchors cover both the protected action and an economic reclaim. + Protected actions keep their only managed input bound to the named anchor, + create no wallet change, and pay the bounded reclaim surplus as miner fee. + Ordinary actions retain their existing change planning. No API, wire or + database migration is required. + ## wallet-toolbox 2.14.6 - UMP support pins anchor verified update lineage. Password and token updates diff --git a/packages/wallet/wallet-toolbox/client/package.json b/packages/wallet/wallet-toolbox/client/package.json index 75b51f6f74..6708d1ce09 100644 --- a/packages/wallet/wallet-toolbox/client/package.json +++ b/packages/wallet/wallet-toolbox/client/package.json @@ -1,6 +1,6 @@ { "name": "@bsv/wallet-toolbox-client", - "version": "2.14.6", + "version": "2.14.7", "type": "module", "sideEffects": false, "engines": { diff --git a/packages/wallet/wallet-toolbox/docs/no-send-expiry.md b/packages/wallet/wallet-toolbox/docs/no-send-expiry.md index 11496a2d63..a5b47339fb 100644 --- a/packages/wallet/wallet-toolbox/docs/no-send-expiry.md +++ b/packages/wallet/wallet-toolbox/docs/no-send-expiry.md @@ -54,8 +54,8 @@ cannot be overridden by a custom permission module or asserted through Before returning the protected action, Wallet Toolbox: -1. calculates the exact wallet funding required by its requested outputs, - explicit inputs, and fee; +1. calculates an anchor large enough for both the wallet funding required by + its requested outputs, explicit inputs, and fee, and an economic reclaim; 2. creates and immediately broadcasts a normal funding transaction containing a dedicated managed-change output; 3. requires processor acceptance of that funding transaction; @@ -64,6 +64,11 @@ Before returning the protected action, Wallet Toolbox: 5. signs and durably stores a one-input reclaim transaction to a fresh `default`-basket output. +When the reclaim floor exceeds the protected action's delivery need, the +protected action pays that difference as miner fee. It still creates no wallet +change and selects no managed input beyond its named anchor. Ordinary action +change planning is unchanged. + The wallet returns the protected transaction to the caller but never broadcasts it. The funding transaction may have ordinary change because it is already on the network; significant wallet change is therefore not held inside diff --git a/packages/wallet/wallet-toolbox/mobile/package.json b/packages/wallet/wallet-toolbox/mobile/package.json index 1ab79c601d..26215e3253 100644 --- a/packages/wallet/wallet-toolbox/mobile/package.json +++ b/packages/wallet/wallet-toolbox/mobile/package.json @@ -1,6 +1,6 @@ { "name": "@bsv/wallet-toolbox-mobile", - "version": "2.14.6", + "version": "2.14.7", "type": "module", "sideEffects": false, "engines": { diff --git a/packages/wallet/wallet-toolbox/package.json b/packages/wallet/wallet-toolbox/package.json index a8006b7c00..4fe67791ad 100644 --- a/packages/wallet/wallet-toolbox/package.json +++ b/packages/wallet/wallet-toolbox/package.json @@ -1,6 +1,6 @@ { "name": "@bsv/wallet-toolbox", - "version": "2.14.6", + "version": "2.14.7", "sideEffects": false, "type": "commonjs", "engines": { diff --git a/packages/wallet/wallet-toolbox/src/storage/methods/__test/GenerateChange/generateChangeSdk.test.ts b/packages/wallet/wallet-toolbox/src/storage/methods/__test/GenerateChange/generateChangeSdk.test.ts index 9c55778755..9f6eb0b181 100644 --- a/packages/wallet/wallet-toolbox/src/storage/methods/__test/GenerateChange/generateChangeSdk.test.ts +++ b/packages/wallet/wallet-toolbox/src/storage/methods/__test/GenerateChange/generateChangeSdk.test.ts @@ -9,7 +9,8 @@ import { generateChangeSdkMakeStorage, GenerateChangeSdkParams, GenerateChangeSdkResult, - maxChangeOutputsPerTransaction + maxChangeOutputsPerTransaction, + validateGenerateChangeSdkResult } from '../../generateChange' describe('generateChange tests', () => { @@ -1109,6 +1110,15 @@ describe('generateChange tests', () => { expect(r.fee).toBe(194) expect(r.fee).toBeGreaterThan(Math.ceil((r.size / 1000) * 1)) expectTransactionSize(params, r) + expect(validateGenerateChangeSdkResult({ ...params, surplusToFee: false }, r).ok).toBe(false) + expect(validateGenerateChangeSdkResult(params, { ...r, fee: r.fee - 1 }).ok).toBe(false) + expect( + validateGenerateChangeSdkResult(params, { + ...r, + fee: r.fee - 1, + changeOutputs: [{ satoshis: 1, lockingScriptLength: 25 }] + }).ok + ).toBe(false) } ) From 7b060de0546c1c7f12fde57402c1de4590be91c9 Mon Sep 17 00:00:00 2001 From: Ty J Everett Date: Sat, 10 Oct 2026 16:12:16 -0700 Subject: [PATCH 08/10] test(wallet-toolbox): qualify compact BEEF transport and compatibility --- docs/packages/wallet/wallet-toolbox-client.md | 5 +++ docs/packages/wallet/wallet-toolbox-mobile.md | 5 +++ docs/packages/wallet/wallet-toolbox.md | 5 +++ docs/reference/package-api-migrations.md | 18 ++++---- governance/package-release-notes.json | 12 ++--- packages/wallet/wallet-toolbox/CHANGELOG.md | 5 +++ .../internalizeAction.storageArgs.test.ts | 26 ++++++----- .../StorageClientBase.inputBeef.test.ts | 35 +++++++++++++-- .../remoting/__test/StorageServerRpc.test.ts | 45 +++++++++++++++++-- .../remoting/__test/tableRowsBinary.test.ts | 26 +++++++++++ 10 files changed, 148 insertions(+), 34 deletions(-) create mode 100644 packages/wallet/wallet-toolbox/src/storage/remoting/__test/tableRowsBinary.test.ts diff --git a/docs/packages/wallet/wallet-toolbox-client.md b/docs/packages/wallet/wallet-toolbox-client.md index 3394e1d8fa..55c1a25ad0 100644 --- a/docs/packages/wallet/wallet-toolbox-client.md +++ b/docs/packages/wallet/wallet-toolbox-client.md @@ -72,6 +72,11 @@ The portable local controller coalesces stale height refresh and immutable object loads, applies failed-load backoff, and validates through the asynchronous `InlineBulkFileDataValidator` without importing Node worker or filesystem code. +BEEF requests and schema-declared storage response bytes use the existing +negotiated compact binary JSON codec. Legacy peers retain numeric-array JSON +and identical transaction/proof bytes. Authentication and payload ceilings +remain unchanged; upgrade both clients and active storage for the savings. + ## Backup and recovery Recover both key material and wallet records; a manager snapshot or device diff --git a/docs/packages/wallet/wallet-toolbox-mobile.md b/docs/packages/wallet/wallet-toolbox-mobile.md index ccd68a5f05..ebc8344936 100644 --- a/docs/packages/wallet/wallet-toolbox-mobile.md +++ b/docs/packages/wallet/wallet-toolbox-mobile.md @@ -72,6 +72,11 @@ The portable local controller coalesces stale height refresh and immutable object loads, applies failed-load backoff, and validates through the asynchronous `InlineBulkFileDataValidator` without importing Node worker or filesystem code. +BEEF requests and schema-declared storage response bytes use the existing +negotiated compact binary JSON codec. Legacy peers retain numeric-array JSON +and identical transaction/proof bytes. Authentication and payload ceilings +remain unchanged; upgrade both clients and active storage for the savings. + ## Backup and recovery Recover both key material and wallet records; a manager snapshot or device diff --git a/docs/packages/wallet/wallet-toolbox.md b/docs/packages/wallet/wallet-toolbox.md index 7b245db059..261a50bfe3 100644 --- a/docs/packages/wallet/wallet-toolbox.md +++ b/docs/packages/wallet/wallet-toolbox.md @@ -19,6 +19,11 @@ repo: 'https://github.com/bsv-blockchain/ts-stack/tree/main/packages/wallet/wall Use this package when you are building a wallet product, a wallet-like service, or another implementation that must match BRC-100 behavior. +BEEF requests and schema-declared storage response bytes use the existing +negotiated compact binary JSON codec. Legacy peers retain numeric-array JSON +and identical transaction/proof bytes. Authentication and payload ceilings +remain unchanged; upgrade both clients and active storage for the savings. + ## Optional registry descriptions ProtoMap, BasketMap and CertMap supply descriptive metadata, not permission grants. diff --git a/docs/reference/package-api-migrations.md b/docs/reference/package-api-migrations.md index 4798f0bf2c..ddb72bb404 100644 --- a/docs/reference/package-api-migrations.md +++ b/docs/reference/package-api-migrations.md @@ -54,9 +54,9 @@ and clean-consumer tests remain the executable type authority. | `@bsv/verifast` | `0.3.6` | `0.3.7` | patch | [API and usage](../packages/sdk/verifast.md) | Valid typed verification calls and worker protocols remain compatible. Custom module factories and WASM adapters must return the exact documented binary and boolean shapes; coercive network, height, flag, byte, batch, verdict, lifecycle, or disposed-instance values now fail closed. Keep THIRD_PARTY_NOTICES.md and LICENSES/ with every JavaScript and WebAssembly distribution. SDK consumers may retain ^2.1.8 or install the proposed SDK3 candidate. The immutable SDK2.8.11 reference has nine pre-existing SDK cold-leaf/UMD import defects; all historical floor versions are not qualified by this matrix. SDK3 removes the obsolete DID token API; use the identity-key DID and optional BRC52 adapters described in docs/guides/identity-did-vc-migration.md. No other public API or wire migration is introduced by the peer alternative. | | `@bsv/wallet-helper` | `0.1.8` | `0.1.9` | patch | [API and usage](../packages/helpers/wallet-helper.md) | getAddress now derives with forSelf: true and returns the caller-owned side of the bilateral relationship. Applications that stored or coordinated the previous peer-owned result must regenerate and exchange the corrected address before sending value. Amount must be an integer from 1 through 1,000 and counterparties must be valid public keys. Valid canonical transaction-builder flows remain supported; malformed, ambiguous, or wallet-mutated results now fail closed. Distributors must retain THIRD_PARTY_NOTICES.md and LICENSES/ with the package. SDK consumers may retain ^2.1.6 or install the proposed SDK3 candidate. The immutable SDK2.8.11 reference has nine pre-existing SDK cold-leaf/UMD import defects; all historical floor versions are not qualified by this matrix. SDK3 removes the obsolete DID token API; use the identity-key DID and optional BRC52 adapters described in docs/guides/identity-did-vc-migration.md. No other public API or wire migration is introduced by the peer alternative. | | `@bsv/wallet-relay` | `0.5.1` | `0.5.2` | patch | [API and usage](../packages/wallet/wallet-relay.md) | Enable signed QR codes and distribute only HTTPS pairing origins and relay API URLs. Root-relative API paths and loopback HTTP remain supported. Configure onApprovalRequired for every method not deliberately listed in autoApproveMethods; unsigned pairing URIs and implicit approval are no longer accepted. Existing relay sessions, custom RPC method names, and supported wallet RPC byte encodings remain valid, and host applications continue to provide their matching Express runtime and type graph. Version 0.5 defaults to two missed pongs; set maxMissedHeartbeats to 1 to retain the prior heartbeat policy. Heartbeat intervals must fit the Node timer range. Preserve Cache-Control: no-store at proxies, prefer the stable bsv-wallet-relay plus token WebSocket subprotocols over the legacy token query parameter, and configure edge rate limiting in addition to the bounded in-process defaults. The 24-hour connected-session lifetime no longer renews on reconnect, malformed wallet calls fail with code 400, and WalletRelayClient retains at most 100 log entries unless maxLogEntries is configured. The QR remains a short-lived bearer invitation: keep it private and require explicit operation approval. Relay envelopes, cryptographic framing, and wallet RPC encodings remain unchanged. SDK consumers may retain ^2.4.1 or install the proposed SDK3 candidate. The immutable SDK2.8.11 reference has nine pre-existing SDK cold-leaf/UMD import defects; all historical floor versions are not qualified by this matrix. SDK3 removes the obsolete DID token API; use the identity-key DID and optional BRC52 adapters described in docs/guides/identity-did-vc-migration.md. No other public API or wire migration is introduced by the peer alternative. | -| `@bsv/wallet-toolbox` | `2.14.5` | `2.14.7` | patch | [API and usage](../packages/wallet/wallet-toolbox.md) | No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. | -| `@bsv/wallet-toolbox-client` | `2.14.5` | `2.14.7` | patch | [API and usage](../packages/wallet/wallet-toolbox-client.md) | No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. | -| `@bsv/wallet-toolbox-mobile` | `2.14.5` | `2.14.7` | patch | [API and usage](../packages/wallet/wallet-toolbox-mobile.md) | No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. | +| `@bsv/wallet-toolbox` | `2.14.5` | `2.14.7` | patch | [API and usage](../packages/wallet/wallet-toolbox.md) | No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. No API, database or peer migration is required for compact BEEF transport. Upgrade clients and active storage to obtain both request and response savings; older peers retain ordinary JSON. Existing payload ceilings still apply. | +| `@bsv/wallet-toolbox-client` | `2.14.5` | `2.14.7` | patch | [API and usage](../packages/wallet/wallet-toolbox-client.md) | No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. No API, database or peer migration is required for compact BEEF transport. Upgrade clients and active storage to obtain both request and response savings; older peers retain ordinary JSON. Existing payload ceilings still apply. | +| `@bsv/wallet-toolbox-mobile` | `2.14.5` | `2.14.7` | patch | [API and usage](../packages/wallet/wallet-toolbox-mobile.md) | No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. No API, database or peer migration is required for compact BEEF transport. Upgrade clients and active storage to obtain both request and response savings; older peers retain ordinary JSON. Existing payload ceilings still apply. | | `create-bsv-app` | `1.1.2` | `1.1.2` | none | [API and usage](../packages/helpers/create-bsv-app.md) | Existing CLI flags, network choices, and generated project structure are unchanged. Regenerate or update dependencies after the patched packages are published. Distributors must retain THIRD_PARTY_NOTICES.md and LICENSES/ with the package. | `none` means the source manifest matches the recorded npm baseline. Any other @@ -515,8 +515,8 @@ CLI entry points: `{"wallet-relay":"./bin/init.mjs"}`. - Package documentation: [docs/packages/wallet/wallet-toolbox.md](../packages/wallet/wallet-toolbox.md) - Source: [packages/wallet/wallet-toolbox](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/wallet/wallet-toolbox) -- Release note: WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Adds an SDK3 peer alternative while retaining the existing SDK2.8.0 floor. Public Argon2id declarations reference the typed hash-wasm package while retaining the same generic binary/string contract and emitted runtime bytes. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged. -- Migration: No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. +- Release note: WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Adds an SDK3 peer alternative while retaining the existing SDK2.8.0 floor. Public Argon2id declarations reference the typed hash-wasm package while retaining the same generic binary/string contract and emitted runtime bytes. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged. BEEF request data and schema-declared storage response bytes use the existing negotiated compact binary JSON encoding. Legacy peers retain numeric-array JSON, existing authentication and payload bounds, and byte-for-byte transaction/proof content. +- Migration: No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. No API, database or peer migration is required for compact BEEF transport. Upgrade clients and active storage to obtain both request and response savings; older peers retain ordinary JSON. Existing payload ceilings still apply. | Public subpath | Runtime target(s) | Declaration target(s) | | ---------------- | ---------------------------------------------------- | -------------------------- | @@ -529,8 +529,8 @@ CLI entry points: `{"wallet-relay":"./bin/init.mjs"}`. - Package documentation: [docs/packages/wallet/wallet-toolbox-client.md](../packages/wallet/wallet-toolbox-client.md) - Source: [packages/wallet/wallet-toolbox/client](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/wallet/wallet-toolbox/client) -- Release note: WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged. -- Migration: No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. +- Release note: WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged. BEEF request data and schema-declared storage response bytes use the existing negotiated compact binary JSON encoding. Legacy peers retain numeric-array JSON, existing authentication and payload bounds, and byte-for-byte transaction/proof content. +- Migration: No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. No API, database or peer migration is required for compact BEEF transport. Upgrade clients and active storage to obtain both request and response savings; older peers retain ordinary JSON. Existing payload ceilings still apply. | Public subpath | Runtime target(s) | Declaration target(s) | | ---------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | @@ -541,8 +541,8 @@ CLI entry points: `{"wallet-relay":"./bin/init.mjs"}`. - Package documentation: [docs/packages/wallet/wallet-toolbox-mobile.md](../packages/wallet/wallet-toolbox-mobile.md) - Source: [packages/wallet/wallet-toolbox/mobile](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/wallet/wallet-toolbox/mobile) -- Release note: WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Adds an SDK3 peer alternative while retaining the existing SDK2.8.0 floor. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged. -- Migration: No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. +- Release note: WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Adds an SDK3 peer alternative while retaining the existing SDK2.8.0 floor. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged. BEEF request data and schema-declared storage response bytes use the existing negotiated compact binary JSON encoding. Legacy peers retain numeric-array JSON, existing authentication and payload bounds, and byte-for-byte transaction/proof content. +- Migration: No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. No API, database or peer migration is required for compact BEEF transport. Upgrade clients and active storage to obtain both request and response savings; older peers retain ordinary JSON. Existing payload ceilings still apply. | Public subpath | Runtime target(s) | Declaration target(s) | | ---------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | diff --git a/governance/package-release-notes.json b/governance/package-release-notes.json index dc417956d6..a488c27c20 100644 --- a/governance/package-release-notes.json +++ b/governance/package-release-notes.json @@ -210,22 +210,22 @@ "name": "@bsv/wallet-toolbox", "publishedVersion": "2.14.5", "releaseType": "patch", - "summary": "WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Adds an SDK3 peer alternative while retaining the existing SDK2.8.0 floor. Public Argon2id declarations reference the typed hash-wasm package while retaining the same generic binary/string contract and emitted runtime bytes. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged.", - "migration": "No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages." + "summary": "WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Adds an SDK3 peer alternative while retaining the existing SDK2.8.0 floor. Public Argon2id declarations reference the typed hash-wasm package while retaining the same generic binary/string contract and emitted runtime bytes. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged. BEEF request data and schema-declared storage response bytes use the existing negotiated compact binary JSON encoding. Legacy peers retain numeric-array JSON, existing authentication and payload bounds, and byte-for-byte transaction/proof content.", + "migration": "No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. No API, database or peer migration is required for compact BEEF transport. Upgrade clients and active storage to obtain both request and response savings; older peers retain ordinary JSON. Existing payload ceilings still apply." }, { "name": "@bsv/wallet-toolbox-client", "publishedVersion": "2.14.5", "releaseType": "patch", - "summary": "WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged.", - "migration": "No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages." + "summary": "WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged. BEEF request data and schema-declared storage response bytes use the existing negotiated compact binary JSON encoding. Legacy peers retain numeric-array JSON, existing authentication and payload bounds, and byte-for-byte transaction/proof content.", + "migration": "No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. No API, database or peer migration is required for compact BEEF transport. Upgrade clients and active storage to obtain both request and response savings; older peers retain ordinary JSON. Existing payload ceilings still apply." }, { "name": "@bsv/wallet-toolbox-mobile", "publishedVersion": "2.14.5", "releaseType": "patch", - "summary": "WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Adds an SDK3 peer alternative while retaining the existing SDK2.8.0 floor. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged.", - "migration": "No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages." + "summary": "WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Adds an SDK3 peer alternative while retaining the existing SDK2.8.0 floor. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged. BEEF request data and schema-declared storage response bytes use the existing negotiated compact binary JSON encoding. Legacy peers retain numeric-array JSON, existing authentication and payload bounds, and byte-for-byte transaction/proof content.", + "migration": "No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. No API, database or peer migration is required for compact BEEF transport. Upgrade clients and active storage to obtain both request and response savings; older peers retain ordinary JSON. Existing payload ceilings still apply." }, { "name": "create-bsv-app", diff --git a/packages/wallet/wallet-toolbox/CHANGELOG.md b/packages/wallet/wallet-toolbox/CHANGELOG.md index efb9ca8d76..9d1a2e14cf 100644 --- a/packages/wallet/wallet-toolbox/CHANGELOG.md +++ b/packages/wallet/wallet-toolbox/CHANGELOG.md @@ -6,6 +6,11 @@ attention to changes that materially alter behavior or extend functionality. ## wallet-toolbox 2.14.7 +- BEEF requests and schema-declared storage response bytes use the existing + negotiated compact binary JSON codec. Legacy peers retain numeric-array + JSON and identical transaction/proof bytes. Existing authentication and + payload ceilings remain unchanged; no API or database migration is required. + - BRC-177 anchors cover both the protected action and an economic reclaim. Protected actions keep their only managed input bound to the named anchor, create no wallet change, and pay the bounded reclaim surplus as miner fee. diff --git a/packages/wallet/wallet-toolbox/src/signer/methods/__tests/internalizeAction.storageArgs.test.ts b/packages/wallet/wallet-toolbox/src/signer/methods/__tests/internalizeAction.storageArgs.test.ts index e100a0b2fd..ddca7b0fe0 100644 --- a/packages/wallet/wallet-toolbox/src/signer/methods/__tests/internalizeAction.storageArgs.test.ts +++ b/packages/wallet/wallet-toolbox/src/signer/methods/__tests/internalizeAction.storageArgs.test.ts @@ -24,17 +24,21 @@ describe('signer internalizeAction storage arguments', () => { } } as unknown as Wallet - await internalizeAction(wallet, { identityKey: `02${'11'.repeat(32)}`, userId: 1 }, { - tx: beef.toBinaryAtomic(txid), - outputs: [ - { - outputIndex: 0, - protocol: 'basket insertion', - insertionRemittance: { basket: 'tokens', tags: [] } - } - ], - description: 'internalize as bytes' - }) + await internalizeAction( + wallet, + { identityKey: `02${'11'.repeat(32)}`, userId: 1 }, + { + tx: beef.toBinaryAtomic(txid), + outputs: [ + { + outputIndex: 0, + protocol: 'basket insertion', + insertionRemittance: { basket: 'tokens', tags: [] } + } + ], + description: 'internalize as bytes' + } + ) const sent = received[0] as { tx: unknown } expect(sent.tx).toBeInstanceOf(Uint8Array) diff --git a/packages/wallet/wallet-toolbox/src/storage/remoting/__test/StorageClientBase.inputBeef.test.ts b/packages/wallet/wallet-toolbox/src/storage/remoting/__test/StorageClientBase.inputBeef.test.ts index abb7358f5a..bd7ab23aa9 100644 --- a/packages/wallet/wallet-toolbox/src/storage/remoting/__test/StorageClientBase.inputBeef.test.ts +++ b/packages/wallet/wallet-toolbox/src/storage/remoting/__test/StorageClientBase.inputBeef.test.ts @@ -1,6 +1,6 @@ import { BEEF_V1, Beef, Script, Transaction, UnlockingScript, Validation, type WalletInterface } from '@bsv/sdk' import { StorageClientBase } from '../StorageClientBase' -import { stringifyJsonRpc } from '../BinaryJson' +import { parseJsonRpc, stringifyJsonRpc } from '../BinaryJson' class CapturingStorageClient extends StorageClientBase { calls: Array<{ method: string; params: unknown[] }> = [] @@ -176,12 +176,39 @@ describe('StorageClientBase createAction inputBEEF pruning', () => { }) expect((client.calls[0].params[1] as Validation.ValidCreateActionArgs).inputBEEF).toBeInstanceOf(Uint8Array) - expect( - (client.calls[1].params[1] as { target: Validation.ValidCreateActionArgs }).target.inputBEEF - ).toBeInstanceOf(Uint8Array) + expect((client.calls[1].params[1] as { target: Validation.ValidCreateActionArgs }).target.inputBEEF).toBeInstanceOf( + Uint8Array + ) expect(target.inputBEEF).not.toBeInstanceOf(Uint8Array) }) + test.each([false, true])('preserves a large BEEF request with binary negotiation %s', async binary => { + const required = makeTransaction(1000) + required.addOutput({ satoshis: 0, lockingScript: Script.fromASM(`OP_FALSE OP_RETURN ${'ff'.repeat(150_000)}`) }) + const beef = new Beef() + beef.mergeTransaction(required) + const originalBytes = beef.toBinary() + const args = Validation.validateCreateActionArgs({ + description: 'forward large proof data', + inputs: [{ outpoint: `${required.id('hex')}.0`, unlockingScript: '00', inputDescription: 'large input' }], + inputBEEF: originalBytes, + outputs: [{ satoshis: 1, lockingScript: '51', outputDescription: 'replacement output' }] + }) + const client = new CapturingStorageClient({} as WalletInterface, 'https://storage.example.test') + await client.createAction(auth, args) + const envelope = { jsonrpc: '2.0', method: 'createAction', params: client.calls[0].params, id: 1 } + const wire = stringifyJsonRpc(envelope, binary) + const decoded = parseJsonRpc(wire, binary).params[1].inputBEEF + expect(Array.from(decoded)).toEqual(originalBytes) + expect(args.inputBEEF).toEqual(originalBytes) + if (binary) { + expect(JSON.parse(wire).params[1].inputBEEF.$bsvBinary).toBe('base64') + expect(Buffer.byteLength(wire)).toBeLessThan(Buffer.byteLength(stringifyJsonRpc(envelope, false)) / 2) + } else { + expect(JSON.parse(wire).params[1].inputBEEF).toEqual(originalBytes) + } + }) + test('preserves BEEF V1 when client-side pruning is required', async () => { const required = makeTransaction(1000) const beef = new Beef(BEEF_V1) diff --git a/packages/wallet/wallet-toolbox/src/storage/remoting/__test/StorageServerRpc.test.ts b/packages/wallet/wallet-toolbox/src/storage/remoting/__test/StorageServerRpc.test.ts index d7c1bb6faa..8399932766 100644 --- a/packages/wallet/wallet-toolbox/src/storage/remoting/__test/StorageServerRpc.test.ts +++ b/packages/wallet/wallet-toolbox/src/storage/remoting/__test/StorageServerRpc.test.ts @@ -185,13 +185,24 @@ describe('StorageServer JSON-RPC boundary', () => { const reqs = [{ ...time, provenTxReqId: 1, rawTx: bytes, inputBEEF: bytes, extraNumbers: bytes }] const server = makeServer({ findOutputsAuth: async () => outputs, findProvenTxReqsAuth: async () => reqs }) for (const { method, params, rows, fields } of [ - { method: 'findOutputsAuth', params: [{ identityKey: 'alice' }, { partial: {} }], rows: outputs, - fields: ['lockingScript'] }, + { + method: 'findOutputsAuth', + params: [{ identityKey: 'alice' }, { partial: {} }], + rows: outputs, + fields: ['lockingScript'] + }, { method: 'findProvenTxReqs', params: [{ partial: {} }], rows: reqs, fields: ['rawTx', 'inputBEEF'] } ]) { const captured = makeResponse() - await invoke(server, 'handleRpcRequest', makeRequest({ jsonrpc: '2.0', method, params, id: 1 }, - binary ? { [BINARY_ENCODING_HEADER]: BINARY_ENCODING } : {}), captured.response) + await invoke( + server, + 'handleRpcRequest', + makeRequest( + { jsonrpc: '2.0', method, params, id: 1 }, + binary ? { [BINARY_ENCODING_HEADER]: BINARY_ENCODING } : {} + ), + captured.response + ) expect(captured.statusCode).toBe(200) for (const field of fields) { if (binary) expect(captured.body.result[0][field].$bsvBinary).toBe('base64') @@ -204,6 +215,32 @@ describe('StorageServer JSON-RPC boundary', () => { expect(reqs[0].rawTx).toBe(bytes) }) + test.each([false, true])( + 'retains the response ceiling for large row bytes with binary negotiation %s', + async binary => { + const bytes = Array.from({ length: 150_000 }, (_, i) => i % 256) + const now = new Date() + const rows = [{ provenTxReqId: 1, rawTx: bytes, created_at: now, updated_at: now }] + const server = makeServer({ findProvenTxReqsAuth: async () => rows }, { maxRpcResponseBytes: 300_000 }) + const captured = makeResponse() + await invoke( + server, + 'handleRpcRequest', + makeRequest( + { jsonrpc: '2.0', method: 'findProvenTxReqs', params: [{ partial: {} }], id: 1 }, + binary ? { [BINARY_ENCODING_HEADER]: BINARY_ENCODING } : {} + ), + captured.response + ) + expect(captured.statusCode).toBe(binary ? 200 : 413) + if (binary) { + expect(validateEntities(parseJsonRpc(JSON.stringify(captured.body), true).result)).toEqual(rows) + expect(Buffer.byteLength(JSON.stringify(captured.body))).toBeLessThan(300_000) + } + expect(rows[0].rawTx).toBe(bytes) + } + ) + test('accounts for HTML escaping when enforcing the response-size ceiling', async () => { const server = makeServer({ getSettings: () => ({ value: '<'.repeat(25) }) }, { maxRpcResponseBytes: 100 }) const captured = makeResponse() diff --git a/packages/wallet/wallet-toolbox/src/storage/remoting/__test/tableRowsBinary.test.ts b/packages/wallet/wallet-toolbox/src/storage/remoting/__test/tableRowsBinary.test.ts new file mode 100644 index 0000000000..144c231afe --- /dev/null +++ b/packages/wallet/wallet-toolbox/src/storage/remoting/__test/tableRowsBinary.test.ts @@ -0,0 +1,26 @@ +import { tableRowsBinary } from '../syncChunkBinary' +import { parseJsonRpc, stringifyJsonRpc } from '../BinaryJson' + +describe('schema-defined row bytes', () => { + test('copies large declared byte fields without mutating rows or tagging unrelated numbers', () => { + const bytes = Array.from({ length: 128 }, (_, i) => i) + const row = { rawTx: bytes, inputBEEF: bytes, extraNumbers: bytes } + const result = tableRowsBinary('provenTxReqs', [row]) + const wire = JSON.parse(stringifyJsonRpc(result, true)) + expect(wire[0].rawTx.$bsvBinary).toBe('base64') + expect(wire[0].inputBEEF.$bsvBinary).toBe('base64') + expect(wire[0].extraNumbers).toEqual(bytes) + expect(Array.from(parseJsonRpc(JSON.stringify(wire), true)[0].rawTx)).toEqual(bytes) + expect(row.rawTx).toBe(bytes) + expect(result[0]).not.toBe(row) + }) + + test.each([undefined, new Uint8Array(128), [1, 2], Array(128).fill(-1), Array(128).fill(256), Array(128).fill(1.5)])( + 'preserves absent, typed, small or invalid declared bytes: %p', + value => { + const row = { lockingScript: value } + expect(tableRowsBinary('outputs', [row])[0].lockingScript).toBe(value) + expect(row.lockingScript).toBe(value) + } + ) +}) From c5f6a97f87f110546152b615e8cdc011b3e96b6f Mon Sep 17 00:00:00 2001 From: Ty J Everett Date: Sat, 10 Oct 2026 16:21:22 -0700 Subject: [PATCH 09/10] test(wallet-toolbox): qualify empty IndexedDB filters and ownership --- docs/packages/wallet/wallet-toolbox-client.md | 5 ++ docs/packages/wallet/wallet-toolbox.md | 5 ++ docs/reference/package-api-migrations.md | 12 ++-- governance/package-release-notes.json | 8 +-- packages/wallet/wallet-toolbox/CHANGELOG.md | 4 ++ .../test/storage/idb/find.test.ts | 58 +++++++++++++++++-- 6 files changed, 78 insertions(+), 14 deletions(-) diff --git a/docs/packages/wallet/wallet-toolbox-client.md b/docs/packages/wallet/wallet-toolbox-client.md index 55c1a25ad0..c83bb2bce2 100644 --- a/docs/packages/wallet/wallet-toolbox-client.md +++ b/docs/packages/wallet/wallet-toolbox-client.md @@ -77,6 +77,11 @@ negotiated compact binary JSON codec. Legacy peers retain numeric-array JSON and identical transaction/proof bytes. Authentication and payload ceilings remain unchanged; upgrade both clients and active storage for the savings. +IndexedDB treats empty certificate certifier/type, transaction status and +output-tag ID arrays like omitted optional filters, matching Knex. Nonempty +arrays, partial predicates and user ownership remain enforced. No API, wire +or database migration is required. + ## Backup and recovery Recover both key material and wallet records; a manager snapshot or device diff --git a/docs/packages/wallet/wallet-toolbox.md b/docs/packages/wallet/wallet-toolbox.md index 261a50bfe3..2037d84b36 100644 --- a/docs/packages/wallet/wallet-toolbox.md +++ b/docs/packages/wallet/wallet-toolbox.md @@ -24,6 +24,11 @@ negotiated compact binary JSON codec. Legacy peers retain numeric-array JSON and identical transaction/proof bytes. Authentication and payload ceilings remain unchanged; upgrade both clients and active storage for the savings. +IndexedDB treats empty certificate certifier/type, transaction status and +output-tag ID arrays like omitted optional filters, matching Knex. Nonempty +arrays, partial predicates and user ownership remain enforced. No API, wire +or database migration is required. + ## Optional registry descriptions ProtoMap, BasketMap and CertMap supply descriptive metadata, not permission grants. diff --git a/docs/reference/package-api-migrations.md b/docs/reference/package-api-migrations.md index ddb72bb404..4db2a66ee8 100644 --- a/docs/reference/package-api-migrations.md +++ b/docs/reference/package-api-migrations.md @@ -54,8 +54,8 @@ and clean-consumer tests remain the executable type authority. | `@bsv/verifast` | `0.3.6` | `0.3.7` | patch | [API and usage](../packages/sdk/verifast.md) | Valid typed verification calls and worker protocols remain compatible. Custom module factories and WASM adapters must return the exact documented binary and boolean shapes; coercive network, height, flag, byte, batch, verdict, lifecycle, or disposed-instance values now fail closed. Keep THIRD_PARTY_NOTICES.md and LICENSES/ with every JavaScript and WebAssembly distribution. SDK consumers may retain ^2.1.8 or install the proposed SDK3 candidate. The immutable SDK2.8.11 reference has nine pre-existing SDK cold-leaf/UMD import defects; all historical floor versions are not qualified by this matrix. SDK3 removes the obsolete DID token API; use the identity-key DID and optional BRC52 adapters described in docs/guides/identity-did-vc-migration.md. No other public API or wire migration is introduced by the peer alternative. | | `@bsv/wallet-helper` | `0.1.8` | `0.1.9` | patch | [API and usage](../packages/helpers/wallet-helper.md) | getAddress now derives with forSelf: true and returns the caller-owned side of the bilateral relationship. Applications that stored or coordinated the previous peer-owned result must regenerate and exchange the corrected address before sending value. Amount must be an integer from 1 through 1,000 and counterparties must be valid public keys. Valid canonical transaction-builder flows remain supported; malformed, ambiguous, or wallet-mutated results now fail closed. Distributors must retain THIRD_PARTY_NOTICES.md and LICENSES/ with the package. SDK consumers may retain ^2.1.6 or install the proposed SDK3 candidate. The immutable SDK2.8.11 reference has nine pre-existing SDK cold-leaf/UMD import defects; all historical floor versions are not qualified by this matrix. SDK3 removes the obsolete DID token API; use the identity-key DID and optional BRC52 adapters described in docs/guides/identity-did-vc-migration.md. No other public API or wire migration is introduced by the peer alternative. | | `@bsv/wallet-relay` | `0.5.1` | `0.5.2` | patch | [API and usage](../packages/wallet/wallet-relay.md) | Enable signed QR codes and distribute only HTTPS pairing origins and relay API URLs. Root-relative API paths and loopback HTTP remain supported. Configure onApprovalRequired for every method not deliberately listed in autoApproveMethods; unsigned pairing URIs and implicit approval are no longer accepted. Existing relay sessions, custom RPC method names, and supported wallet RPC byte encodings remain valid, and host applications continue to provide their matching Express runtime and type graph. Version 0.5 defaults to two missed pongs; set maxMissedHeartbeats to 1 to retain the prior heartbeat policy. Heartbeat intervals must fit the Node timer range. Preserve Cache-Control: no-store at proxies, prefer the stable bsv-wallet-relay plus token WebSocket subprotocols over the legacy token query parameter, and configure edge rate limiting in addition to the bounded in-process defaults. The 24-hour connected-session lifetime no longer renews on reconnect, malformed wallet calls fail with code 400, and WalletRelayClient retains at most 100 log entries unless maxLogEntries is configured. The QR remains a short-lived bearer invitation: keep it private and require explicit operation approval. Relay envelopes, cryptographic framing, and wallet RPC encodings remain unchanged. SDK consumers may retain ^2.4.1 or install the proposed SDK3 candidate. The immutable SDK2.8.11 reference has nine pre-existing SDK cold-leaf/UMD import defects; all historical floor versions are not qualified by this matrix. SDK3 removes the obsolete DID token API; use the identity-key DID and optional BRC52 adapters described in docs/guides/identity-did-vc-migration.md. No other public API or wire migration is introduced by the peer alternative. | -| `@bsv/wallet-toolbox` | `2.14.5` | `2.14.7` | patch | [API and usage](../packages/wallet/wallet-toolbox.md) | No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. No API, database or peer migration is required for compact BEEF transport. Upgrade clients and active storage to obtain both request and response savings; older peers retain ordinary JSON. Existing payload ceilings still apply. | -| `@bsv/wallet-toolbox-client` | `2.14.5` | `2.14.7` | patch | [API and usage](../packages/wallet/wallet-toolbox-client.md) | No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. No API, database or peer migration is required for compact BEEF transport. Upgrade clients and active storage to obtain both request and response savings; older peers retain ordinary JSON. Existing payload ceilings still apply. | +| `@bsv/wallet-toolbox` | `2.14.5` | `2.14.7` | patch | [API and usage](../packages/wallet/wallet-toolbox.md) | No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. No API, database or peer migration is required for compact BEEF transport. Upgrade clients and active storage to obtain both request and response savings; older peers retain ordinary JSON. Existing payload ceilings still apply. No API, wire or schema migration is required for the IndexedDB filter fix. Empty optional arrays now behave like omitted filters. Applications still receive only records allowed by their user and partial predicates; nonempty arrays retain their restrictions. | +| `@bsv/wallet-toolbox-client` | `2.14.5` | `2.14.7` | patch | [API and usage](../packages/wallet/wallet-toolbox-client.md) | No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. No API, database or peer migration is required for compact BEEF transport. Upgrade clients and active storage to obtain both request and response savings; older peers retain ordinary JSON. Existing payload ceilings still apply. No API, wire or schema migration is required for the IndexedDB filter fix. Empty optional arrays now behave like omitted filters. Applications still receive only records allowed by their user and partial predicates; nonempty arrays retain their restrictions. | | `@bsv/wallet-toolbox-mobile` | `2.14.5` | `2.14.7` | patch | [API and usage](../packages/wallet/wallet-toolbox-mobile.md) | No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. No API, database or peer migration is required for compact BEEF transport. Upgrade clients and active storage to obtain both request and response savings; older peers retain ordinary JSON. Existing payload ceilings still apply. | | `create-bsv-app` | `1.1.2` | `1.1.2` | none | [API and usage](../packages/helpers/create-bsv-app.md) | Existing CLI flags, network choices, and generated project structure are unchanged. Regenerate or update dependencies after the patched packages are published. Distributors must retain THIRD_PARTY_NOTICES.md and LICENSES/ with the package. | @@ -515,8 +515,8 @@ CLI entry points: `{"wallet-relay":"./bin/init.mjs"}`. - Package documentation: [docs/packages/wallet/wallet-toolbox.md](../packages/wallet/wallet-toolbox.md) - Source: [packages/wallet/wallet-toolbox](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/wallet/wallet-toolbox) -- Release note: WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Adds an SDK3 peer alternative while retaining the existing SDK2.8.0 floor. Public Argon2id declarations reference the typed hash-wasm package while retaining the same generic binary/string contract and emitted runtime bytes. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged. BEEF request data and schema-declared storage response bytes use the existing negotiated compact binary JSON encoding. Legacy peers retain numeric-array JSON, existing authentication and payload bounds, and byte-for-byte transaction/proof content. -- Migration: No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. No API, database or peer migration is required for compact BEEF transport. Upgrade clients and active storage to obtain both request and response savings; older peers retain ordinary JSON. Existing payload ceilings still apply. +- Release note: WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Adds an SDK3 peer alternative while retaining the existing SDK2.8.0 floor. Public Argon2id declarations reference the typed hash-wasm package while retaining the same generic binary/string contract and emitted runtime bytes. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged. BEEF request data and schema-declared storage response bytes use the existing negotiated compact binary JSON encoding. Legacy peers retain numeric-array JSON, existing authentication and payload bounds, and byte-for-byte transaction/proof content. IndexedDB treats empty certificate certifier/type, transaction status and output-tag ID arrays as unrestricted optional filters, matching Knex. Nonempty filters, partial predicates and user ownership remain enforced. +- Migration: No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. No API, database or peer migration is required for compact BEEF transport. Upgrade clients and active storage to obtain both request and response savings; older peers retain ordinary JSON. Existing payload ceilings still apply. No API, wire or schema migration is required for the IndexedDB filter fix. Empty optional arrays now behave like omitted filters. Applications still receive only records allowed by their user and partial predicates; nonempty arrays retain their restrictions. | Public subpath | Runtime target(s) | Declaration target(s) | | ---------------- | ---------------------------------------------------- | -------------------------- | @@ -529,8 +529,8 @@ CLI entry points: `{"wallet-relay":"./bin/init.mjs"}`. - Package documentation: [docs/packages/wallet/wallet-toolbox-client.md](../packages/wallet/wallet-toolbox-client.md) - Source: [packages/wallet/wallet-toolbox/client](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/wallet/wallet-toolbox/client) -- Release note: WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged. BEEF request data and schema-declared storage response bytes use the existing negotiated compact binary JSON encoding. Legacy peers retain numeric-array JSON, existing authentication and payload bounds, and byte-for-byte transaction/proof content. -- Migration: No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. No API, database or peer migration is required for compact BEEF transport. Upgrade clients and active storage to obtain both request and response savings; older peers retain ordinary JSON. Existing payload ceilings still apply. +- Release note: WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged. BEEF request data and schema-declared storage response bytes use the existing negotiated compact binary JSON encoding. Legacy peers retain numeric-array JSON, existing authentication and payload bounds, and byte-for-byte transaction/proof content. IndexedDB treats empty certificate certifier/type, transaction status and output-tag ID arrays as unrestricted optional filters, matching Knex. Nonempty filters, partial predicates and user ownership remain enforced. +- Migration: No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. No API, database or peer migration is required for compact BEEF transport. Upgrade clients and active storage to obtain both request and response savings; older peers retain ordinary JSON. Existing payload ceilings still apply. No API, wire or schema migration is required for the IndexedDB filter fix. Empty optional arrays now behave like omitted filters. Applications still receive only records allowed by their user and partial predicates; nonempty arrays retain their restrictions. | Public subpath | Runtime target(s) | Declaration target(s) | | ---------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | diff --git a/governance/package-release-notes.json b/governance/package-release-notes.json index a488c27c20..44f4d45b17 100644 --- a/governance/package-release-notes.json +++ b/governance/package-release-notes.json @@ -210,15 +210,15 @@ "name": "@bsv/wallet-toolbox", "publishedVersion": "2.14.5", "releaseType": "patch", - "summary": "WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Adds an SDK3 peer alternative while retaining the existing SDK2.8.0 floor. Public Argon2id declarations reference the typed hash-wasm package while retaining the same generic binary/string contract and emitted runtime bytes. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged. BEEF request data and schema-declared storage response bytes use the existing negotiated compact binary JSON encoding. Legacy peers retain numeric-array JSON, existing authentication and payload bounds, and byte-for-byte transaction/proof content.", - "migration": "No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. No API, database or peer migration is required for compact BEEF transport. Upgrade clients and active storage to obtain both request and response savings; older peers retain ordinary JSON. Existing payload ceilings still apply." + "summary": "WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Adds an SDK3 peer alternative while retaining the existing SDK2.8.0 floor. Public Argon2id declarations reference the typed hash-wasm package while retaining the same generic binary/string contract and emitted runtime bytes. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged. BEEF request data and schema-declared storage response bytes use the existing negotiated compact binary JSON encoding. Legacy peers retain numeric-array JSON, existing authentication and payload bounds, and byte-for-byte transaction/proof content. IndexedDB treats empty certificate certifier/type, transaction status and output-tag ID arrays as unrestricted optional filters, matching Knex. Nonempty filters, partial predicates and user ownership remain enforced.", + "migration": "No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. No API, database or peer migration is required for compact BEEF transport. Upgrade clients and active storage to obtain both request and response savings; older peers retain ordinary JSON. Existing payload ceilings still apply. No API, wire or schema migration is required for the IndexedDB filter fix. Empty optional arrays now behave like omitted filters. Applications still receive only records allowed by their user and partial predicates; nonempty arrays retain their restrictions." }, { "name": "@bsv/wallet-toolbox-client", "publishedVersion": "2.14.5", "releaseType": "patch", - "summary": "WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged. BEEF request data and schema-declared storage response bytes use the existing negotiated compact binary JSON encoding. Legacy peers retain numeric-array JSON, existing authentication and payload bounds, and byte-for-byte transaction/proof content.", - "migration": "No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. No API, database or peer migration is required for compact BEEF transport. Upgrade clients and active storage to obtain both request and response savings; older peers retain ordinary JSON. Existing payload ceilings still apply." + "summary": "WalletPermissionsManager retires signed no-send ownership and reference aliases after createAction or signAction reports sending or unproven, including sendWith-only calls and successful members of an undelayed review error. Failed or unreported actions retain their originator binding for retry or abort. Cleanup visits only the retired transaction's references and preserves aliases reused by newer actions. Fixes WAB faucet redemption for an empty wallet when storage adds a service charge. Local signer decisions for independently validated commission and derived change are retained privately through the permissions wrapper and bound by the SDK before signing. UMP pins anchor verified token-update lineage: password and token updates supersede their pinned predecessor, while unrelated historical continuations cannot override the anchor. Explicit overlay history is linked past confirmed Merkle anchors. Updates with missing pin ancestry remain indeterminate instead of selecting an unrelated continuation. Every retained token spend proves control through its unlocking script, including confirmed updates and hash rotation; funding paths do not establish token lineage. BRC-177 anchors fund both the protected action and an economic reclaim. Protected actions pay the bounded reclaim allowance surplus as fee and produce no automatically generated wallet change; ordinary change planning and its positive output cap remain unchanged. BEEF request data and schema-declared storage response bytes use the existing negotiated compact binary JSON encoding. Legacy peers retain numeric-array JSON, existing authentication and payload bounds, and byte-for-byte transaction/proof content. IndexedDB treats empty certificate certifier/type, transaction status and output-tag ID arrays as unrestricted optional filters, matching Knex. Nonempty filters, partial predicates and user ownership remain enforced.", + "migration": "No public API, wire, database, or consumer migration is required. Pending no-send actions and retryable failures retain the same originator-bound abort behavior; positively queued or broadcast actions no longer retain session ownership records. Upgrade the SDK to a release exposing completeBoundAction.outputAuthorizationVersion=1 alongside wallet-toolbox 2.14.6. SDK2 peers keep their existing strict behavior; they do not obtain this fee-bearing faucet fix. The main SDK3.1 candidate retains the separate SDK3 retirement migration; SDK2 hosts require a coordinated additive backport or that migration. Serialized or cross-package-instance result adapters discard local authority and fail closed. Signup interruption/retry persistence is unchanged; reconcile any previous faucet transaction before repeating a failed signup. No API or schema migration is required for pin continuity. Retire a support pin only after unpinned lookup independently returns the verified current token. Deploy overlay 2.6.4 plus overlay-topics 2.0.1 or an equivalent history decider for confirmed ancestry. Stored WAB pins remain lineage anchors; the client follows verified descendants without needing an administrative pin rewrite. No API, wire or database migration is required for the BRC-177 funding fix. A protected action can pay the difference between its delivery need and the reclaim floor as miner fee. Funding/reclaim fees remain the wallet owner's responsibility; upgrade the active storage implementation as well as client packages. No API, database or peer migration is required for compact BEEF transport. Upgrade clients and active storage to obtain both request and response savings; older peers retain ordinary JSON. Existing payload ceilings still apply. No API, wire or schema migration is required for the IndexedDB filter fix. Empty optional arrays now behave like omitted filters. Applications still receive only records allowed by their user and partial predicates; nonempty arrays retain their restrictions." }, { "name": "@bsv/wallet-toolbox-mobile", diff --git a/packages/wallet/wallet-toolbox/CHANGELOG.md b/packages/wallet/wallet-toolbox/CHANGELOG.md index 9d1a2e14cf..f99ab57c2c 100644 --- a/packages/wallet/wallet-toolbox/CHANGELOG.md +++ b/packages/wallet/wallet-toolbox/CHANGELOG.md @@ -6,6 +6,10 @@ attention to changes that materially alter behavior or extend functionality. ## wallet-toolbox 2.14.7 +- IndexedDB empty certificate certifier/type, transaction status and output-tag + ID arrays mean no optional filter, matching Knex. Nonempty arrays, partial + predicates and user ownership remain enforced; no schema migration is required. + - BEEF requests and schema-declared storage response bytes use the existing negotiated compact binary JSON codec. Legacy peers retain numeric-array JSON and identical transaction/proof bytes. Existing authentication and diff --git a/packages/wallet/wallet-toolbox/test/storage/idb/find.test.ts b/packages/wallet/wallet-toolbox/test/storage/idb/find.test.ts index 5273bac729..f1f0b7fb9f 100644 --- a/packages/wallet/wallet-toolbox/test/storage/idb/find.test.ts +++ b/packages/wallet/wallet-toolbox/test/storage/idb/find.test.ts @@ -72,6 +72,15 @@ describe('idb find tests', () => { types: [setup.u1cert2.type] }) ).toHaveLength(1) + expect( + await storage.findCertificates({ partial: {}, certifiers: [setup.u1cert1.certifier], types: [] }) + ).toHaveLength(1) + expect( + await storage.findCertificates({ partial: { userId: setup.u1.userId }, certifiers: [], types: [] }) + ).toHaveLength(3) + expect( + await storage.findCertificates({ partial: { userId: setup.u2.userId }, certifiers: [], types: [] }) + ).toEqual([]) } }) @@ -117,8 +126,21 @@ describe('idb find tests', () => { }) test('6 find Transaction', async () => { - for (const { storage, setup: _setup } of setups) { - expect(await storage.findTransactions({ partial: {} })).toHaveLength(3) + for (const { storage, setup } of setups) { + const rows = await storage.findTransactions({ partial: {} }) + expect(rows).toHaveLength(3) + expect(await storage.findTransactions({ partial: {}, status: [] })).toEqual(rows) + const status = rows[0].status + expect(await storage.findTransactions({ partial: {}, status: [status] })).toEqual( + rows.filter(row => row.status === status) + ) + expect(await storage.findTransactions({ partial: { userId: setup.u1.userId }, status: [] })).toEqual( + rows.filter(row => row.userId === setup.u1.userId) + ) + expect(await storage.findTransactions({ partial: { userId: setup.u2.userId }, status: [] })).toEqual( + rows.filter(row => row.userId === setup.u2.userId) + ) + expect(await storage.findTransactions({ partial: { userId: 99 }, status: [] })).toEqual([]) } }) @@ -141,8 +163,36 @@ describe('idb find tests', () => { }) test('10 find OutputTagMap', async () => { - for (const { storage, setup: _setup } of setups) { - expect(await storage.findOutputTagMaps({ partial: {} })).toHaveLength(3) + for (const { storage, setup } of setups) { + const rows = await storage.findOutputTagMaps({ partial: {} }) + expect(rows).toHaveLength(3) + expect(await storage.findOutputTagMaps({ partial: {}, tagIds: [] })).toEqual(rows) + expect(await storage.findOutputTagMaps({ partial: {}, tagIds: [setup.u1tag1.outputTagId] })).toEqual( + rows.filter(row => row.outputTagId === setup.u1tag1.outputTagId) + ) + expect(await storage.findOutputTagMaps({ partial: {}, tagIds: [Number.MAX_SAFE_INTEGER] })).toEqual([]) + expect(await storage.findOutputTagMaps({ partial: { outputId: setup.u1tx1o0.outputId }, tagIds: [] })).toEqual( + rows.filter(row => row.outputId === setup.u1tx1o0.outputId) + ) + if (!(storage instanceof StorageIdb)) throw new Error('IndexedDB fixture required') + const owned: typeof rows = [] + await storage.filterOutputTagMaps( + { partial: {}, tagIds: [] }, + row => { + owned.push(row) + }, + setup.u1.userId + ) + expect(owned).toEqual(rows) + const foreign: typeof rows = [] + await storage.filterOutputTagMaps( + { partial: {}, tagIds: [] }, + row => { + foreign.push(row) + }, + setup.u2.userId + ) + expect(foreign).toEqual([]) } }) From 3cc18d80adff807ce7fc3c7faeddde581f7aed7c Mon Sep 17 00:00:00 2001 From: Ty J Everett Date: Sat, 10 Oct 2026 16:32:14 -0700 Subject: [PATCH 10/10] test(wallet-toolbox): verify filter parity across IndexedDB and SQLite --- .../test/storage/idb/find.test.ts | 67 ++++++++++++------- 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/packages/wallet/wallet-toolbox/test/storage/idb/find.test.ts b/packages/wallet/wallet-toolbox/test/storage/idb/find.test.ts index f1f0b7fb9f..501f5bb3d9 100644 --- a/packages/wallet/wallet-toolbox/test/storage/idb/find.test.ts +++ b/packages/wallet/wallet-toolbox/test/storage/idb/find.test.ts @@ -2,6 +2,8 @@ import { _tu, TestSetup1 } from '../../utils/TestUtilsWalletStorage' import { sdk, StorageProvider, StorageProviderOptions } from '../../../src/index.client' import { StorageIdb } from '../../../src/storage/StorageIdb' +import { StorageKnex } from '../../../src/storage/StorageKnex' +import { knex } from 'knex' import 'fake-indexeddb/auto' @@ -14,12 +16,26 @@ describe('idb find tests', () => { beforeEach(async () => { const options: StorageProviderOptions = StorageProvider.createStorageBaseOptions(chain) - const storage = new StorageIdb(options) - await storage.dropAllData() - await storage.migrate('idb find tests', '1'.repeat(64)) - await storage.makeAvailable() - const setup = await _tu.createTestSetup1(storage) - setups = [{ setup, storage }] + const stores = [ + new StorageIdb(options), + new StorageKnex({ + ...options, + knex: knex({ + client: 'better-sqlite3', + connection: { filename: ':memory:' }, + useNullAsDefault: true, + pool: { min: 1, max: 1 } + }) + }) + ] + setups = [] + for (const storage of stores) { + await storage.dropAllData() + await storage.migrate('idb find tests', '1'.repeat(64)) + await storage.makeAvailable() + const setup = await _tu.createTestSetup1(storage) + setups.push({ setup, storage }) + } }) afterEach(async () => { @@ -174,25 +190,26 @@ describe('idb find tests', () => { expect(await storage.findOutputTagMaps({ partial: { outputId: setup.u1tx1o0.outputId }, tagIds: [] })).toEqual( rows.filter(row => row.outputId === setup.u1tx1o0.outputId) ) - if (!(storage instanceof StorageIdb)) throw new Error('IndexedDB fixture required') - const owned: typeof rows = [] - await storage.filterOutputTagMaps( - { partial: {}, tagIds: [] }, - row => { - owned.push(row) - }, - setup.u1.userId - ) - expect(owned).toEqual(rows) - const foreign: typeof rows = [] - await storage.filterOutputTagMaps( - { partial: {}, tagIds: [] }, - row => { - foreign.push(row) - }, - setup.u2.userId - ) - expect(foreign).toEqual([]) + if (storage instanceof StorageIdb) { + const owned: typeof rows = [] + await storage.filterOutputTagMaps( + { partial: {}, tagIds: [] }, + row => { + owned.push(row) + }, + setup.u1.userId + ) + expect(owned).toEqual(rows) + const foreign: typeof rows = [] + await storage.filterOutputTagMaps( + { partial: {}, tagIds: [] }, + row => { + foreign.push(row) + }, + setup.u2.userId + ) + expect(foreign).toEqual([]) + } } })