@bsv/wallet-toolbox-client 2.9.0 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/index.client.cjs +505 -327
- package/out/index.client.cjs.map +1 -1
- package/out/index.client.d.cts +82 -52
- package/out/index.client.d.cts.map +1 -1
- package/out/index.client.d.mts +82 -52
- package/out/index.client.d.mts.map +1 -1
- package/out/index.client.mjs +506 -328
- package/out/index.client.mjs.map +1 -1
- package/package.json +1 -1
package/out/index.client.cjs
CHANGED
|
@@ -4081,7 +4081,7 @@ var EntitySyncState = class EntitySyncState extends EntityBase {
|
|
|
4081
4081
|
log += formatSyncSection("OUTPUTS", c.outputs, (r) => `${r.outputId} ${r.txid}.${r.vout} ${r.transactionId} ${r.spendable ? "spendable" : ""} sats:${r.satoshis}`);
|
|
4082
4082
|
return log;
|
|
4083
4083
|
}
|
|
4084
|
-
async processSyncChunk(writer, args, chunk) {
|
|
4084
|
+
async processSyncChunk(writer, args, chunk, trx) {
|
|
4085
4085
|
const mes = [
|
|
4086
4086
|
new MergeEntity(chunk.provenTxs, EntityProvenTx.mergeFind, this.syncMap.provenTx),
|
|
4087
4087
|
new MergeEntity(chunk.outputBaskets, EntityOutputBasket.mergeFind, this.syncMap.outputBasket),
|
|
@@ -4102,16 +4102,16 @@ var EntitySyncState = class EntitySyncState extends EntityBase {
|
|
|
4102
4102
|
let done = true;
|
|
4103
4103
|
if (chunk.user != null) {
|
|
4104
4104
|
const ei = chunk.user;
|
|
4105
|
-
const { found, eo } = await EntityUser.mergeFind(writer, this.userId, ei);
|
|
4105
|
+
const { found, eo } = await EntityUser.mergeFind(writer, this.userId, ei, trx);
|
|
4106
4106
|
if (found) {
|
|
4107
|
-
if (await eo.mergeExisting(writer, args.since, ei)) {
|
|
4107
|
+
if (await eo.mergeExisting(writer, args.since, ei, void 0, trx)) {
|
|
4108
4108
|
maxUpdated_at = maxDate(maxUpdated_at, ei.updated_at);
|
|
4109
4109
|
updates++;
|
|
4110
4110
|
}
|
|
4111
4111
|
}
|
|
4112
4112
|
}
|
|
4113
4113
|
for (const me of mes) {
|
|
4114
|
-
const r = await me.merge(args.since, writer, this.userId, this.syncMap);
|
|
4114
|
+
const r = await me.merge(args.since, writer, this.userId, this.syncMap, trx);
|
|
4115
4115
|
me.esm.count += me.stateArray?.length || 0;
|
|
4116
4116
|
updates += r.updates;
|
|
4117
4117
|
inserts += r.inserts;
|
|
@@ -4122,7 +4122,7 @@ var EntitySyncState = class EntitySyncState extends EntityBase {
|
|
|
4122
4122
|
this.when = maxUpdated_at;
|
|
4123
4123
|
for (const me of mes) me.esm.count = 0;
|
|
4124
4124
|
}
|
|
4125
|
-
await this.updateStorage(writer, false);
|
|
4125
|
+
await this.updateStorage(writer, false, trx);
|
|
4126
4126
|
return {
|
|
4127
4127
|
done,
|
|
4128
4128
|
maxUpdated_at,
|
|
@@ -8254,28 +8254,70 @@ function verifyActionBatchManifestDigest(manifest) {
|
|
|
8254
8254
|
//#endregion
|
|
8255
8255
|
//#region ../src/utility/beefForTxids.ts
|
|
8256
8256
|
/**
|
|
8257
|
-
* Return
|
|
8257
|
+
* Return a minimal BEEF when the source contains data outside the requested
|
|
8258
|
+
* transaction dependency closure. Return undefined when no pruning is needed.
|
|
8258
8259
|
*
|
|
8259
|
-
*
|
|
8260
|
-
*
|
|
8260
|
+
* This form lets forwarding clients retain the caller's original bytes in the
|
|
8261
|
+
* common no-op case instead of rebuilding and reserializing an equivalent BEEF.
|
|
8262
|
+
*/
|
|
8263
|
+
function pruneBeefForTxids(source, txids) {
|
|
8264
|
+
const selection = selectTransactions(source, txids);
|
|
8265
|
+
if (selection.transactions.size === source.txs.length && selection.bumpIndexes.size === source.bumps.length) return;
|
|
8266
|
+
return copySelection(source, selection);
|
|
8267
|
+
}
|
|
8268
|
+
/**
|
|
8269
|
+
* Return an independent minimal BEEF needed to prove the requested transactions.
|
|
8270
|
+
*
|
|
8271
|
+
* Transactions remain in source order and are sorted by Beef when serialized.
|
|
8272
|
+
* The source is indexed and walked once, using an explicit stack so a hostile
|
|
8273
|
+
* dependency depth cannot exhaust the JavaScript call stack.
|
|
8261
8274
|
*/
|
|
8262
8275
|
function beefForTxids(source, txids) {
|
|
8263
|
-
|
|
8276
|
+
return copySelection(source, selectTransactions(source, txids));
|
|
8277
|
+
}
|
|
8278
|
+
function selectTransactions(source, txids) {
|
|
8279
|
+
const byTxid = /* @__PURE__ */ new Map();
|
|
8280
|
+
for (const tx of source.txs) byTxid.set(tx.txid, tx);
|
|
8281
|
+
const transactions = /* @__PURE__ */ new Set();
|
|
8282
|
+
const bumpIndexes = /* @__PURE__ */ new Set();
|
|
8264
8283
|
const visited = /* @__PURE__ */ new Set();
|
|
8265
|
-
const
|
|
8266
|
-
|
|
8284
|
+
const stack = [...txids];
|
|
8285
|
+
while (stack.length > 0) {
|
|
8286
|
+
const txid = stack.pop();
|
|
8287
|
+
if (txid == null || visited.has(txid)) continue;
|
|
8267
8288
|
visited.add(txid);
|
|
8268
|
-
const
|
|
8269
|
-
if (
|
|
8270
|
-
|
|
8271
|
-
|
|
8272
|
-
|
|
8273
|
-
|
|
8274
|
-
|
|
8289
|
+
const tx = byTxid.get(txid);
|
|
8290
|
+
if (tx == null) continue;
|
|
8291
|
+
transactions.add(tx);
|
|
8292
|
+
const bumpIndex = tx.bumpIndex;
|
|
8293
|
+
if (bumpIndex != null && Number.isSafeInteger(bumpIndex) && bumpIndex >= 0 && bumpIndex < source.bumps.length) bumpIndexes.add(bumpIndex);
|
|
8294
|
+
for (const inputTxid of tx.inputTxids) if (!visited.has(inputTxid)) stack.push(inputTxid);
|
|
8295
|
+
}
|
|
8296
|
+
return {
|
|
8297
|
+
transactions,
|
|
8298
|
+
bumpIndexes
|
|
8275
8299
|
};
|
|
8276
|
-
|
|
8300
|
+
}
|
|
8301
|
+
function copySelection(source, selection) {
|
|
8302
|
+
const beef = new _bsv_sdk.Beef(source.version);
|
|
8303
|
+
const bumpIndexMap = /* @__PURE__ */ new Map();
|
|
8304
|
+
for (let index = 0; index < source.bumps.length; index++) {
|
|
8305
|
+
if (!selection.bumpIndexes.has(index)) continue;
|
|
8306
|
+
bumpIndexMap.set(index, beef.bumps.length);
|
|
8307
|
+
beef.bumps.push(cloneMerklePath(source.bumps[index]));
|
|
8308
|
+
}
|
|
8309
|
+
for (const sourceTx of source.txs) {
|
|
8310
|
+
if (!selection.transactions.has(sourceTx)) continue;
|
|
8311
|
+
const bumpIndex = sourceTx.bumpIndex == null ? void 0 : bumpIndexMap.get(sourceTx.bumpIndex);
|
|
8312
|
+
const rawTx = sourceTx.rawTxUint8Array;
|
|
8313
|
+
const copy = rawTx == null ? _bsv_sdk.BeefTx.fromTxid(sourceTx.txid, bumpIndex) : new _bsv_sdk.BeefTx(Uint8Array.from(rawTx), bumpIndex, Array.from(sourceTx.inputTxids));
|
|
8314
|
+
beef.txs.push(copy);
|
|
8315
|
+
}
|
|
8277
8316
|
return beef;
|
|
8278
8317
|
}
|
|
8318
|
+
function cloneMerklePath(source) {
|
|
8319
|
+
return new _bsv_sdk.MerklePath(source.blockHeight, source.path.map((level) => level.map((leaf) => ({ ...leaf }))), false, false);
|
|
8320
|
+
}
|
|
8279
8321
|
//#endregion
|
|
8280
8322
|
//#region ../src/storage/methods/offsetKey.ts
|
|
8281
8323
|
function keyOffsetToHashedSecret(pub, keyOffset) {
|
|
@@ -10973,7 +11015,7 @@ function validateRequiredOutputs(storage, userId, vargs) {
|
|
|
10973
11015
|
* @returns {xinputs} extended validated required inputs.
|
|
10974
11016
|
*/
|
|
10975
11017
|
async function validateRequiredInputs(storage, userId, vargs) {
|
|
10976
|
-
|
|
11018
|
+
let beef = new _bsv_sdk.Beef();
|
|
10977
11019
|
if (vargs.inputs.length === 0) return {
|
|
10978
11020
|
storageBeef: beef,
|
|
10979
11021
|
beef,
|
|
@@ -10999,6 +11041,7 @@ async function validateRequiredInputs(storage, userId, vargs) {
|
|
|
10999
11041
|
inputsByTxid[input.outpoint.txid] ||= [];
|
|
11000
11042
|
inputsByTxid[input.outpoint.txid].push(input);
|
|
11001
11043
|
}
|
|
11044
|
+
beef = beefForTxids(beef, Object.keys(inputsByTxid));
|
|
11002
11045
|
const localKnownInputTxids = {};
|
|
11003
11046
|
for (const [txid, txInputs] of Object.entries(inputsByTxid)) localKnownInputTxids[txid] = txInputs.every((input) => {
|
|
11004
11047
|
const output = preloadedOutputsByOutpoint[`${input.outpoint.txid}.${input.outpoint.vout}`];
|
|
@@ -15260,11 +15303,19 @@ var StorageProvider = class StorageProvider extends StorageReaderWriter {
|
|
|
15260
15303
|
return await this.updateOutput(output.outputId, { basketId: void 0 });
|
|
15261
15304
|
}
|
|
15262
15305
|
async processSyncChunk(args, chunk) {
|
|
15263
|
-
|
|
15264
|
-
|
|
15265
|
-
|
|
15266
|
-
|
|
15267
|
-
|
|
15306
|
+
return await this.transaction(async (trx) => {
|
|
15307
|
+
const user = verifyTruthy(verifyOneOrNone(await this.findUsers({
|
|
15308
|
+
partial: { identityKey: args.identityKey },
|
|
15309
|
+
trx
|
|
15310
|
+
})));
|
|
15311
|
+
return await new EntitySyncState(verifyOne(await this.findSyncStates({
|
|
15312
|
+
partial: {
|
|
15313
|
+
storageIdentityKey: args.fromStorageIdentityKey,
|
|
15314
|
+
userId: user.userId
|
|
15315
|
+
},
|
|
15316
|
+
trx
|
|
15317
|
+
}))).processSyncChunk(this, args, chunk, trx);
|
|
15318
|
+
});
|
|
15268
15319
|
}
|
|
15269
15320
|
/**
|
|
15270
15321
|
* Handles storage changes when a valid MerklePath and mined block header are found for a ProvenTxReq txid.
|
|
@@ -17329,8 +17380,10 @@ var StorageIdb = class extends StorageProvider {
|
|
|
17329
17380
|
await tx.done;
|
|
17330
17381
|
return r;
|
|
17331
17382
|
} catch (err) {
|
|
17332
|
-
|
|
17333
|
-
|
|
17383
|
+
try {
|
|
17384
|
+
tx.abort();
|
|
17385
|
+
await tx.done;
|
|
17386
|
+
} catch {}
|
|
17334
17387
|
throw err;
|
|
17335
17388
|
}
|
|
17336
17389
|
}
|
|
@@ -18297,6 +18350,25 @@ var StorageClientBase = class {
|
|
|
18297
18350
|
* @returns `StorageCreateActionResults` supporting additional wallet processing to yield `createAction` results.
|
|
18298
18351
|
*/
|
|
18299
18352
|
async createAction(auth, args) {
|
|
18353
|
+
if (args.inputBEEF != null) if (args.inputs.length === 0) args = {
|
|
18354
|
+
...args,
|
|
18355
|
+
inputBEEF: void 0
|
|
18356
|
+
};
|
|
18357
|
+
else {
|
|
18358
|
+
let source;
|
|
18359
|
+
try {
|
|
18360
|
+
source = _bsv_sdk.Beef.fromBinary(args.inputBEEF);
|
|
18361
|
+
} catch {
|
|
18362
|
+
source = void 0;
|
|
18363
|
+
}
|
|
18364
|
+
if (source != null) {
|
|
18365
|
+
const pruned = pruneBeefForTxids(source, args.inputs.map((input) => input.outpoint.txid));
|
|
18366
|
+
if (pruned != null) args = {
|
|
18367
|
+
...args,
|
|
18368
|
+
inputBEEF: pruned.toBinary()
|
|
18369
|
+
};
|
|
18370
|
+
}
|
|
18371
|
+
}
|
|
18300
18372
|
return await this.rpcCall("createAction", [auth, args]);
|
|
18301
18373
|
}
|
|
18302
18374
|
/**
|
|
@@ -30967,6 +31039,7 @@ var SetupClient = class SetupClient {
|
|
|
30967
31039
|
};
|
|
30968
31040
|
//#endregion
|
|
30969
31041
|
//#region ../src/CWIStyleWalletManager.ts
|
|
31042
|
+
const CWI_COMPONENT = "wallet-toolbox.cwi-manager";
|
|
30970
31043
|
/**
|
|
30971
31044
|
* Number of rounds used in PBKDF2 for deriving password keys.
|
|
30972
31045
|
*/
|
|
@@ -31199,11 +31272,11 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31199
31272
|
* @param hash The 32-byte SHA-256 hash of the presentation key.
|
|
31200
31273
|
* @returns A UMPToken object (including currentOutpoint) if found, otherwise undefined.
|
|
31201
31274
|
*/
|
|
31202
|
-
async findByPresentationKeyHash(hash) {
|
|
31203
|
-
return
|
|
31275
|
+
async findByPresentationKeyHash(hash, options) {
|
|
31276
|
+
return this.findToken({
|
|
31204
31277
|
service: "ls_users",
|
|
31205
31278
|
query: { presentationHash: _bsv_sdk.Utils.toHex(hash) }
|
|
31206
|
-
}, "presentation");
|
|
31279
|
+
}, "presentation", options);
|
|
31207
31280
|
}
|
|
31208
31281
|
/**
|
|
31209
31282
|
* Finds a UMP token on-chain by the given recovery key hash, if it exists.
|
|
@@ -31212,13 +31285,13 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31212
31285
|
* @param hash The 32-byte SHA-256 hash of the recovery key.
|
|
31213
31286
|
* @returns A UMPToken object (including currentOutpoint) if found, otherwise undefined.
|
|
31214
31287
|
*/
|
|
31215
|
-
async findByRecoveryKeyHash(hash) {
|
|
31216
|
-
return
|
|
31288
|
+
async findByRecoveryKeyHash(hash, options) {
|
|
31289
|
+
return this.findToken({
|
|
31217
31290
|
service: "ls_users",
|
|
31218
31291
|
query: { recoveryHash: _bsv_sdk.Utils.toHex(hash) }
|
|
31219
|
-
}, "recovery");
|
|
31292
|
+
}, "recovery", options);
|
|
31220
31293
|
}
|
|
31221
|
-
async findToken(question, lookupKind) {
|
|
31294
|
+
async findToken(question, lookupKind, options) {
|
|
31222
31295
|
const correlationId = this.telemetry.enabled === true ? this.telemetry.createCorrelationId() : void 0;
|
|
31223
31296
|
const startedAt = Date.now();
|
|
31224
31297
|
this.telemetry.capture({
|
|
@@ -31235,34 +31308,39 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31235
31308
|
correlationId
|
|
31236
31309
|
});
|
|
31237
31310
|
} catch (error) {
|
|
31238
|
-
const diagnostics = this.
|
|
31239
|
-
this.
|
|
31311
|
+
const diagnostics = this.emptyStats(correlationId);
|
|
31312
|
+
this.lookupFailed(lookupKind, "lookup-unavailable", diagnostics, startedAt, error);
|
|
31240
31313
|
throw new UMPTokenLookupError("lookup-unavailable", diagnostics, { cause: error });
|
|
31241
31314
|
}
|
|
31242
|
-
const diagnostics = this.
|
|
31315
|
+
const diagnostics = this.diagnosticsFor(resolution);
|
|
31243
31316
|
const tokens = this.parseLookupAnswers(resolution.answer);
|
|
31244
31317
|
const expectedHash = question.query[lookupKind === "presentation" ? "presentationHash" : "recoveryHash"].toLowerCase();
|
|
31245
31318
|
const matchingTokens = tokens.filter((token) => _bsv_sdk.Utils.toHex(lookupKind === "presentation" ? token.presentationHash : token.recoveryHash).toLowerCase() === expectedHash);
|
|
31246
31319
|
if (matchingTokens.length > 1) {
|
|
31247
31320
|
const newest = this.resolveNewestToken(matchingTokens, resolution.answer.outputs);
|
|
31248
31321
|
if (newest != null) {
|
|
31249
|
-
this.
|
|
31322
|
+
this.lookupDone(lookupKind, "found", diagnostics, startedAt, { supersededTokens: matchingTokens.length - 1 });
|
|
31250
31323
|
return newest;
|
|
31251
31324
|
}
|
|
31325
|
+
const pinned = options?.pinnedOutpoint ? matchingTokens.find((token) => token.currentOutpoint === options.pinnedOutpoint) : void 0;
|
|
31326
|
+
if (pinned != null) {
|
|
31327
|
+
this.lookupDone(lookupKind, "found", diagnostics, startedAt);
|
|
31328
|
+
return pinned;
|
|
31329
|
+
}
|
|
31252
31330
|
const reason = "token-ambiguous";
|
|
31253
|
-
this.
|
|
31331
|
+
this.lookupFailed(lookupKind, reason, diagnostics, startedAt);
|
|
31254
31332
|
throw new UMPTokenLookupError(reason, diagnostics);
|
|
31255
31333
|
}
|
|
31256
31334
|
if (matchingTokens.length === 1) {
|
|
31257
|
-
this.
|
|
31335
|
+
this.lookupDone(lookupKind, "found", diagnostics, startedAt);
|
|
31258
31336
|
return matchingTokens[0];
|
|
31259
31337
|
}
|
|
31260
31338
|
if (resolution.progress.emptyHosts > 0) {
|
|
31261
|
-
this.
|
|
31339
|
+
this.lookupDone(lookupKind, "not-found", diagnostics, startedAt);
|
|
31262
31340
|
return;
|
|
31263
31341
|
}
|
|
31264
31342
|
const reason = resolution.answer.outputs.length > 0 ? "token-malformed" : "lookup-incomplete";
|
|
31265
|
-
this.
|
|
31343
|
+
this.lookupFailed(lookupKind, reason, diagnostics, startedAt);
|
|
31266
31344
|
throw new UMPTokenLookupError(reason, diagnostics);
|
|
31267
31345
|
}
|
|
31268
31346
|
/**
|
|
@@ -31293,7 +31371,7 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31293
31371
|
spent: /* @__PURE__ */ new Set()
|
|
31294
31372
|
};
|
|
31295
31373
|
evidence.txs.push(tx);
|
|
31296
|
-
this.
|
|
31374
|
+
this.collectSpends(tx, evidence.spent, /* @__PURE__ */ new Set());
|
|
31297
31375
|
evidenceByCandidate.set(outpoint, evidence);
|
|
31298
31376
|
} catch {}
|
|
31299
31377
|
if (evidenceByCandidate.size !== candidates.size) return void 0;
|
|
@@ -31302,7 +31380,7 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31302
31380
|
const provenContinuations = survivors.filter((outpoint) => {
|
|
31303
31381
|
const evidence = evidenceByCandidate.get(outpoint);
|
|
31304
31382
|
const token = candidates.get(outpoint);
|
|
31305
|
-
return evidence != null && token != null && evidence.txs.some((tx) => this.
|
|
31383
|
+
return evidence != null && token != null && evidence.txs.some((tx) => this.consumesIdentity(tx, token));
|
|
31306
31384
|
});
|
|
31307
31385
|
if (provenContinuations.length !== 1) return void 0;
|
|
31308
31386
|
return candidates.get(provenContinuations[0]);
|
|
@@ -31313,7 +31391,7 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31313
31391
|
* hash — on-chain proof that the candidate is an update of a same-identity
|
|
31314
31392
|
* predecessor rather than an independently minted token.
|
|
31315
31393
|
*/
|
|
31316
|
-
|
|
31394
|
+
consumesIdentity(tx, token) {
|
|
31317
31395
|
const presentationHash = _bsv_sdk.Utils.toHex(token.presentationHash);
|
|
31318
31396
|
const recoveryHash = _bsv_sdk.Utils.toHex(token.recoveryHash);
|
|
31319
31397
|
for (const input of tx.inputs) {
|
|
@@ -31339,7 +31417,7 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31339
31417
|
* renditions are absent from the lookup answer. Iterative so arbitrarily
|
|
31340
31418
|
* long update chains cannot exhaust the call stack.
|
|
31341
31419
|
*/
|
|
31342
|
-
|
|
31420
|
+
collectSpends(tx, spent, visited) {
|
|
31343
31421
|
const pending = [tx];
|
|
31344
31422
|
while (pending.length > 0) {
|
|
31345
31423
|
const current = pending.pop();
|
|
@@ -31354,7 +31432,7 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31354
31432
|
}
|
|
31355
31433
|
}
|
|
31356
31434
|
}
|
|
31357
|
-
|
|
31435
|
+
emptyStats(correlationId) {
|
|
31358
31436
|
return {
|
|
31359
31437
|
hostCount: 0,
|
|
31360
31438
|
completedHosts: 0,
|
|
@@ -31367,7 +31445,7 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31367
31445
|
...correlationId !== void 0 ? { correlationId } : {}
|
|
31368
31446
|
};
|
|
31369
31447
|
}
|
|
31370
|
-
|
|
31448
|
+
diagnosticsFor(resolution) {
|
|
31371
31449
|
const progress = resolution.progress;
|
|
31372
31450
|
return {
|
|
31373
31451
|
hostCount: progress.hostCount,
|
|
@@ -31381,7 +31459,7 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31381
31459
|
...progress.correlationId !== void 0 ? { correlationId: progress.correlationId } : {}
|
|
31382
31460
|
};
|
|
31383
31461
|
}
|
|
31384
|
-
|
|
31462
|
+
lookupAttrs(diagnostics) {
|
|
31385
31463
|
return {
|
|
31386
31464
|
hostCount: diagnostics.hostCount,
|
|
31387
31465
|
completedHosts: diagnostics.completedHosts,
|
|
@@ -31393,7 +31471,7 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31393
31471
|
outputCount: diagnostics.outputCount
|
|
31394
31472
|
};
|
|
31395
31473
|
}
|
|
31396
|
-
|
|
31474
|
+
lookupDone(lookupKind, result, diagnostics, startedAt, extraAttributes = {}) {
|
|
31397
31475
|
this.telemetry.capture({
|
|
31398
31476
|
name: "wallet-toolbox.ump.lookup.completed",
|
|
31399
31477
|
component: "wallet-toolbox.ump",
|
|
@@ -31403,12 +31481,12 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31403
31481
|
lookupKind,
|
|
31404
31482
|
result,
|
|
31405
31483
|
durationMs: Date.now() - startedAt,
|
|
31406
|
-
...this.
|
|
31484
|
+
...this.lookupAttrs(diagnostics),
|
|
31407
31485
|
...extraAttributes
|
|
31408
31486
|
}
|
|
31409
31487
|
});
|
|
31410
31488
|
}
|
|
31411
|
-
|
|
31489
|
+
lookupFailed(lookupKind, reason, diagnostics, startedAt, error) {
|
|
31412
31490
|
this.telemetry.capture({
|
|
31413
31491
|
name: "wallet-toolbox.ump.lookup.indeterminate",
|
|
31414
31492
|
component: "wallet-toolbox.ump",
|
|
@@ -31418,7 +31496,7 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31418
31496
|
lookupKind,
|
|
31419
31497
|
reason,
|
|
31420
31498
|
durationMs: Date.now() - startedAt,
|
|
31421
|
-
...this.
|
|
31499
|
+
...this.lookupAttrs(diagnostics)
|
|
31422
31500
|
},
|
|
31423
31501
|
error
|
|
31424
31502
|
});
|
|
@@ -31436,27 +31514,27 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31436
31514
|
* @returns The outpoint of the newly created UMP token (e.g. "abcd1234...ef.0").
|
|
31437
31515
|
*/
|
|
31438
31516
|
async buildAndSend(wallet, adminOriginator, token, oldTokenToConsume) {
|
|
31439
|
-
const fields = this.
|
|
31517
|
+
const fields = this.tokenFields(token);
|
|
31440
31518
|
const tokenOutput = [{
|
|
31441
31519
|
lockingScript: (await new _bsv_sdk.PushDrop(wallet, adminOriginator).lock(fields, [2, "admin user management token"], "1", "self", true, true)).toHex(),
|
|
31442
31520
|
satoshis: 1,
|
|
31443
31521
|
outputDescription: "New UMP token output"
|
|
31444
31522
|
}];
|
|
31445
|
-
const { resolvedOldToken, inputToken } = await this.
|
|
31523
|
+
const { resolvedOldToken, inputToken } = await this.resolveOldInput(oldTokenToConsume);
|
|
31446
31524
|
const inputs = resolvedOldToken?.currentOutpoint ? [{
|
|
31447
31525
|
outpoint: resolvedOldToken.currentOutpoint,
|
|
31448
31526
|
unlockingScriptLength: 73,
|
|
31449
31527
|
inputDescription: "Consume old UMP token"
|
|
31450
31528
|
}] : [];
|
|
31451
|
-
const createResult = await this.
|
|
31452
|
-
if (!createResult.signableTransaction) return
|
|
31529
|
+
const createResult = await this.createAction(wallet, adminOriginator, inputs, tokenOutput, inputToken, resolvedOldToken);
|
|
31530
|
+
if (!createResult.signableTransaction) return this.broadcastFinal(createResult);
|
|
31453
31531
|
const reference = createResult.signableTransaction.reference;
|
|
31454
31532
|
const partialTx = _bsv_sdk.Transaction.fromBEEF(createResult.signableTransaction.tx);
|
|
31455
|
-
if (resolvedOldToken?.currentOutpoint) return
|
|
31456
|
-
return
|
|
31533
|
+
if (resolvedOldToken?.currentOutpoint) return this.renewToken(wallet, adminOriginator, reference, partialTx);
|
|
31534
|
+
return this.broadcastNew(wallet, adminOriginator, reference);
|
|
31457
31535
|
}
|
|
31458
31536
|
/** Assembles the ordered number[][] fields array from a UMPToken. */
|
|
31459
|
-
|
|
31537
|
+
tokenFields(token) {
|
|
31460
31538
|
const fields = [];
|
|
31461
31539
|
fields[0] = token.passwordSalt;
|
|
31462
31540
|
fields[1] = token.passwordPresentationPrimary;
|
|
@@ -31483,20 +31561,20 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31483
31561
|
return fields;
|
|
31484
31562
|
}
|
|
31485
31563
|
/** Looks up the old token on the overlay; returns undefined resolved token if not found. */
|
|
31486
|
-
async
|
|
31564
|
+
async resolveOldInput(oldTokenToConsume) {
|
|
31487
31565
|
if (!oldTokenToConsume?.currentOutpoint) return {
|
|
31488
31566
|
resolvedOldToken: void 0,
|
|
31489
31567
|
inputToken: void 0
|
|
31490
31568
|
};
|
|
31491
31569
|
const inputToken = await this.findByOutpoint(oldTokenToConsume.currentOutpoint);
|
|
31492
|
-
if (inputToken == null) throw new Error("
|
|
31570
|
+
if (inputToken == null) throw new Error("Previous UMP token unavailable; update refused.");
|
|
31493
31571
|
return {
|
|
31494
31572
|
resolvedOldToken: oldTokenToConsume,
|
|
31495
31573
|
inputToken
|
|
31496
31574
|
};
|
|
31497
31575
|
}
|
|
31498
31576
|
/** Creates the UMP action without dropping a required old-token input on failure. */
|
|
31499
|
-
async
|
|
31577
|
+
async createAction(wallet, adminOriginator, inputs, outputs, inputToken, resolvedOldToken) {
|
|
31500
31578
|
try {
|
|
31501
31579
|
return await wallet.createAction({
|
|
31502
31580
|
description: resolvedOldToken == null ? "Create new UMP token" : "Renew UMP token (consume old, create new)",
|
|
@@ -31523,43 +31601,43 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31523
31601
|
}
|
|
31524
31602
|
}
|
|
31525
31603
|
/** Handles a fully-finalized (no signable tx) createAction result — broadcasts and returns outpoint. */
|
|
31526
|
-
async
|
|
31604
|
+
async broadcastFinal(createResult) {
|
|
31527
31605
|
const finalTxid = createResult.txid || (createResult.tx != null ? _bsv_sdk.Transaction.fromAtomicBEEF(createResult.tx).id("hex") : void 0);
|
|
31528
|
-
if (!finalTxid) throw new Error("
|
|
31529
|
-
if (createResult.tx == null) throw new Error("
|
|
31606
|
+
if (!finalTxid) throw new Error("UMP transaction was not finalized.");
|
|
31607
|
+
if (createResult.tx == null) throw new Error("UMP transaction data missing.");
|
|
31530
31608
|
const broadcastTx = _bsv_sdk.Transaction.fromAtomicBEEF(createResult.tx);
|
|
31531
31609
|
const result = await this.broadcaster.broadcast(broadcastTx);
|
|
31532
|
-
this.
|
|
31610
|
+
this.assertBroadcast(result, "create-finalized");
|
|
31533
31611
|
return `${finalTxid}.0`;
|
|
31534
31612
|
}
|
|
31535
31613
|
/** Signs the old-token input and broadcasts — used during UMP token renewal. */
|
|
31536
|
-
async
|
|
31614
|
+
async renewToken(wallet, adminOriginator, reference, partialTx) {
|
|
31537
31615
|
const unlockingScript = await new _bsv_sdk.PushDrop(wallet, adminOriginator).unlock([2, "admin user management token"], "1", "self").sign(partialTx, 0);
|
|
31538
31616
|
const signResult = await wallet.signAction({
|
|
31539
31617
|
reference,
|
|
31540
31618
|
spends: { 0: { unlockingScript: unlockingScript.toHex() } }
|
|
31541
31619
|
}, adminOriginator);
|
|
31542
31620
|
const finalTxid = signResult.txid || (signResult.tx == null ? "" : _bsv_sdk.Transaction.fromAtomicBEEF(signResult.tx).id("hex"));
|
|
31543
|
-
if (!finalTxid) throw new Error("Could not finalize
|
|
31544
|
-
if (signResult.tx == null) throw new Error("
|
|
31621
|
+
if (!finalTxid) throw new Error("Could not finalize renewed UMP token.");
|
|
31622
|
+
if (signResult.tx == null) throw new Error("Renewed UMP token transaction data missing.");
|
|
31545
31623
|
const result = await this.broadcaster.broadcast(_bsv_sdk.Transaction.fromAtomicBEEF(signResult.tx));
|
|
31546
|
-
this.
|
|
31624
|
+
this.assertBroadcast(result, "renew");
|
|
31547
31625
|
return `${finalTxid}.0`;
|
|
31548
31626
|
}
|
|
31549
31627
|
/** Signs without input spending and broadcasts — used when creating a brand-new UMP token. */
|
|
31550
|
-
async
|
|
31628
|
+
async broadcastNew(wallet, adminOriginator, reference) {
|
|
31551
31629
|
const signResult = await wallet.signAction({
|
|
31552
31630
|
reference,
|
|
31553
31631
|
spends: {}
|
|
31554
31632
|
}, adminOriginator);
|
|
31555
31633
|
const finalTxid = signResult.txid || (signResult.tx == null ? "" : _bsv_sdk.Transaction.fromAtomicBEEF(signResult.tx).id("hex"));
|
|
31556
|
-
if (!finalTxid) throw new Error("
|
|
31557
|
-
if (signResult.tx == null) throw new Error("
|
|
31634
|
+
if (!finalTxid) throw new Error("Could not finalize new UMP token.");
|
|
31635
|
+
if (signResult.tx == null) throw new Error("New UMP token transaction data missing.");
|
|
31558
31636
|
const result = await this.broadcaster.broadcast(_bsv_sdk.Transaction.fromAtomicBEEF(signResult.tx));
|
|
31559
|
-
this.
|
|
31637
|
+
this.assertBroadcast(result, "create");
|
|
31560
31638
|
return `${finalTxid}.0`;
|
|
31561
31639
|
}
|
|
31562
|
-
|
|
31640
|
+
assertBroadcast(result, operation) {
|
|
31563
31641
|
const succeeded = result.status === "success";
|
|
31564
31642
|
this.telemetry.capture({
|
|
31565
31643
|
name: succeeded ? "wallet-toolbox.ump.broadcast.completed" : "wallet-toolbox.ump.broadcast.failed",
|
|
@@ -31588,12 +31666,12 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31588
31666
|
if (answer.type !== "output-list" || answer.outputs.length === 0) return [];
|
|
31589
31667
|
const tokens = [];
|
|
31590
31668
|
for (const output of answer.outputs) {
|
|
31591
|
-
const token = this.
|
|
31669
|
+
const token = this.parseOutput(output);
|
|
31592
31670
|
if (token != null) tokens.push(token);
|
|
31593
31671
|
}
|
|
31594
31672
|
return tokens;
|
|
31595
31673
|
}
|
|
31596
|
-
|
|
31674
|
+
parseOutput(output) {
|
|
31597
31675
|
try {
|
|
31598
31676
|
const tx = _bsv_sdk.Transaction.fromBEEF(output.beef);
|
|
31599
31677
|
const txOutput = tx.outputs[output.outputIndex];
|
|
@@ -31643,14 +31721,14 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31643
31721
|
correlationId
|
|
31644
31722
|
});
|
|
31645
31723
|
} catch (error) {
|
|
31646
|
-
const diagnostics = this.
|
|
31647
|
-
this.
|
|
31724
|
+
const diagnostics = this.emptyStats(correlationId);
|
|
31725
|
+
this.lookupFailed("outpoint", "lookup-unavailable", diagnostics, startedAt, error);
|
|
31648
31726
|
throw new UMPTokenLookupError("lookup-unavailable", diagnostics, { cause: error });
|
|
31649
31727
|
}
|
|
31650
31728
|
if (resolution.answer.outputs.length === 0) {
|
|
31651
31729
|
if (resolution.progress.emptyHosts === 0) {
|
|
31652
|
-
const diagnostics = this.
|
|
31653
|
-
this.
|
|
31730
|
+
const diagnostics = this.diagnosticsFor(resolution);
|
|
31731
|
+
this.lookupFailed("outpoint", "lookup-incomplete", diagnostics, startedAt);
|
|
31654
31732
|
throw new UMPTokenLookupError("lookup-incomplete", diagnostics);
|
|
31655
31733
|
}
|
|
31656
31734
|
return;
|
|
@@ -31792,20 +31870,20 @@ var CWIStyleWalletManager = class {
|
|
|
31792
31870
|
if (this._initSnapshot !== void 0) {
|
|
31793
31871
|
this.telemetry.capture({
|
|
31794
31872
|
name: "wallet-toolbox.snapshot.initialization.started",
|
|
31795
|
-
component:
|
|
31873
|
+
component: CWI_COMPONENT,
|
|
31796
31874
|
severity: "debug"
|
|
31797
31875
|
});
|
|
31798
31876
|
try {
|
|
31799
31877
|
await this.loadSnapshot(this._initSnapshot);
|
|
31800
31878
|
this.telemetry.capture({
|
|
31801
31879
|
name: "wallet-toolbox.snapshot.initialization.completed",
|
|
31802
|
-
component:
|
|
31880
|
+
component: CWI_COMPONENT,
|
|
31803
31881
|
severity: "info"
|
|
31804
31882
|
});
|
|
31805
31883
|
} catch (error) {
|
|
31806
31884
|
this.telemetry.capture({
|
|
31807
31885
|
name: "wallet-toolbox.snapshot.initialization.failed",
|
|
31808
|
-
component:
|
|
31886
|
+
component: CWI_COMPONENT,
|
|
31809
31887
|
severity: "error",
|
|
31810
31888
|
error
|
|
31811
31889
|
});
|
|
@@ -31814,9 +31892,11 @@ var CWIStyleWalletManager = class {
|
|
|
31814
31892
|
}
|
|
31815
31893
|
}
|
|
31816
31894
|
/**
|
|
31817
|
-
* Provides the presentation key.
|
|
31895
|
+
* Provides the presentation key. A WAB operator pin may be supplied by the
|
|
31896
|
+
* authentication manager; normal lookup and lineage resolution always run
|
|
31897
|
+
* before this ambiguity-only fallback.
|
|
31818
31898
|
*/
|
|
31819
|
-
async providePresentationKey(key) {
|
|
31899
|
+
async providePresentationKey(key, lookupOptions) {
|
|
31820
31900
|
if (this.authenticated) throw new Error("User is already authenticated");
|
|
31821
31901
|
if (this.authenticationMode === "recovery-key-and-password") throw new Error("Presentation key is not needed in this mode");
|
|
31822
31902
|
if (key.length !== 32 || key.some((byte) => !Number.isInteger(byte) || byte < 0 || byte > 255)) throw new TypeError("Presentation key must contain exactly 32 bytes.");
|
|
@@ -31825,17 +31905,17 @@ var CWIStyleWalletManager = class {
|
|
|
31825
31905
|
const startedAt = Date.now();
|
|
31826
31906
|
this.telemetry.capture({
|
|
31827
31907
|
name: "wallet-toolbox.authentication.account-lookup.started",
|
|
31828
|
-
component:
|
|
31908
|
+
component: CWI_COMPONENT,
|
|
31829
31909
|
severity: "debug",
|
|
31830
31910
|
attributes: { lookupKind: "presentation" }
|
|
31831
31911
|
});
|
|
31832
31912
|
let token;
|
|
31833
31913
|
try {
|
|
31834
|
-
token = await this.UMPTokenInteractor.findByPresentationKeyHash(hash);
|
|
31914
|
+
token = await this.UMPTokenInteractor.findByPresentationKeyHash(hash, lookupOptions);
|
|
31835
31915
|
} catch (error) {
|
|
31836
31916
|
this.telemetry.capture({
|
|
31837
31917
|
name: "wallet-toolbox.authentication.account-lookup.failed",
|
|
31838
|
-
component:
|
|
31918
|
+
component: CWI_COMPONENT,
|
|
31839
31919
|
severity: "warn",
|
|
31840
31920
|
attributes: {
|
|
31841
31921
|
lookupKind: "presentation",
|
|
@@ -31855,7 +31935,7 @@ var CWIStyleWalletManager = class {
|
|
|
31855
31935
|
}
|
|
31856
31936
|
this.telemetry.capture({
|
|
31857
31937
|
name: "wallet-toolbox.authentication.account-lookup.completed",
|
|
31858
|
-
component:
|
|
31938
|
+
component: CWI_COMPONENT,
|
|
31859
31939
|
severity: "info",
|
|
31860
31940
|
attributes: {
|
|
31861
31941
|
lookupKind: "presentation",
|
|
@@ -31871,11 +31951,11 @@ var CWIStyleWalletManager = class {
|
|
|
31871
31951
|
if (this.authenticated) throw new Error("User is already authenticated");
|
|
31872
31952
|
if (this.authenticationMode === "presentation-key-and-recovery-key") throw new Error("Password is not needed in this mode");
|
|
31873
31953
|
if (this.authenticationFlow === "unknown") throw new Error("Determine account status with a presentation or recovery key before providing a password.");
|
|
31874
|
-
if (this.authenticationFlow === "existing-user") await this.
|
|
31875
|
-
else await this.
|
|
31954
|
+
if (this.authenticationFlow === "existing-user") await this.unlockExisting(password);
|
|
31955
|
+
else await this.createNewUser(password);
|
|
31876
31956
|
}
|
|
31877
31957
|
/** Handles the password step for an existing user — derives keys, sets up infrastructure. */
|
|
31878
|
-
async
|
|
31958
|
+
async unlockExisting(password) {
|
|
31879
31959
|
if (this.currentUMPToken == null) throw new Error("Provide presentation or recovery key first.");
|
|
31880
31960
|
const derivedPasswordKey = await derivePasswordKey(this.currentUMPToken, _bsv_sdk.Utils.toArray(password, "utf8"));
|
|
31881
31961
|
let rootPrimaryKey;
|
|
@@ -31888,11 +31968,11 @@ var CWIStyleWalletManager = class {
|
|
|
31888
31968
|
rootPrimaryKey = new _bsv_sdk.SymmetricKey(this.XOR(this.recoveryKey, derivedPasswordKey)).decrypt(this.currentUMPToken.passwordRecoveryPrimary);
|
|
31889
31969
|
rootPrivilegedKey = new _bsv_sdk.SymmetricKey(this.XOR(rootPrimaryKey, derivedPasswordKey)).decrypt(this.currentUMPToken.passwordPrimaryPrivileged);
|
|
31890
31970
|
}
|
|
31891
|
-
await this.
|
|
31971
|
+
await this.setupRoot(rootPrimaryKey, rootPrivilegedKey);
|
|
31892
31972
|
await this.switchProfile(this.activeProfileId);
|
|
31893
31973
|
}
|
|
31894
31974
|
/** Handles the password step for a new user — generates keys, builds UMP token, publishes on-chain. */
|
|
31895
|
-
async
|
|
31975
|
+
async createNewUser(password) {
|
|
31896
31976
|
if (this.authenticationMode !== "presentation-key-and-password") throw new Error("New-user flow requires presentation key and password mode.");
|
|
31897
31977
|
if (this.presentationKey == null) throw new Error("No presentation key provided for new-user flow.");
|
|
31898
31978
|
const recoveryKey = (0, _bsv_sdk.Random)(32);
|
|
@@ -31931,14 +32011,14 @@ var CWIStyleWalletManager = class {
|
|
|
31931
32011
|
passwordKdf: this.kdfConfig
|
|
31932
32012
|
};
|
|
31933
32013
|
this.currentUMPToken = newToken;
|
|
31934
|
-
await this.
|
|
32014
|
+
await this.setupRoot(rootPrimaryKey);
|
|
31935
32015
|
await this.switchProfile(DEFAULT_PROFILE_ID);
|
|
31936
32016
|
if (this.newWalletFunder != null && this.underlying != null) try {
|
|
31937
32017
|
await this.newWalletFunder(this.presentationKey, this.underlying, this.adminOriginator);
|
|
31938
32018
|
} catch (error) {
|
|
31939
32019
|
this.telemetry.capture({
|
|
31940
32020
|
name: "wallet-toolbox.authentication.new-wallet-funding.failed",
|
|
31941
|
-
component:
|
|
32021
|
+
component: CWI_COMPONENT,
|
|
31942
32022
|
severity: "error",
|
|
31943
32023
|
error: /* @__PURE__ */ new Error("New wallet funding failed.")
|
|
31944
32024
|
});
|
|
@@ -31969,7 +32049,7 @@ var CWIStyleWalletManager = class {
|
|
|
31969
32049
|
const xorKey = this.XOR(this.presentationKey, recoveryKey);
|
|
31970
32050
|
const rootPrimaryKey = new _bsv_sdk.SymmetricKey(xorKey).decrypt(this.currentUMPToken.presentationRecoveryPrimary);
|
|
31971
32051
|
const rootPrivilegedKey = new _bsv_sdk.SymmetricKey(xorKey).decrypt(this.currentUMPToken.presentationRecoveryPrivileged);
|
|
31972
|
-
await this.
|
|
32052
|
+
await this.setupRoot(rootPrimaryKey, rootPrivilegedKey);
|
|
31973
32053
|
await this.switchProfile(this.activeProfileId);
|
|
31974
32054
|
}
|
|
31975
32055
|
}
|
|
@@ -32000,7 +32080,7 @@ var CWIStyleWalletManager = class {
|
|
|
32000
32080
|
if (snapshot.length > 16777216) throw new Error("Snapshot exceeds the maximum supported size.");
|
|
32001
32081
|
this.telemetry.capture({
|
|
32002
32082
|
name: "wallet-toolbox.snapshot.saved",
|
|
32003
|
-
component:
|
|
32083
|
+
component: CWI_COMPONENT,
|
|
32004
32084
|
severity: "info",
|
|
32005
32085
|
attributes: {
|
|
32006
32086
|
formatVersion: 2,
|
|
@@ -32038,12 +32118,12 @@ var CWIStyleWalletManager = class {
|
|
|
32038
32118
|
const tokenBytes = payloadReader.read(tokenLen);
|
|
32039
32119
|
const token = this.deserializeUMPToken(tokenBytes);
|
|
32040
32120
|
this.currentUMPToken = token;
|
|
32041
|
-
await this.
|
|
32121
|
+
await this.setupRoot(rootPrimaryKey);
|
|
32042
32122
|
await this.switchProfile(activeProfileId);
|
|
32043
32123
|
this.authenticationFlow = "existing-user";
|
|
32044
32124
|
this.telemetry.capture({
|
|
32045
32125
|
name: "wallet-toolbox.snapshot.loaded",
|
|
32046
|
-
component:
|
|
32126
|
+
component: CWI_COMPONENT,
|
|
32047
32127
|
severity: "info",
|
|
32048
32128
|
attributes: {
|
|
32049
32129
|
formatVersion: version,
|
|
@@ -32054,7 +32134,7 @@ var CWIStyleWalletManager = class {
|
|
|
32054
32134
|
this.destroy();
|
|
32055
32135
|
this.telemetry.capture({
|
|
32056
32136
|
name: "wallet-toolbox.snapshot.load-failed",
|
|
32057
|
-
component:
|
|
32137
|
+
component: CWI_COMPONENT,
|
|
32058
32138
|
severity: "error",
|
|
32059
32139
|
error
|
|
32060
32140
|
});
|
|
@@ -32071,7 +32151,7 @@ var CWIStyleWalletManager = class {
|
|
|
32071
32151
|
if (refreshed == null) return false;
|
|
32072
32152
|
if (refreshed.currentOutpoint && currentToken.currentOutpoint && refreshed.currentOutpoint === currentToken.currentOutpoint) return false;
|
|
32073
32153
|
this.currentUMPToken = refreshed;
|
|
32074
|
-
await this.
|
|
32154
|
+
await this.setupRoot(this.rootPrimaryKey);
|
|
32075
32155
|
this.saveSnapshot();
|
|
32076
32156
|
return true;
|
|
32077
32157
|
}
|
|
@@ -32130,7 +32210,7 @@ var CWIStyleWalletManager = class {
|
|
|
32130
32210
|
createdAt: Math.floor(Date.now() / 1e3)
|
|
32131
32211
|
};
|
|
32132
32212
|
this.profiles.push(newProfile);
|
|
32133
|
-
await this.
|
|
32213
|
+
await this.updateFactors(this.currentUMPToken.passwordSalt, await this.getFactor("passwordKey"), await this.getFactor("presentationKey"), await this.getFactor("recoveryKey"), this.rootPrimaryKey, await this.getFactor("privilegedKey"), this.profiles);
|
|
32134
32214
|
return newProfile.id;
|
|
32135
32215
|
}
|
|
32136
32216
|
/**
|
|
@@ -32147,7 +32227,7 @@ var CWIStyleWalletManager = class {
|
|
|
32147
32227
|
if (profileIndex === -1) throw new Error("Profile not found.");
|
|
32148
32228
|
this.profiles.splice(profileIndex, 1);
|
|
32149
32229
|
if (this.activeProfileId.every((x, i) => x === profileId[i])) await this.switchProfile(DEFAULT_PROFILE_ID);
|
|
32150
|
-
await this.
|
|
32230
|
+
await this.updateFactors(this.currentUMPToken.passwordSalt, await this.getFactor("passwordKey"), await this.getFactor("presentationKey"), await this.getFactor("recoveryKey"), this.rootPrimaryKey, await this.getFactor("privilegedKey"), this.profiles);
|
|
32151
32231
|
}
|
|
32152
32232
|
/**
|
|
32153
32233
|
* Switches the active profile. This re-derives keys and rebuilds the underlying wallet.
|
|
@@ -32188,14 +32268,14 @@ var CWIStyleWalletManager = class {
|
|
|
32188
32268
|
const recoveryKey = await this.getFactor("recoveryKey");
|
|
32189
32269
|
const presentationKey = await this.getFactor("presentationKey");
|
|
32190
32270
|
const rootPrivilegedKey = await this.getFactor("privilegedKey");
|
|
32191
|
-
await this.
|
|
32271
|
+
await this.updateFactors(passwordSalt, newPasswordKey, presentationKey, recoveryKey, this.rootPrimaryKey, rootPrivilegedKey, this.profiles);
|
|
32192
32272
|
}
|
|
32193
32273
|
/**
|
|
32194
32274
|
* Retrieves the current recovery key. Requires privileged access.
|
|
32195
32275
|
*/
|
|
32196
32276
|
async getRecoveryKey() {
|
|
32197
32277
|
if (!this.authenticated || this.currentUMPToken == null || this.rootPrivilegedKeyManager == null) throw new Error("Not authenticated or missing required data.");
|
|
32198
|
-
return
|
|
32278
|
+
return this.getFactor("recoveryKey");
|
|
32199
32279
|
}
|
|
32200
32280
|
/**
|
|
32201
32281
|
* Changes the user's recovery key. Prompts user to save the new key.
|
|
@@ -32207,7 +32287,7 @@ var CWIStyleWalletManager = class {
|
|
|
32207
32287
|
const rootPrivilegedKey = await this.getFactor("privilegedKey");
|
|
32208
32288
|
const newRecoveryKey = (0, _bsv_sdk.Random)(32);
|
|
32209
32289
|
await this.recoveryKeySaver(newRecoveryKey);
|
|
32210
|
-
await this.
|
|
32290
|
+
await this.updateFactors(this.currentUMPToken.passwordSalt, passwordKey, presentationKey, newRecoveryKey, this.rootPrimaryKey, rootPrivilegedKey, this.profiles);
|
|
32211
32291
|
}
|
|
32212
32292
|
/**
|
|
32213
32293
|
* Changes the user's presentation key.
|
|
@@ -32218,7 +32298,7 @@ var CWIStyleWalletManager = class {
|
|
|
32218
32298
|
const recoveryKey = await this.getFactor("recoveryKey");
|
|
32219
32299
|
const passwordKey = await this.getFactor("passwordKey");
|
|
32220
32300
|
const rootPrivilegedKey = await this.getFactor("privilegedKey");
|
|
32221
|
-
await this.
|
|
32301
|
+
await this.updateFactors(this.currentUMPToken.passwordSalt, passwordKey, newPresentationKey, recoveryKey, this.rootPrimaryKey, rootPrivilegedKey, this.profiles);
|
|
32222
32302
|
if (this.presentationKey != null) this.presentationKey = newPresentationKey;
|
|
32223
32303
|
}
|
|
32224
32304
|
/**
|
|
@@ -32264,7 +32344,7 @@ var CWIStyleWalletManager = class {
|
|
|
32264
32344
|
} catch (error) {
|
|
32265
32345
|
this.telemetry.capture({
|
|
32266
32346
|
name: "wallet-toolbox.authentication.factor-decryption.failed",
|
|
32267
|
-
component:
|
|
32347
|
+
component: CWI_COMPONENT,
|
|
32268
32348
|
severity: "error",
|
|
32269
32349
|
attributes: { factor: factorName },
|
|
32270
32350
|
error
|
|
@@ -32277,7 +32357,7 @@ var CWIStyleWalletManager = class {
|
|
|
32277
32357
|
* Recomputes UMP token fields with updated factors and profiles, then publishes the update.
|
|
32278
32358
|
* This operation requires the *root* privileged key and the *default* profile wallet.
|
|
32279
32359
|
*/
|
|
32280
|
-
async
|
|
32360
|
+
async updateFactors(passwordSalt, passwordKey, presentationKey, recoveryKey, rootPrimaryKey, rootPrivilegedKey, profiles) {
|
|
32281
32361
|
if (!this.authenticated || this.rootPrimaryKey == null || this.currentUMPToken == null) throw new Error("Wallet is not properly authenticated or missing data for update.");
|
|
32282
32362
|
const oldTokenToConsume = { ...this.currentUMPToken };
|
|
32283
32363
|
if (!oldTokenToConsume.currentOutpoint) throw new Error("Cannot update UMP token: Old token has no outpoint.");
|
|
@@ -32328,7 +32408,7 @@ var CWIStyleWalletManager = class {
|
|
|
32328
32408
|
if (!currentActiveId.every((x) => x === 0)) {
|
|
32329
32409
|
this.telemetry.capture({
|
|
32330
32410
|
name: "wallet-toolbox.ump.profile-switch.started",
|
|
32331
|
-
component:
|
|
32411
|
+
component: CWI_COMPONENT,
|
|
32332
32412
|
severity: "debug",
|
|
32333
32413
|
attributes: { reason: "token-update" }
|
|
32334
32414
|
});
|
|
@@ -32344,7 +32424,7 @@ var CWIStyleWalletManager = class {
|
|
|
32344
32424
|
await this.switchProfile(currentActiveId);
|
|
32345
32425
|
this.telemetry.capture({
|
|
32346
32426
|
name: "wallet-toolbox.ump.profile-switch.completed",
|
|
32347
|
-
component:
|
|
32427
|
+
component: CWI_COMPONENT,
|
|
32348
32428
|
severity: "debug",
|
|
32349
32429
|
attributes: { reason: "token-update" }
|
|
32350
32430
|
});
|
|
@@ -32471,9 +32551,9 @@ var CWIStyleWalletManager = class {
|
|
|
32471
32551
|
* @param rootPrimaryKey The user's root primary key (32 bytes).
|
|
32472
32552
|
* @param ephemeralRootPrivilegedKey Optional root privileged key (e.g., during recovery flows).
|
|
32473
32553
|
*/
|
|
32474
|
-
async
|
|
32554
|
+
async setupRoot(rootKey, ephemeralRootPrivilegedKey) {
|
|
32475
32555
|
if (this.currentUMPToken == null) throw new Error("A UMP token must exist before setting up root infrastructure!");
|
|
32476
|
-
this.rootPrimaryKey =
|
|
32556
|
+
this.rootPrimaryKey = rootKey;
|
|
32477
32557
|
let oneTimePrivilegedKey = ephemeralRootPrivilegedKey == null ? void 0 : new _bsv_sdk.PrivateKey(ephemeralRootPrivilegedKey);
|
|
32478
32558
|
this.rootPrivilegedKeyManager = new PrivilegedKeyManager(async (reason) => {
|
|
32479
32559
|
if (oneTimePrivilegedKey != null) {
|
|
@@ -32494,7 +32574,7 @@ var CWIStyleWalletManager = class {
|
|
|
32494
32574
|
});
|
|
32495
32575
|
this.profiles = [];
|
|
32496
32576
|
if (this.currentUMPToken.profilesEncrypted != null && this.currentUMPToken.profilesEncrypted.length > 0) try {
|
|
32497
|
-
const decryptedProfileBytes = new _bsv_sdk.SymmetricKey(
|
|
32577
|
+
const decryptedProfileBytes = new _bsv_sdk.SymmetricKey(rootKey).decrypt(this.currentUMPToken.profilesEncrypted);
|
|
32498
32578
|
const profilesJson = _bsv_sdk.Utils.toUTF8(decryptedProfileBytes);
|
|
32499
32579
|
const profiles = JSON.parse(profilesJson);
|
|
32500
32580
|
if (!Array.isArray(profiles) || profiles.length > 1e3 || !profiles.every(isValidProfile)) throw new Error("Decrypted profile data is invalid or exceeds supported bounds.");
|
|
@@ -32503,7 +32583,7 @@ var CWIStyleWalletManager = class {
|
|
|
32503
32583
|
this.profiles = [];
|
|
32504
32584
|
this.telemetry.capture({
|
|
32505
32585
|
name: "wallet-toolbox.profile.load-failed",
|
|
32506
|
-
component:
|
|
32586
|
+
component: CWI_COMPONENT,
|
|
32507
32587
|
severity: "error",
|
|
32508
32588
|
error
|
|
32509
32589
|
});
|
|
@@ -32512,98 +32592,98 @@ var CWIStyleWalletManager = class {
|
|
|
32512
32592
|
}
|
|
32513
32593
|
this.authenticated = true;
|
|
32514
32594
|
}
|
|
32515
|
-
|
|
32595
|
+
assertReady(originator) {
|
|
32516
32596
|
if (!this.authenticated) throw new Error("User is not authenticated.");
|
|
32517
32597
|
if (this.underlying == null) throw new Error("Underlying wallet for the active profile is not initialized.");
|
|
32518
32598
|
if (originator === this.adminOriginator) throw new Error("External applications are not allowed to use the admin originator.");
|
|
32519
32599
|
}
|
|
32520
32600
|
async getPublicKey(args, originator) {
|
|
32521
|
-
this.
|
|
32522
|
-
return
|
|
32601
|
+
this.assertReady(originator);
|
|
32602
|
+
return this.underlying.getPublicKey(args, originator);
|
|
32523
32603
|
}
|
|
32524
32604
|
async revealCounterpartyKeyLinkage(args, originator) {
|
|
32525
|
-
this.
|
|
32526
|
-
return
|
|
32605
|
+
this.assertReady(originator);
|
|
32606
|
+
return this.underlying.revealCounterpartyKeyLinkage(args, originator);
|
|
32527
32607
|
}
|
|
32528
32608
|
async revealSpecificKeyLinkage(args, originator) {
|
|
32529
|
-
this.
|
|
32530
|
-
return
|
|
32609
|
+
this.assertReady(originator);
|
|
32610
|
+
return this.underlying.revealSpecificKeyLinkage(args, originator);
|
|
32531
32611
|
}
|
|
32532
32612
|
async encrypt(args, originator) {
|
|
32533
|
-
this.
|
|
32534
|
-
return
|
|
32613
|
+
this.assertReady(originator);
|
|
32614
|
+
return this.underlying.encrypt(args, originator);
|
|
32535
32615
|
}
|
|
32536
32616
|
async decrypt(args, originator) {
|
|
32537
|
-
this.
|
|
32538
|
-
return
|
|
32617
|
+
this.assertReady(originator);
|
|
32618
|
+
return this.underlying.decrypt(args, originator);
|
|
32539
32619
|
}
|
|
32540
32620
|
async createHmac(args, originator) {
|
|
32541
|
-
this.
|
|
32542
|
-
return
|
|
32621
|
+
this.assertReady(originator);
|
|
32622
|
+
return this.underlying.createHmac(args, originator);
|
|
32543
32623
|
}
|
|
32544
32624
|
async verifyHmac(args, originator) {
|
|
32545
|
-
this.
|
|
32546
|
-
return
|
|
32625
|
+
this.assertReady(originator);
|
|
32626
|
+
return this.underlying.verifyHmac(args, originator);
|
|
32547
32627
|
}
|
|
32548
32628
|
async createSignature(args, originator) {
|
|
32549
|
-
this.
|
|
32550
|
-
return
|
|
32629
|
+
this.assertReady(originator);
|
|
32630
|
+
return this.underlying.createSignature(args, originator);
|
|
32551
32631
|
}
|
|
32552
32632
|
async verifySignature(args, originator) {
|
|
32553
|
-
this.
|
|
32554
|
-
return
|
|
32633
|
+
this.assertReady(originator);
|
|
32634
|
+
return this.underlying.verifySignature(args, originator);
|
|
32555
32635
|
}
|
|
32556
32636
|
async createAction(args, originator) {
|
|
32557
|
-
this.
|
|
32558
|
-
return
|
|
32637
|
+
this.assertReady(originator);
|
|
32638
|
+
return this.underlying.createAction(args, originator);
|
|
32559
32639
|
}
|
|
32560
32640
|
async signAction(args, originator) {
|
|
32561
|
-
this.
|
|
32562
|
-
return
|
|
32641
|
+
this.assertReady(originator);
|
|
32642
|
+
return this.underlying.signAction(args, originator);
|
|
32563
32643
|
}
|
|
32564
32644
|
async abortAction(args, originator) {
|
|
32565
|
-
this.
|
|
32566
|
-
return
|
|
32645
|
+
this.assertReady(originator);
|
|
32646
|
+
return this.underlying.abortAction(args, originator);
|
|
32567
32647
|
}
|
|
32568
32648
|
async listActions(args, originator) {
|
|
32569
|
-
this.
|
|
32570
|
-
return
|
|
32649
|
+
this.assertReady(originator);
|
|
32650
|
+
return this.underlying.listActions(args, originator);
|
|
32571
32651
|
}
|
|
32572
32652
|
async internalizeAction(args, originator) {
|
|
32573
|
-
this.
|
|
32574
|
-
return
|
|
32653
|
+
this.assertReady(originator);
|
|
32654
|
+
return this.underlying.internalizeAction(args, originator);
|
|
32575
32655
|
}
|
|
32576
32656
|
async listOutputs(args, originator) {
|
|
32577
|
-
this.
|
|
32578
|
-
return
|
|
32657
|
+
this.assertReady(originator);
|
|
32658
|
+
return this.underlying.listOutputs(args, originator);
|
|
32579
32659
|
}
|
|
32580
32660
|
async relinquishOutput(args, originator) {
|
|
32581
|
-
this.
|
|
32582
|
-
return
|
|
32661
|
+
this.assertReady(originator);
|
|
32662
|
+
return this.underlying.relinquishOutput(args, originator);
|
|
32583
32663
|
}
|
|
32584
32664
|
async acquireCertificate(args, originator) {
|
|
32585
|
-
this.
|
|
32586
|
-
return
|
|
32665
|
+
this.assertReady(originator);
|
|
32666
|
+
return this.underlying.acquireCertificate(args, originator);
|
|
32587
32667
|
}
|
|
32588
32668
|
async listCertificates(args, originator) {
|
|
32589
|
-
this.
|
|
32590
|
-
return
|
|
32669
|
+
this.assertReady(originator);
|
|
32670
|
+
return this.underlying.listCertificates(args, originator);
|
|
32591
32671
|
}
|
|
32592
32672
|
async proveCertificate(args, originator) {
|
|
32593
|
-
this.
|
|
32594
|
-
return
|
|
32673
|
+
this.assertReady(originator);
|
|
32674
|
+
return this.underlying.proveCertificate(args, originator);
|
|
32595
32675
|
}
|
|
32596
32676
|
async relinquishCertificate(args, originator) {
|
|
32597
|
-
this.
|
|
32598
|
-
return
|
|
32677
|
+
this.assertReady(originator);
|
|
32678
|
+
return this.underlying.relinquishCertificate(args, originator);
|
|
32599
32679
|
}
|
|
32600
32680
|
async discoverByIdentityKey(args, originator) {
|
|
32601
|
-
this.
|
|
32602
|
-
return
|
|
32681
|
+
this.assertReady(originator);
|
|
32682
|
+
return this.underlying.discoverByIdentityKey(args, originator);
|
|
32603
32683
|
}
|
|
32604
32684
|
async discoverByAttributes(args, originator) {
|
|
32605
|
-
this.
|
|
32606
|
-
return
|
|
32685
|
+
this.assertReady(originator);
|
|
32686
|
+
return this.underlying.discoverByAttributes(args, originator);
|
|
32607
32687
|
}
|
|
32608
32688
|
async isAuthenticated(_, originator) {
|
|
32609
32689
|
if (!this.authenticated) throw new Error("User is not authenticated.");
|
|
@@ -32613,23 +32693,23 @@ var CWIStyleWalletManager = class {
|
|
|
32613
32693
|
async waitForAuthentication(_, originator) {
|
|
32614
32694
|
if (originator === this.adminOriginator) throw new Error("External applications are not allowed to use the admin originator.");
|
|
32615
32695
|
while (!this.authenticated || this.underlying == null) await new Promise((resolve) => setTimeout(resolve, 100));
|
|
32616
|
-
return
|
|
32696
|
+
return this.underlying.waitForAuthentication({}, originator);
|
|
32617
32697
|
}
|
|
32618
32698
|
async getHeight(_, originator) {
|
|
32619
|
-
this.
|
|
32620
|
-
return
|
|
32699
|
+
this.assertReady(originator);
|
|
32700
|
+
return this.underlying.getHeight({}, originator);
|
|
32621
32701
|
}
|
|
32622
32702
|
async getHeaderForHeight(args, originator) {
|
|
32623
|
-
this.
|
|
32624
|
-
return
|
|
32703
|
+
this.assertReady(originator);
|
|
32704
|
+
return this.underlying.getHeaderForHeight(args, originator);
|
|
32625
32705
|
}
|
|
32626
32706
|
async getNetwork(_, originator) {
|
|
32627
|
-
this.
|
|
32628
|
-
return
|
|
32707
|
+
this.assertReady(originator);
|
|
32708
|
+
return this.underlying.getNetwork({}, originator);
|
|
32629
32709
|
}
|
|
32630
32710
|
async getVersion(_, originator) {
|
|
32631
|
-
this.
|
|
32632
|
-
return
|
|
32711
|
+
this.assertReady(originator);
|
|
32712
|
+
return this.underlying.getVersion({}, originator);
|
|
32633
32713
|
}
|
|
32634
32714
|
};
|
|
32635
32715
|
//#endregion
|
|
@@ -32973,6 +33053,8 @@ const DEFAULT_MAX_RESPONSE_BYTES = 1024 * 1024;
|
|
|
32973
33053
|
const MAX_CONFIGURED_TIMEOUT_MS = 12e4;
|
|
32974
33054
|
const MAX_CONFIGURED_REQUEST_BYTES = 10 * 1024 * 1024;
|
|
32975
33055
|
const MAX_CONFIGURED_RESPONSE_BYTES = 10 * 1024 * 1024;
|
|
33056
|
+
const WAB_COMPONENT = "wallet-toolbox.wab-transport";
|
|
33057
|
+
const WAB_REQUEST_EVENT = "wallet-toolbox.wab.request.";
|
|
32976
33058
|
const defaultFetch = typeof globalThis !== "undefined" && typeof globalThis.fetch === "function" ? globalThis.fetch.bind(globalThis) : void 0;
|
|
32977
33059
|
/**
|
|
32978
33060
|
* A privacy-safe WAB transport failure. Response bodies and request payloads
|
|
@@ -33011,8 +33093,8 @@ function normalizeServerUrl(serverUrl) {
|
|
|
33011
33093
|
} catch {
|
|
33012
33094
|
throw new WABClientError("WAB_INVALID_CONFIGURATION", "WAB server URL must be an absolute URL.", false);
|
|
33013
33095
|
}
|
|
33014
|
-
if (parsed.username !== "" || parsed.password !== "" || parsed.search !== "" || parsed.hash !== "") throw new WABClientError("WAB_INVALID_CONFIGURATION", "WAB
|
|
33015
|
-
if (parsed.protocol !== "https:" && !(parsed.protocol === "http:" && isLocalHostname(parsed.hostname))) throw new WABClientError("WAB_INVALID_CONFIGURATION", "WAB
|
|
33096
|
+
if (parsed.username !== "" || parsed.password !== "" || parsed.search !== "" || parsed.hash !== "") throw new WABClientError("WAB_INVALID_CONFIGURATION", "WAB URL cannot include credentials, query, or fragment.", false);
|
|
33097
|
+
if (parsed.protocol !== "https:" && !(parsed.protocol === "http:" && isLocalHostname(parsed.hostname))) throw new WABClientError("WAB_INVALID_CONFIGURATION", "WAB URL requires HTTPS except on localhost.", false);
|
|
33016
33098
|
let pathname = parsed.pathname;
|
|
33017
33099
|
while (pathname.endsWith("/")) pathname = pathname.slice(0, -1);
|
|
33018
33100
|
return {
|
|
@@ -33022,7 +33104,7 @@ function normalizeServerUrl(serverUrl) {
|
|
|
33022
33104
|
}
|
|
33023
33105
|
function normalizePositiveInteger(value, fallback, maximum, name) {
|
|
33024
33106
|
const resolved = value ?? fallback;
|
|
33025
|
-
if (!Number.isInteger(resolved) || resolved <= 0 || resolved > maximum) throw new WABClientError("WAB_INVALID_CONFIGURATION", `${name} must be
|
|
33107
|
+
if (!Number.isInteger(resolved) || resolved <= 0 || resolved > maximum) throw new WABClientError("WAB_INVALID_CONFIGURATION", `${name} must be an integer from 1 to ${maximum}.`, false);
|
|
33026
33108
|
return resolved;
|
|
33027
33109
|
}
|
|
33028
33110
|
function assertSafePath(path) {
|
|
@@ -33047,20 +33129,20 @@ var WABTransport = class {
|
|
|
33047
33129
|
serverUrl;
|
|
33048
33130
|
serverOrigin;
|
|
33049
33131
|
telemetry;
|
|
33050
|
-
|
|
33051
|
-
|
|
33052
|
-
|
|
33053
|
-
|
|
33132
|
+
fetcher;
|
|
33133
|
+
timeout;
|
|
33134
|
+
requestLimit;
|
|
33135
|
+
responseLimit;
|
|
33054
33136
|
constructor(serverUrl, options = {}) {
|
|
33055
33137
|
const normalized = normalizeServerUrl(serverUrl);
|
|
33056
33138
|
this.serverUrl = normalized.baseUrl;
|
|
33057
33139
|
this.serverOrigin = normalized.origin;
|
|
33058
|
-
const
|
|
33059
|
-
if (typeof
|
|
33060
|
-
this.
|
|
33061
|
-
this.
|
|
33062
|
-
this.
|
|
33063
|
-
this.
|
|
33140
|
+
const fetcher = options.fetch ?? defaultFetch;
|
|
33141
|
+
if (typeof fetcher !== "function") throw new WABClientError("WAB_INVALID_CONFIGURATION", "WABClient requires a fetch implementation.", false);
|
|
33142
|
+
this.fetcher = fetcher;
|
|
33143
|
+
this.timeout = normalizePositiveInteger(options.timeoutMs, DEFAULT_TIMEOUT_MS, MAX_CONFIGURED_TIMEOUT_MS, "timeoutMs");
|
|
33144
|
+
this.requestLimit = normalizePositiveInteger(options.maxRequestBytes, DEFAULT_MAX_REQUEST_BYTES, MAX_CONFIGURED_REQUEST_BYTES, "maxRequestBytes");
|
|
33145
|
+
this.responseLimit = normalizePositiveInteger(options.maxResponseBytes, DEFAULT_MAX_RESPONSE_BYTES, MAX_CONFIGURED_RESPONSE_BYTES, "maxResponseBytes");
|
|
33064
33146
|
this.telemetry = new _bsv_sdk.Telemetry(options.telemetry);
|
|
33065
33147
|
}
|
|
33066
33148
|
createCorrelationId() {
|
|
@@ -33068,38 +33150,9 @@ var WABTransport = class {
|
|
|
33068
33150
|
return isSafeCorrelationId(correlationId) ? correlationId : new _bsv_sdk.Telemetry().createCorrelationId();
|
|
33069
33151
|
}
|
|
33070
33152
|
async request(path, options) {
|
|
33071
|
-
const metadata = this.createRequestMetadata(path, options);
|
|
33072
|
-
this.captureRequestStarted(metadata);
|
|
33073
|
-
const body = this.encodeRequestBody(options, metadata);
|
|
33074
|
-
const timeout = this.startRequestTimeout(metadata.errorContext);
|
|
33075
|
-
const response = await this.fetchResponse(metadata, body, timeout);
|
|
33076
|
-
const responseContext = this.createResponseContext(response, metadata);
|
|
33077
|
-
this.assertSuccessfulResponse(response, responseContext, metadata, timeout);
|
|
33078
|
-
const responseText = await this.readResponseText(response, responseContext, metadata, timeout);
|
|
33079
|
-
const parsed = this.parseResponseObject(responseText, response, responseContext, metadata);
|
|
33080
|
-
this.telemetry.capture({
|
|
33081
|
-
name: "wallet-toolbox.wab.request.completed",
|
|
33082
|
-
component: "wallet-toolbox.wab-transport",
|
|
33083
|
-
severity: "info",
|
|
33084
|
-
correlationId: metadata.correlationId,
|
|
33085
|
-
attributes: {
|
|
33086
|
-
operation: metadata.operation,
|
|
33087
|
-
method: metadata.method,
|
|
33088
|
-
route: metadata.path,
|
|
33089
|
-
serverOrigin: this.serverOrigin,
|
|
33090
|
-
status: response.status,
|
|
33091
|
-
endpointMarkerPresent: responseContext.endpointMarkerPresent,
|
|
33092
|
-
responseCorrelationMatched: responseContext.responseCorrelationMatched,
|
|
33093
|
-
responseBytes: new TextEncoder().encode(responseText).byteLength,
|
|
33094
|
-
durationMs: Date.now() - metadata.startedAt
|
|
33095
|
-
}
|
|
33096
|
-
});
|
|
33097
|
-
return parsed;
|
|
33098
|
-
}
|
|
33099
|
-
createRequestMetadata(path, options) {
|
|
33100
33153
|
assertSafePath(path);
|
|
33101
33154
|
const correlationId = options.correlationId != null && isSafeCorrelationId(options.correlationId) ? options.correlationId : this.createCorrelationId();
|
|
33102
|
-
|
|
33155
|
+
const metadata = {
|
|
33103
33156
|
method: options.method ?? "POST",
|
|
33104
33157
|
path,
|
|
33105
33158
|
operation: options.operation,
|
|
@@ -33111,46 +33164,73 @@ var WABTransport = class {
|
|
|
33111
33164
|
route: path
|
|
33112
33165
|
}
|
|
33113
33166
|
};
|
|
33114
|
-
}
|
|
33115
|
-
captureRequestStarted(metadata) {
|
|
33116
33167
|
this.telemetry.capture({
|
|
33117
|
-
name:
|
|
33118
|
-
component:
|
|
33168
|
+
name: `${WAB_REQUEST_EVENT}started`,
|
|
33169
|
+
component: WAB_COMPONENT,
|
|
33119
33170
|
severity: "debug",
|
|
33171
|
+
correlationId,
|
|
33172
|
+
attributes: {
|
|
33173
|
+
operation: metadata.operation,
|
|
33174
|
+
method: metadata.method,
|
|
33175
|
+
route: path,
|
|
33176
|
+
serverOrigin: this.serverOrigin
|
|
33177
|
+
}
|
|
33178
|
+
});
|
|
33179
|
+
const body = this.bodyFor(options, metadata);
|
|
33180
|
+
const timeout = this.startTimer(metadata.errorContext);
|
|
33181
|
+
const response = await this.fetch(metadata, body, timeout);
|
|
33182
|
+
const responseContext = {
|
|
33183
|
+
...metadata.errorContext,
|
|
33184
|
+
endpointMarkerPresent: isWabResponse(response),
|
|
33185
|
+
responseCorrelationMatched: response.headers.get("X-Correlation-ID") === correlationId
|
|
33186
|
+
};
|
|
33187
|
+
this.checkResponse(response, responseContext, metadata, timeout);
|
|
33188
|
+
const responseText = await this.readText(response, responseContext, metadata, timeout);
|
|
33189
|
+
const parsed = this.parse(responseText, response, responseContext, metadata);
|
|
33190
|
+
this.telemetry.capture({
|
|
33191
|
+
name: `${WAB_REQUEST_EVENT}completed`,
|
|
33192
|
+
component: WAB_COMPONENT,
|
|
33193
|
+
severity: "info",
|
|
33120
33194
|
correlationId: metadata.correlationId,
|
|
33121
33195
|
attributes: {
|
|
33122
33196
|
operation: metadata.operation,
|
|
33123
33197
|
method: metadata.method,
|
|
33124
33198
|
route: metadata.path,
|
|
33125
|
-
serverOrigin: this.serverOrigin
|
|
33199
|
+
serverOrigin: this.serverOrigin,
|
|
33200
|
+
status: response.status,
|
|
33201
|
+
endpointMarkerPresent: responseContext.endpointMarkerPresent,
|
|
33202
|
+
responseCorrelationMatched: responseContext.responseCorrelationMatched,
|
|
33203
|
+
responseBytes: new TextEncoder().encode(responseText).byteLength,
|
|
33204
|
+
durationMs: Date.now() - metadata.startedAt
|
|
33126
33205
|
}
|
|
33127
33206
|
});
|
|
33207
|
+
return parsed;
|
|
33128
33208
|
}
|
|
33129
|
-
|
|
33209
|
+
bodyFor(options, metadata) {
|
|
33130
33210
|
let body;
|
|
33131
33211
|
try {
|
|
33132
33212
|
body = options.body === void 0 ? void 0 : JSON.stringify(options.body);
|
|
33133
33213
|
} catch (cause) {
|
|
33134
|
-
const error = new WABClientError("WAB_INVALID_REQUEST", "WAB request
|
|
33214
|
+
const error = new WABClientError("WAB_INVALID_REQUEST", "WAB request encoding failed.", false, void 0, {
|
|
33135
33215
|
...metadata.errorContext,
|
|
33136
33216
|
cause
|
|
33137
33217
|
});
|
|
33138
|
-
this.
|
|
33218
|
+
this.report(metadata, error);
|
|
33139
33219
|
throw error;
|
|
33140
33220
|
}
|
|
33141
33221
|
if (options.body !== void 0 && body === void 0) {
|
|
33142
|
-
const error = new WABClientError("WAB_INVALID_REQUEST", "WAB request
|
|
33143
|
-
this.
|
|
33222
|
+
const error = new WABClientError("WAB_INVALID_REQUEST", "WAB request is not JSON-serializable.", false, void 0, metadata.errorContext);
|
|
33223
|
+
this.report(metadata, error);
|
|
33144
33224
|
throw error;
|
|
33145
33225
|
}
|
|
33146
|
-
if (body != null && new TextEncoder().encode(body).byteLength > this.
|
|
33147
|
-
const error = new WABClientError("WAB_REQUEST_TOO_LARGE", "WAB request
|
|
33148
|
-
this.
|
|
33226
|
+
if (body != null && new TextEncoder().encode(body).byteLength > this.requestLimit) {
|
|
33227
|
+
const error = new WABClientError("WAB_REQUEST_TOO_LARGE", "WAB request exceeds its size limit.", false, void 0, metadata.errorContext);
|
|
33228
|
+
this.report(metadata, error);
|
|
33149
33229
|
throw error;
|
|
33150
33230
|
}
|
|
33151
33231
|
return body;
|
|
33152
33232
|
}
|
|
33153
|
-
|
|
33233
|
+
startTimer(errorContext) {
|
|
33154
33234
|
const timeout = {
|
|
33155
33235
|
controller: new AbortController(),
|
|
33156
33236
|
timedOut: false
|
|
@@ -33160,12 +33240,12 @@ var WABTransport = class {
|
|
|
33160
33240
|
timeout.timedOut = true;
|
|
33161
33241
|
timeout.controller.abort();
|
|
33162
33242
|
reject(new WABClientError("WAB_TIMEOUT", "WAB request timed out.", true, void 0, errorContext));
|
|
33163
|
-
}, this.
|
|
33243
|
+
}, this.timeout);
|
|
33164
33244
|
});
|
|
33165
33245
|
return timeout;
|
|
33166
33246
|
}
|
|
33167
|
-
async
|
|
33168
|
-
const requestPromise = Promise.resolve().then(() => this.
|
|
33247
|
+
async fetch(metadata, body, timeout) {
|
|
33248
|
+
const requestPromise = Promise.resolve().then(() => this.fetcher(`${this.serverUrl}${metadata.path}`, {
|
|
33169
33249
|
method: metadata.method,
|
|
33170
33250
|
headers: {
|
|
33171
33251
|
Accept: "application/json",
|
|
@@ -33189,33 +33269,26 @@ var WABTransport = class {
|
|
|
33189
33269
|
...metadata.errorContext,
|
|
33190
33270
|
cause
|
|
33191
33271
|
});
|
|
33192
|
-
else error = new WABClientError("WAB_NETWORK_ERROR", "WAB request failed before
|
|
33272
|
+
else error = new WABClientError("WAB_NETWORK_ERROR", "WAB request failed before response.", true, void 0, {
|
|
33193
33273
|
...metadata.errorContext,
|
|
33194
33274
|
cause
|
|
33195
33275
|
});
|
|
33196
|
-
this.
|
|
33276
|
+
this.report(metadata, error);
|
|
33197
33277
|
throw error;
|
|
33198
33278
|
}
|
|
33199
33279
|
}
|
|
33200
|
-
|
|
33201
|
-
return {
|
|
33202
|
-
...metadata.errorContext,
|
|
33203
|
-
endpointMarkerPresent: isWabResponse(response),
|
|
33204
|
-
responseCorrelationMatched: response.headers.get("X-Correlation-ID") === metadata.correlationId
|
|
33205
|
-
};
|
|
33206
|
-
}
|
|
33207
|
-
assertSuccessfulResponse(response, responseContext, metadata, timeout) {
|
|
33280
|
+
checkResponse(response, responseContext, metadata, timeout) {
|
|
33208
33281
|
if (response.ok) return;
|
|
33209
33282
|
if (timeout.timer !== void 0) clearTimeout(timeout.timer);
|
|
33210
33283
|
const endpointMismatch = response.status === 404 && responseContext.endpointMarkerPresent !== true;
|
|
33211
|
-
const error = new WABClientError(endpointMismatch ? "WAB_ENDPOINT_MISMATCH" : "WAB_HTTP_ERROR", endpointMismatch ? "
|
|
33212
|
-
this.
|
|
33284
|
+
const error = new WABClientError(endpointMismatch ? "WAB_ENDPOINT_MISMATCH" : "WAB_HTTP_ERROR", endpointMismatch ? "WAB endpoint is incompatible." : `WAB request failed with HTTP status ${response.status}.`, isRetryableStatus(response.status), response.status, responseContext);
|
|
33285
|
+
this.report(metadata, error);
|
|
33213
33286
|
response.body?.cancel().catch(() => {});
|
|
33214
33287
|
throw error;
|
|
33215
33288
|
}
|
|
33216
|
-
async
|
|
33289
|
+
async readText(response, responseContext, metadata, timeout) {
|
|
33217
33290
|
try {
|
|
33218
|
-
return await Promise.race([this.
|
|
33291
|
+
return await Promise.race([this.read(response, responseContext), timeout.promise]);
|
|
33219
33292
|
} catch (cause) {
|
|
33220
33293
|
let error;
|
|
33221
33294
|
if (cause instanceof WABClientError) error = cause;
|
|
@@ -33223,17 +33296,17 @@ var WABTransport = class {
|
|
|
33223
33296
|
...responseContext,
|
|
33224
33297
|
cause
|
|
33225
33298
|
});
|
|
33226
|
-
else error = new WABClientError("WAB_INVALID_RESPONSE", "WAB response
|
|
33299
|
+
else error = new WABClientError("WAB_INVALID_RESPONSE", "WAB response read failed.", true, response.status, {
|
|
33227
33300
|
...responseContext,
|
|
33228
33301
|
cause
|
|
33229
33302
|
});
|
|
33230
|
-
this.
|
|
33303
|
+
this.report(metadata, error);
|
|
33231
33304
|
throw error;
|
|
33232
33305
|
} finally {
|
|
33233
33306
|
if (timeout.timer !== void 0) clearTimeout(timeout.timer);
|
|
33234
33307
|
}
|
|
33235
33308
|
}
|
|
33236
|
-
|
|
33309
|
+
parse(responseText, response, responseContext, metadata) {
|
|
33237
33310
|
let parsed;
|
|
33238
33311
|
try {
|
|
33239
33312
|
parsed = JSON.parse(responseText);
|
|
@@ -33242,37 +33315,32 @@ var WABTransport = class {
|
|
|
33242
33315
|
...responseContext,
|
|
33243
33316
|
cause
|
|
33244
33317
|
});
|
|
33245
|
-
this.
|
|
33318
|
+
this.report(metadata, error);
|
|
33246
33319
|
throw error;
|
|
33247
33320
|
}
|
|
33248
33321
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
33249
33322
|
const error = new WABClientError("WAB_INVALID_RESPONSE", "WAB response must be a JSON object.", true, response.status, responseContext);
|
|
33250
|
-
this.
|
|
33323
|
+
this.report(metadata, error);
|
|
33251
33324
|
throw error;
|
|
33252
33325
|
}
|
|
33253
33326
|
return parsed;
|
|
33254
33327
|
}
|
|
33255
|
-
|
|
33256
|
-
this.captureFailure(metadata.operation, metadata.method, metadata.path, metadata.correlationId, metadata.startedAt, error);
|
|
33257
|
-
}
|
|
33258
|
-
async readBoundedResponse(response, responseContext) {
|
|
33259
|
-
await this.rejectOversizedDeclaredResponse(response, responseContext);
|
|
33260
|
-
const reader = response.body?.getReader();
|
|
33261
|
-
if (reader == null) return await this.readBoundedArrayBuffer(response, responseContext);
|
|
33262
|
-
return await this.readBoundedStream(reader, response, responseContext);
|
|
33263
|
-
}
|
|
33264
|
-
async rejectOversizedDeclaredResponse(response, responseContext) {
|
|
33328
|
+
async read(response, responseContext) {
|
|
33265
33329
|
const contentLength = Number(response.headers.get("content-length"));
|
|
33266
|
-
if (
|
|
33267
|
-
|
|
33268
|
-
|
|
33330
|
+
if (Number.isFinite(contentLength) && contentLength > this.responseLimit) {
|
|
33331
|
+
await this.stopBody(response);
|
|
33332
|
+
throw this.sizeError(response, responseContext);
|
|
33333
|
+
}
|
|
33334
|
+
const reader = response.body?.getReader();
|
|
33335
|
+
if (reader == null) return this.readBuffer(response, responseContext);
|
|
33336
|
+
return this.readStream(reader, response, responseContext);
|
|
33269
33337
|
}
|
|
33270
|
-
async
|
|
33338
|
+
async readBuffer(response, responseContext) {
|
|
33271
33339
|
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
33272
|
-
if (bytes.byteLength > this.
|
|
33340
|
+
if (bytes.byteLength > this.responseLimit) throw this.sizeError(response, responseContext);
|
|
33273
33341
|
return new TextDecoder().decode(bytes);
|
|
33274
33342
|
}
|
|
33275
|
-
async
|
|
33343
|
+
async readStream(reader, response, responseContext) {
|
|
33276
33344
|
const chunks = [];
|
|
33277
33345
|
let total = 0;
|
|
33278
33346
|
while (true) {
|
|
@@ -33280,15 +33348,12 @@ var WABTransport = class {
|
|
|
33280
33348
|
if (done) break;
|
|
33281
33349
|
if (value == null) continue;
|
|
33282
33350
|
total += value.byteLength;
|
|
33283
|
-
if (total > this.
|
|
33284
|
-
await this.
|
|
33285
|
-
throw this.
|
|
33351
|
+
if (total > this.responseLimit) {
|
|
33352
|
+
await this.stopReader(reader);
|
|
33353
|
+
throw this.sizeError(response, responseContext);
|
|
33286
33354
|
}
|
|
33287
33355
|
chunks.push(value);
|
|
33288
33356
|
}
|
|
33289
|
-
return this.decodeChunks(chunks, total);
|
|
33290
|
-
}
|
|
33291
|
-
decodeChunks(chunks, total) {
|
|
33292
33357
|
const bytes = new Uint8Array(total);
|
|
33293
33358
|
let offset = 0;
|
|
33294
33359
|
for (const chunk of chunks) {
|
|
@@ -33297,35 +33362,35 @@ var WABTransport = class {
|
|
|
33297
33362
|
}
|
|
33298
33363
|
return new TextDecoder().decode(bytes);
|
|
33299
33364
|
}
|
|
33300
|
-
|
|
33301
|
-
return new WABClientError("WAB_RESPONSE_TOO_LARGE", "WAB response
|
|
33365
|
+
sizeError(response, responseContext) {
|
|
33366
|
+
return new WABClientError("WAB_RESPONSE_TOO_LARGE", "WAB response exceeds its size limit.", false, response.status, responseContext);
|
|
33302
33367
|
}
|
|
33303
|
-
async
|
|
33368
|
+
async stopBody(response) {
|
|
33304
33369
|
try {
|
|
33305
33370
|
await response.body?.cancel();
|
|
33306
33371
|
} catch {}
|
|
33307
33372
|
}
|
|
33308
|
-
async
|
|
33373
|
+
async stopReader(reader) {
|
|
33309
33374
|
try {
|
|
33310
33375
|
await reader.cancel();
|
|
33311
33376
|
} catch {}
|
|
33312
33377
|
}
|
|
33313
|
-
|
|
33378
|
+
report(metadata, error) {
|
|
33314
33379
|
this.telemetry.capture({
|
|
33315
|
-
name:
|
|
33316
|
-
component:
|
|
33380
|
+
name: `${WAB_REQUEST_EVENT}failed`,
|
|
33381
|
+
component: WAB_COMPONENT,
|
|
33317
33382
|
severity: error.retryable ? "warn" : "error",
|
|
33318
|
-
correlationId,
|
|
33383
|
+
correlationId: metadata.correlationId,
|
|
33319
33384
|
attributes: {
|
|
33320
|
-
operation,
|
|
33321
|
-
method,
|
|
33322
|
-
route: path,
|
|
33385
|
+
operation: metadata.operation,
|
|
33386
|
+
method: metadata.method,
|
|
33387
|
+
route: metadata.path,
|
|
33323
33388
|
serverOrigin: this.serverOrigin,
|
|
33324
33389
|
retryable: error.retryable,
|
|
33325
33390
|
...error.status !== void 0 ? { status: error.status } : {},
|
|
33326
33391
|
...error.endpointMarkerPresent !== void 0 ? { endpointMarkerPresent: error.endpointMarkerPresent } : {},
|
|
33327
33392
|
...error.responseCorrelationMatched !== void 0 ? { responseCorrelationMatched: error.responseCorrelationMatched } : {},
|
|
33328
|
-
durationMs: Date.now() - startedAt
|
|
33393
|
+
durationMs: Date.now() - metadata.startedAt
|
|
33329
33394
|
},
|
|
33330
33395
|
error
|
|
33331
33396
|
});
|
|
@@ -33434,8 +33499,8 @@ var WABClient = class {
|
|
|
33434
33499
|
constructor(serverUrl, options = {}) {
|
|
33435
33500
|
this.transport = new WABTransport(serverUrl, options);
|
|
33436
33501
|
}
|
|
33437
|
-
|
|
33438
|
-
return
|
|
33502
|
+
getInfo() {
|
|
33503
|
+
return this.transport.request("/info", {
|
|
33439
33504
|
method: "GET",
|
|
33440
33505
|
operation: "get-info"
|
|
33441
33506
|
});
|
|
@@ -33445,15 +33510,15 @@ var WABClient = class {
|
|
|
33445
33510
|
}
|
|
33446
33511
|
async startAuthMethod(authMethod, presentationKey, payload, correlationId) {
|
|
33447
33512
|
assertHexIdentifier(presentationKey, "presentationKey");
|
|
33448
|
-
return
|
|
33513
|
+
return authMethod.startAuth(this.transport.serverUrl, presentationKey, payload, this.transport, correlationId);
|
|
33449
33514
|
}
|
|
33450
33515
|
async completeAuthMethod(authMethod, presentationKey, payload, correlationId) {
|
|
33451
33516
|
assertHexIdentifier(presentationKey, "presentationKey");
|
|
33452
|
-
return
|
|
33517
|
+
return authMethod.completeAuth(this.transport.serverUrl, presentationKey, payload, this.transport, correlationId);
|
|
33453
33518
|
}
|
|
33454
33519
|
async listLinkedMethods(presentationKey) {
|
|
33455
33520
|
assertHexIdentifier(presentationKey, "presentationKey");
|
|
33456
|
-
return
|
|
33521
|
+
return this.transport.request("/user/linkedMethods", {
|
|
33457
33522
|
operation: "list-linked-methods",
|
|
33458
33523
|
body: { presentationKey }
|
|
33459
33524
|
});
|
|
@@ -33461,7 +33526,7 @@ var WABClient = class {
|
|
|
33461
33526
|
async unlinkMethod(presentationKey, authMethodId) {
|
|
33462
33527
|
assertHexIdentifier(presentationKey, "presentationKey");
|
|
33463
33528
|
if (!Number.isSafeInteger(authMethodId) || authMethodId <= 0) throw new TypeError("authMethodId must be a positive safe integer.");
|
|
33464
|
-
return
|
|
33529
|
+
return this.transport.request("/user/unlinkMethod", {
|
|
33465
33530
|
operation: "unlink-method",
|
|
33466
33531
|
body: {
|
|
33467
33532
|
presentationKey,
|
|
@@ -33471,14 +33536,14 @@ var WABClient = class {
|
|
|
33471
33536
|
}
|
|
33472
33537
|
async requestFaucet(presentationKey) {
|
|
33473
33538
|
assertHexIdentifier(presentationKey, "presentationKey");
|
|
33474
|
-
return
|
|
33539
|
+
return this.transport.request("/faucet/request", {
|
|
33475
33540
|
operation: "request-faucet",
|
|
33476
33541
|
body: { presentationKey }
|
|
33477
33542
|
});
|
|
33478
33543
|
}
|
|
33479
33544
|
async deleteUser(presentationKey) {
|
|
33480
33545
|
assertHexIdentifier(presentationKey, "presentationKey");
|
|
33481
|
-
return
|
|
33546
|
+
return this.transport.request("/user/delete", {
|
|
33482
33547
|
operation: "delete-user",
|
|
33483
33548
|
body: { presentationKey }
|
|
33484
33549
|
});
|
|
@@ -33487,7 +33552,7 @@ var WABClient = class {
|
|
|
33487
33552
|
assertMethodType(methodType);
|
|
33488
33553
|
assertHexIdentifier(userIdHash, "userIdHash");
|
|
33489
33554
|
const normalizedPayload = normalizeAuthPayload(methodType, payload);
|
|
33490
|
-
return
|
|
33555
|
+
return this.transport.request("/auth/start", {
|
|
33491
33556
|
operation: "start-share-auth",
|
|
33492
33557
|
body: {
|
|
33493
33558
|
methodType,
|
|
@@ -33500,7 +33565,7 @@ var WABClient = class {
|
|
|
33500
33565
|
assertMethodType(methodType);
|
|
33501
33566
|
assertHexIdentifier(userIdHash, "userIdHash");
|
|
33502
33567
|
const normalizedPayload = normalizeAuthPayload(methodType, payload);
|
|
33503
|
-
return
|
|
33568
|
+
return this.transport.request("/share/store", {
|
|
33504
33569
|
operation: "store-share",
|
|
33505
33570
|
body: {
|
|
33506
33571
|
methodType,
|
|
@@ -33514,7 +33579,7 @@ var WABClient = class {
|
|
|
33514
33579
|
assertMethodType(methodType);
|
|
33515
33580
|
assertHexIdentifier(userIdHash, "userIdHash");
|
|
33516
33581
|
const normalizedPayload = normalizeAuthPayload(methodType, payload);
|
|
33517
|
-
return
|
|
33582
|
+
return this.transport.request("/share/retrieve", {
|
|
33518
33583
|
operation: "retrieve-share",
|
|
33519
33584
|
body: {
|
|
33520
33585
|
methodType,
|
|
@@ -33527,7 +33592,7 @@ var WABClient = class {
|
|
|
33527
33592
|
assertMethodType(methodType);
|
|
33528
33593
|
assertHexIdentifier(userIdHash, "userIdHash");
|
|
33529
33594
|
const normalizedPayload = normalizeAuthPayload(methodType, payload);
|
|
33530
|
-
return
|
|
33595
|
+
return this.transport.request("/share/update", {
|
|
33531
33596
|
operation: "update-share",
|
|
33532
33597
|
body: {
|
|
33533
33598
|
methodType,
|
|
@@ -33541,7 +33606,7 @@ var WABClient = class {
|
|
|
33541
33606
|
assertMethodType(methodType);
|
|
33542
33607
|
assertHexIdentifier(userIdHash, "userIdHash");
|
|
33543
33608
|
const normalizedPayload = normalizeAuthPayload(methodType, payload);
|
|
33544
|
-
return
|
|
33609
|
+
return this.transport.request("/share/delete", {
|
|
33545
33610
|
operation: "delete-share-user",
|
|
33546
33611
|
body: {
|
|
33547
33612
|
methodType,
|
|
@@ -33555,9 +33620,13 @@ var WABClient = class {
|
|
|
33555
33620
|
//#region ../src/WalletAuthenticationManager.ts
|
|
33556
33621
|
const DEFAULT_AUTH_SESSION_TTL_MS = 600 * 1e3;
|
|
33557
33622
|
const MAX_AUTH_SESSION_TTL_MS = 3600 * 1e3;
|
|
33623
|
+
const AUTH_COMPONENT = "wallet-toolbox.authentication-manager";
|
|
33624
|
+
const AUTH_EVENT = "wallet-toolbox.authentication.";
|
|
33625
|
+
const EXISTING_USER = "existing-user";
|
|
33626
|
+
const NEW_USER = "new-user";
|
|
33558
33627
|
var WABAccountContinuityError = class extends Error {
|
|
33559
33628
|
code = "WERR_WAB_ACCOUNT_CONTINUITY";
|
|
33560
|
-
constructor(message = "WAB and UMP
|
|
33629
|
+
constructor(message = "WAB and UMP accounts disagree; retry or recover.") {
|
|
33561
33630
|
super(message);
|
|
33562
33631
|
this.name = "WABAccountContinuityError";
|
|
33563
33632
|
}
|
|
@@ -33572,6 +33641,7 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
|
|
|
33572
33641
|
wabClient;
|
|
33573
33642
|
authMethod;
|
|
33574
33643
|
authSession;
|
|
33644
|
+
phoneChangeSession;
|
|
33575
33645
|
authSessionTtlMs;
|
|
33576
33646
|
constructor(...[adminOriginator, walletBuilder, interactor, recoveryKeySaver, passwordRetriever, wabClient, authMethod, stateSnapshot, options = {}]) {
|
|
33577
33647
|
super(adminOriginator, walletBuilder, interactor, recoveryKeySaver, passwordRetriever, async (presentationKey, wallet, adminOriginator) => {
|
|
@@ -33594,7 +33664,7 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
|
|
|
33594
33664
|
description: "Fund wallet",
|
|
33595
33665
|
options: { acceptDelayedBroadcast: false }
|
|
33596
33666
|
}, adminOriginator);
|
|
33597
|
-
if (faucetRedeemTXCreationResult.signableTransaction == null) throw new Error("Faucet redemption
|
|
33667
|
+
if (faucetRedeemTXCreationResult.signableTransaction == null) throw new Error("Faucet redemption was not signable.");
|
|
33598
33668
|
const faucetRedeemTX = _bsv_sdk.Transaction.fromAtomicBEEF(faucetRedeemTXCreationResult.signableTransaction.tx);
|
|
33599
33669
|
const faucetRedemptionPuzzle = new _bsv_sdk.RPuzzle();
|
|
33600
33670
|
const randomRedemptionPrivateKey = _bsv_sdk.PrivateKey.fromRandom();
|
|
@@ -33627,7 +33697,7 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
|
|
|
33627
33697
|
* using the chosen AuthMethodInteractor.
|
|
33628
33698
|
*/
|
|
33629
33699
|
async startAuth(payload) {
|
|
33630
|
-
if (this.authMethod == null) throw new Error("No
|
|
33700
|
+
if (this.authMethod == null) throw new Error("No WAB authentication method selected.");
|
|
33631
33701
|
const authMethod = this.authMethod;
|
|
33632
33702
|
if (this.authenticated) throw new Error("User is already authenticated");
|
|
33633
33703
|
this.cancelAuth();
|
|
@@ -33640,8 +33710,8 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
|
|
|
33640
33710
|
...correlationId !== void 0 ? { correlationId } : {}
|
|
33641
33711
|
};
|
|
33642
33712
|
this.telemetry.capture({
|
|
33643
|
-
name:
|
|
33644
|
-
component:
|
|
33713
|
+
name: `${AUTH_EVENT}wab-start.started`,
|
|
33714
|
+
component: AUTH_COMPONENT,
|
|
33645
33715
|
severity: "debug",
|
|
33646
33716
|
correlationId,
|
|
33647
33717
|
attributes: { methodType: authMethod.methodType }
|
|
@@ -33653,8 +33723,8 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
|
|
|
33653
33723
|
throw new Error(message);
|
|
33654
33724
|
}
|
|
33655
33725
|
this.telemetry.capture({
|
|
33656
|
-
name:
|
|
33657
|
-
component:
|
|
33726
|
+
name: `${AUTH_EVENT}wab-start.completed`,
|
|
33727
|
+
component: AUTH_COMPONENT,
|
|
33658
33728
|
severity: "info",
|
|
33659
33729
|
correlationId,
|
|
33660
33730
|
attributes: { methodType: authMethod.methodType }
|
|
@@ -33662,8 +33732,8 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
|
|
|
33662
33732
|
} catch (error) {
|
|
33663
33733
|
this.cancelAuth();
|
|
33664
33734
|
this.telemetry.capture({
|
|
33665
|
-
name:
|
|
33666
|
-
component:
|
|
33735
|
+
name: `${AUTH_EVENT}wab-start.failed`,
|
|
33736
|
+
component: AUTH_COMPONENT,
|
|
33667
33737
|
severity: "warn",
|
|
33668
33738
|
correlationId,
|
|
33669
33739
|
attributes: { methodType: authMethod.methodType },
|
|
@@ -33676,22 +33746,22 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
|
|
|
33676
33746
|
* Completes the WAB-based flow, retrieving the final presentationKey from WAB if successful.
|
|
33677
33747
|
*/
|
|
33678
33748
|
async completeAuth(payload) {
|
|
33679
|
-
if (this.authMethod == null || this.authSession == null) throw new Error("
|
|
33749
|
+
if (this.authMethod == null || this.authSession == null) throw new Error("Start WAB authentication first.");
|
|
33680
33750
|
const authMethod = this.authMethod;
|
|
33681
33751
|
if (this.authSession.methodType !== authMethod.methodType) {
|
|
33682
33752
|
this.cancelAuth();
|
|
33683
|
-
throw new Error("
|
|
33753
|
+
throw new Error("WAB authentication method changed; restart.");
|
|
33684
33754
|
}
|
|
33685
33755
|
if (Date.now() >= this.authSession.expiresAt) {
|
|
33686
33756
|
this.cancelAuth();
|
|
33687
|
-
throw new Error("
|
|
33757
|
+
throw new Error("WAB authentication expired; restart.");
|
|
33688
33758
|
}
|
|
33689
33759
|
const session = this.authSession;
|
|
33690
33760
|
const result = await this.wabClient.completeAuthMethod(authMethod, session.presentationKey, payload, session.correlationId);
|
|
33691
33761
|
if (result.success !== true || result.presentationKey == null || result.presentationKey.length === 0) {
|
|
33692
33762
|
this.telemetry.capture({
|
|
33693
|
-
name:
|
|
33694
|
-
component:
|
|
33763
|
+
name: `${AUTH_EVENT}wab-complete.rejected`,
|
|
33764
|
+
component: AUTH_COMPONENT,
|
|
33695
33765
|
severity: "warn",
|
|
33696
33766
|
correlationId: session.correlationId,
|
|
33697
33767
|
attributes: { methodType: session.methodType }
|
|
@@ -33706,11 +33776,11 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
|
|
|
33706
33776
|
this.cancelAuth();
|
|
33707
33777
|
const wabAccountStatus = this.inferAccountStatus(result, session.presentationKey);
|
|
33708
33778
|
try {
|
|
33709
|
-
await this.
|
|
33779
|
+
await this.provideWABPresentationKey(result, wabAccountStatus);
|
|
33710
33780
|
} catch (error) {
|
|
33711
33781
|
this.telemetry.capture({
|
|
33712
|
-
name:
|
|
33713
|
-
component:
|
|
33782
|
+
name: `${AUTH_EVENT}ump-continuity.failed`,
|
|
33783
|
+
component: AUTH_COMPONENT,
|
|
33714
33784
|
severity: "warn",
|
|
33715
33785
|
correlationId: session.correlationId,
|
|
33716
33786
|
attributes: {
|
|
@@ -33721,18 +33791,18 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
|
|
|
33721
33791
|
});
|
|
33722
33792
|
throw error;
|
|
33723
33793
|
}
|
|
33724
|
-
if (wabAccountStatus ===
|
|
33794
|
+
if (wabAccountStatus === EXISTING_USER && this.authenticationFlow !== EXISTING_USER) {
|
|
33725
33795
|
super.destroy();
|
|
33726
33796
|
const error = new WABAccountContinuityError();
|
|
33727
33797
|
this.telemetry.capture({
|
|
33728
|
-
name:
|
|
33729
|
-
component:
|
|
33798
|
+
name: `${AUTH_EVENT}account-continuity.mismatch`,
|
|
33799
|
+
component: AUTH_COMPONENT,
|
|
33730
33800
|
severity: "error",
|
|
33731
33801
|
correlationId: session.correlationId,
|
|
33732
33802
|
attributes: {
|
|
33733
33803
|
methodType: session.methodType,
|
|
33734
33804
|
wabAccountStatus,
|
|
33735
|
-
umpAccountStatus:
|
|
33805
|
+
umpAccountStatus: NEW_USER
|
|
33736
33806
|
},
|
|
33737
33807
|
error
|
|
33738
33808
|
});
|
|
@@ -33740,8 +33810,8 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
|
|
|
33740
33810
|
}
|
|
33741
33811
|
const continuity = wabAccountStatus === this.authenticationFlow ? "matched" : "ump-existing";
|
|
33742
33812
|
this.telemetry.capture({
|
|
33743
|
-
name:
|
|
33744
|
-
component:
|
|
33813
|
+
name: `${AUTH_EVENT}completed`,
|
|
33814
|
+
component: AUTH_COMPONENT,
|
|
33745
33815
|
severity: continuity === "matched" ? "info" : "warn",
|
|
33746
33816
|
correlationId: session.correlationId,
|
|
33747
33817
|
attributes: {
|
|
@@ -33755,23 +33825,131 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
|
|
|
33755
33825
|
cancelAuth() {
|
|
33756
33826
|
this.authSession = void 0;
|
|
33757
33827
|
}
|
|
33828
|
+
readPendingPhoneChange(result) {
|
|
33829
|
+
const presentationKey = result.pendingPresentationKey;
|
|
33830
|
+
const changeId = result.pendingPhoneChangeId;
|
|
33831
|
+
if (presentationKey === void 0 && changeId === void 0) return void 0;
|
|
33832
|
+
if (!/^[0-9a-fA-F]{64}$/.test(presentationKey ?? "") || !Number.isSafeInteger(changeId) || changeId <= 0) throw new WABAccountContinuityError("WAB returned invalid pending phone-change data.");
|
|
33833
|
+
return {
|
|
33834
|
+
presentationKey,
|
|
33835
|
+
changeId
|
|
33836
|
+
};
|
|
33837
|
+
}
|
|
33838
|
+
async provideWABPresentationKey(result, wabAccountStatus) {
|
|
33839
|
+
const umpTokenOutpoint = typeof result.umpTokenOutpoint === "string" ? result.umpTokenOutpoint : void 0;
|
|
33840
|
+
const lookupOptions = umpTokenOutpoint == null ? void 0 : { pinnedOutpoint: umpTokenOutpoint };
|
|
33841
|
+
const pending = this.readPendingPhoneChange(result);
|
|
33842
|
+
let usePending = false;
|
|
33843
|
+
try {
|
|
33844
|
+
await this.providePresentationKey(_bsv_sdk.Utils.toArray(result.presentationKey, "hex"), lookupOptions);
|
|
33845
|
+
} catch (error) {
|
|
33846
|
+
if (pending == null) throw error;
|
|
33847
|
+
usePending = true;
|
|
33848
|
+
}
|
|
33849
|
+
if (pending != null && (usePending || wabAccountStatus === EXISTING_USER && this.authenticationFlow !== EXISTING_USER)) {
|
|
33850
|
+
await this.providePresentationKey(_bsv_sdk.Utils.toArray(pending.presentationKey, "hex"), lookupOptions);
|
|
33851
|
+
if (this.authenticationFlow === EXISTING_USER) await this.finalizePendingPhoneChange(result.presentationKey, pending);
|
|
33852
|
+
}
|
|
33853
|
+
}
|
|
33854
|
+
async finalizePendingPhoneChange(currentPresentationKey, pending) {
|
|
33855
|
+
const finalized = await this.phoneChange("finalize", {
|
|
33856
|
+
changeId: pending.changeId,
|
|
33857
|
+
presentationKey: currentPresentationKey,
|
|
33858
|
+
newPresentationKey: pending.presentationKey
|
|
33859
|
+
});
|
|
33860
|
+
if (finalized.success !== true || finalized.changeId !== pending.changeId) throw new WABAccountContinuityError(finalized.message || "WAB could not finalize the pending phone change.");
|
|
33861
|
+
}
|
|
33862
|
+
/**
|
|
33863
|
+
* Starts OTP verification for a replacement phone number. The same number
|
|
33864
|
+
* is valid and intentionally produces a fresh presentation key/hash.
|
|
33865
|
+
*/
|
|
33866
|
+
async startPhoneNumberChange(phoneNumber) {
|
|
33867
|
+
if (!this.authenticated) throw new Error("Not authenticated");
|
|
33868
|
+
const normalizedPhone = phoneNumber.trim();
|
|
33869
|
+
const currentPresentationKey = _bsv_sdk.Utils.toHex(await this.getFactor("presentationKey"));
|
|
33870
|
+
const response = await this.phoneChange("start", {
|
|
33871
|
+
presentationKey: currentPresentationKey,
|
|
33872
|
+
phoneNumber: normalizedPhone
|
|
33873
|
+
});
|
|
33874
|
+
if (response.success !== true) throw new Error(response.message || "Phone change failed");
|
|
33875
|
+
this.phoneChangeSession = {
|
|
33876
|
+
phoneNumber: normalizedPhone,
|
|
33877
|
+
presentationKey: currentPresentationKey
|
|
33878
|
+
};
|
|
33879
|
+
}
|
|
33880
|
+
/**
|
|
33881
|
+
* Completes phone verification and stages the WAB association before
|
|
33882
|
+
* publishing the UMP key rotation. WAB retains both the current and pending
|
|
33883
|
+
* presentation keys until finalization, so either side of an interrupted
|
|
33884
|
+
* transition remains recoverable on the next verified login.
|
|
33885
|
+
*/
|
|
33886
|
+
async completePhoneNumberChange(otp) {
|
|
33887
|
+
const session = this.phoneChangeSession;
|
|
33888
|
+
if (session == null) throw new Error("No phone change");
|
|
33889
|
+
if (session.changeToken == null) {
|
|
33890
|
+
const authorization = await this.phoneChange("complete", {
|
|
33891
|
+
presentationKey: session.presentationKey,
|
|
33892
|
+
phoneNumber: session.phoneNumber,
|
|
33893
|
+
otp: otp.trim()
|
|
33894
|
+
});
|
|
33895
|
+
if (authorization.success !== true) throw new Error(authorization.message || "Phone change failed");
|
|
33896
|
+
if (/^[0-9a-fA-F]{64}$/.test(authorization.pendingPresentationKey ?? "") && Number.isSafeInteger(authorization.pendingPhoneChangeId) && authorization.pendingPhoneChangeId > 0) {
|
|
33897
|
+
session.newKey = _bsv_sdk.Utils.toArray(authorization.pendingPresentationKey, "hex");
|
|
33898
|
+
session.changeId = authorization.pendingPhoneChangeId;
|
|
33899
|
+
} else if (typeof authorization.changeToken === "string" && authorization.changeToken.length > 0) session.changeToken = authorization.changeToken;
|
|
33900
|
+
else throw new Error(authorization.message || "Phone change failed");
|
|
33901
|
+
}
|
|
33902
|
+
session.newKey ??= (0, _bsv_sdk.Random)(32);
|
|
33903
|
+
if (session.changeId == null) {
|
|
33904
|
+
const committed = await this.phoneChange("commit", {
|
|
33905
|
+
changeToken: session.changeToken,
|
|
33906
|
+
presentationKey: session.presentationKey,
|
|
33907
|
+
newPresentationKey: _bsv_sdk.Utils.toHex(session.newKey)
|
|
33908
|
+
});
|
|
33909
|
+
if (committed.success !== true || !Number.isSafeInteger(committed.changeId) || committed.changeId <= 0) throw new Error(committed.message || "Phone change failed");
|
|
33910
|
+
session.changeId = committed.changeId;
|
|
33911
|
+
}
|
|
33912
|
+
const changeId = session.changeId;
|
|
33913
|
+
if (session.umpUpdated !== true) {
|
|
33914
|
+
await this.changePresentationKey(session.newKey);
|
|
33915
|
+
session.umpUpdated = true;
|
|
33916
|
+
}
|
|
33917
|
+
const finalized = await this.phoneChange("finalize", {
|
|
33918
|
+
changeId,
|
|
33919
|
+
presentationKey: session.presentationKey,
|
|
33920
|
+
newPresentationKey: _bsv_sdk.Utils.toHex(session.newKey)
|
|
33921
|
+
});
|
|
33922
|
+
if (finalized.success !== true || finalized.changeId !== changeId) throw new Error(finalized.message || "Phone change failed");
|
|
33923
|
+
this.phoneChangeSession = void 0;
|
|
33924
|
+
return { changeId };
|
|
33925
|
+
}
|
|
33926
|
+
cancelPhoneNumberChange() {
|
|
33927
|
+
this.phoneChangeSession = void 0;
|
|
33928
|
+
}
|
|
33758
33929
|
destroy() {
|
|
33759
33930
|
this.cancelAuth();
|
|
33931
|
+
this.cancelPhoneNumberChange();
|
|
33760
33932
|
super.destroy();
|
|
33761
33933
|
}
|
|
33934
|
+
phoneChange(phase, body) {
|
|
33935
|
+
return this.wabClient.transport.request(`/auth/phone-change/${phase}`, {
|
|
33936
|
+
operation: "phone-change",
|
|
33937
|
+
body
|
|
33938
|
+
});
|
|
33939
|
+
}
|
|
33762
33940
|
inferAccountStatus(result, temporaryPresentationKey) {
|
|
33763
33941
|
if (result.presentationKey == null) throw new WABAccountContinuityError("WAB did not return a presentation key.");
|
|
33764
33942
|
const keyMatchesTemporary = this.constantTimeHexEqual(result.presentationKey, temporaryPresentationKey);
|
|
33765
33943
|
const rawAccountStatus = result.accountStatus;
|
|
33766
|
-
if (rawAccountStatus !== void 0 && rawAccountStatus !==
|
|
33944
|
+
if (rawAccountStatus !== void 0 && rawAccountStatus !== NEW_USER && rawAccountStatus !== EXISTING_USER) throw new WABAccountContinuityError("WAB returned an invalid account status.");
|
|
33767
33945
|
const rawExistingUser = result.existingUser;
|
|
33768
|
-
if (rawExistingUser !== void 0 && typeof rawExistingUser !== "boolean") throw new WABAccountContinuityError("WAB returned
|
|
33769
|
-
if (rawAccountStatus !== void 0 && rawExistingUser !== void 0 && rawAccountStatus ===
|
|
33946
|
+
if (rawExistingUser !== void 0 && typeof rawExistingUser !== "boolean") throw new WABAccountContinuityError("WAB returned invalid existing-user data.");
|
|
33947
|
+
if (rawAccountStatus !== void 0 && rawExistingUser !== void 0 && rawAccountStatus === EXISTING_USER !== rawExistingUser) throw new WABAccountContinuityError("WAB returned conflicting account status.");
|
|
33770
33948
|
let compatibilityStatus;
|
|
33771
|
-
if (typeof rawExistingUser === "boolean") compatibilityStatus = rawExistingUser ?
|
|
33949
|
+
if (typeof rawExistingUser === "boolean") compatibilityStatus = rawExistingUser ? EXISTING_USER : NEW_USER;
|
|
33772
33950
|
const explicitStatus = rawAccountStatus ?? compatibilityStatus;
|
|
33773
|
-
if (explicitStatus ===
|
|
33774
|
-
return explicitStatus ?? (keyMatchesTemporary ?
|
|
33951
|
+
if (explicitStatus === NEW_USER && !keyMatchesTemporary || explicitStatus === EXISTING_USER && keyMatchesTemporary) throw new WABAccountContinuityError("WAB returned conflicting account status.");
|
|
33952
|
+
return explicitStatus ?? (keyMatchesTemporary ? NEW_USER : EXISTING_USER);
|
|
33775
33953
|
}
|
|
33776
33954
|
constantTimeHexEqual(left, right) {
|
|
33777
33955
|
if (left.length !== right.length) return false;
|