@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.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
-
import { AuthFetch, BEEF_V1, BEEF_V2, Beef, BeefParty, BigNumber, BlockHeadersService, CachedKeyDeriver, Certificate, Curve, Hash, LocalKVStore, LockingScript, LookupResolver, MasterCertificate, MerklePath, P2PKH, PrivateKey, ProtoWallet, PublicKey, PushDrop, RPuzzle, Random, SHIPBroadcaster, Script, ScriptEvaluationError, Signature, Spend, SymmetricKey, Telemetry, Transaction, TransactionSignature, Utils, Validation, Validation as Validation$1, VerifiableCertificate, createNonce, defaultHttpClient, verifyNonce } from "@bsv/sdk";
|
|
2
|
+
import { AuthFetch, BEEF_V1, BEEF_V2, Beef, BeefParty, BeefTx, BigNumber, BlockHeadersService, CachedKeyDeriver, Certificate, Curve, Hash, LocalKVStore, LockingScript, LookupResolver, MasterCertificate, MerklePath, P2PKH, PrivateKey, ProtoWallet, PublicKey, PushDrop, RPuzzle, Random, SHIPBroadcaster, Script, ScriptEvaluationError, Signature, Spend, SymmetricKey, Telemetry, Transaction, TransactionSignature, Utils, Validation, Validation as Validation$1, VerifiableCertificate, createNonce, defaultHttpClient, verifyNonce } from "@bsv/sdk";
|
|
3
3
|
import { deleteDB, openDB } from "idb";
|
|
4
4
|
import { AESGCM, AESGCMDecrypt } from "@bsv/sdk/primitives/AESGCM";
|
|
5
5
|
import argon2Api from "hash-wasm/dist/argon2.umd.min.js";
|
|
@@ -4046,7 +4046,7 @@ var EntitySyncState = class EntitySyncState extends EntityBase {
|
|
|
4046
4046
|
log += formatSyncSection("OUTPUTS", c.outputs, (r) => `${r.outputId} ${r.txid}.${r.vout} ${r.transactionId} ${r.spendable ? "spendable" : ""} sats:${r.satoshis}`);
|
|
4047
4047
|
return log;
|
|
4048
4048
|
}
|
|
4049
|
-
async processSyncChunk(writer, args, chunk) {
|
|
4049
|
+
async processSyncChunk(writer, args, chunk, trx) {
|
|
4050
4050
|
const mes = [
|
|
4051
4051
|
new MergeEntity(chunk.provenTxs, EntityProvenTx.mergeFind, this.syncMap.provenTx),
|
|
4052
4052
|
new MergeEntity(chunk.outputBaskets, EntityOutputBasket.mergeFind, this.syncMap.outputBasket),
|
|
@@ -4067,16 +4067,16 @@ var EntitySyncState = class EntitySyncState extends EntityBase {
|
|
|
4067
4067
|
let done = true;
|
|
4068
4068
|
if (chunk.user != null) {
|
|
4069
4069
|
const ei = chunk.user;
|
|
4070
|
-
const { found, eo } = await EntityUser.mergeFind(writer, this.userId, ei);
|
|
4070
|
+
const { found, eo } = await EntityUser.mergeFind(writer, this.userId, ei, trx);
|
|
4071
4071
|
if (found) {
|
|
4072
|
-
if (await eo.mergeExisting(writer, args.since, ei)) {
|
|
4072
|
+
if (await eo.mergeExisting(writer, args.since, ei, void 0, trx)) {
|
|
4073
4073
|
maxUpdated_at = maxDate(maxUpdated_at, ei.updated_at);
|
|
4074
4074
|
updates++;
|
|
4075
4075
|
}
|
|
4076
4076
|
}
|
|
4077
4077
|
}
|
|
4078
4078
|
for (const me of mes) {
|
|
4079
|
-
const r = await me.merge(args.since, writer, this.userId, this.syncMap);
|
|
4079
|
+
const r = await me.merge(args.since, writer, this.userId, this.syncMap, trx);
|
|
4080
4080
|
me.esm.count += me.stateArray?.length || 0;
|
|
4081
4081
|
updates += r.updates;
|
|
4082
4082
|
inserts += r.inserts;
|
|
@@ -4087,7 +4087,7 @@ var EntitySyncState = class EntitySyncState extends EntityBase {
|
|
|
4087
4087
|
this.when = maxUpdated_at;
|
|
4088
4088
|
for (const me of mes) me.esm.count = 0;
|
|
4089
4089
|
}
|
|
4090
|
-
await this.updateStorage(writer, false);
|
|
4090
|
+
await this.updateStorage(writer, false, trx);
|
|
4091
4091
|
return {
|
|
4092
4092
|
done,
|
|
4093
4093
|
maxUpdated_at,
|
|
@@ -8219,28 +8219,70 @@ function verifyActionBatchManifestDigest(manifest) {
|
|
|
8219
8219
|
//#endregion
|
|
8220
8220
|
//#region ../src/utility/beefForTxids.ts
|
|
8221
8221
|
/**
|
|
8222
|
-
* Return
|
|
8222
|
+
* Return a minimal BEEF when the source contains data outside the requested
|
|
8223
|
+
* transaction dependency closure. Return undefined when no pruning is needed.
|
|
8223
8224
|
*
|
|
8224
|
-
*
|
|
8225
|
-
*
|
|
8225
|
+
* This form lets forwarding clients retain the caller's original bytes in the
|
|
8226
|
+
* common no-op case instead of rebuilding and reserializing an equivalent BEEF.
|
|
8227
|
+
*/
|
|
8228
|
+
function pruneBeefForTxids(source, txids) {
|
|
8229
|
+
const selection = selectTransactions(source, txids);
|
|
8230
|
+
if (selection.transactions.size === source.txs.length && selection.bumpIndexes.size === source.bumps.length) return;
|
|
8231
|
+
return copySelection(source, selection);
|
|
8232
|
+
}
|
|
8233
|
+
/**
|
|
8234
|
+
* Return an independent minimal BEEF needed to prove the requested transactions.
|
|
8235
|
+
*
|
|
8236
|
+
* Transactions remain in source order and are sorted by Beef when serialized.
|
|
8237
|
+
* The source is indexed and walked once, using an explicit stack so a hostile
|
|
8238
|
+
* dependency depth cannot exhaust the JavaScript call stack.
|
|
8226
8239
|
*/
|
|
8227
8240
|
function beefForTxids(source, txids) {
|
|
8228
|
-
|
|
8241
|
+
return copySelection(source, selectTransactions(source, txids));
|
|
8242
|
+
}
|
|
8243
|
+
function selectTransactions(source, txids) {
|
|
8244
|
+
const byTxid = /* @__PURE__ */ new Map();
|
|
8245
|
+
for (const tx of source.txs) byTxid.set(tx.txid, tx);
|
|
8246
|
+
const transactions = /* @__PURE__ */ new Set();
|
|
8247
|
+
const bumpIndexes = /* @__PURE__ */ new Set();
|
|
8229
8248
|
const visited = /* @__PURE__ */ new Set();
|
|
8230
|
-
const
|
|
8231
|
-
|
|
8249
|
+
const stack = [...txids];
|
|
8250
|
+
while (stack.length > 0) {
|
|
8251
|
+
const txid = stack.pop();
|
|
8252
|
+
if (txid == null || visited.has(txid)) continue;
|
|
8232
8253
|
visited.add(txid);
|
|
8233
|
-
const
|
|
8234
|
-
if (
|
|
8235
|
-
|
|
8236
|
-
|
|
8237
|
-
|
|
8238
|
-
|
|
8239
|
-
|
|
8254
|
+
const tx = byTxid.get(txid);
|
|
8255
|
+
if (tx == null) continue;
|
|
8256
|
+
transactions.add(tx);
|
|
8257
|
+
const bumpIndex = tx.bumpIndex;
|
|
8258
|
+
if (bumpIndex != null && Number.isSafeInteger(bumpIndex) && bumpIndex >= 0 && bumpIndex < source.bumps.length) bumpIndexes.add(bumpIndex);
|
|
8259
|
+
for (const inputTxid of tx.inputTxids) if (!visited.has(inputTxid)) stack.push(inputTxid);
|
|
8260
|
+
}
|
|
8261
|
+
return {
|
|
8262
|
+
transactions,
|
|
8263
|
+
bumpIndexes
|
|
8240
8264
|
};
|
|
8241
|
-
|
|
8265
|
+
}
|
|
8266
|
+
function copySelection(source, selection) {
|
|
8267
|
+
const beef = new Beef(source.version);
|
|
8268
|
+
const bumpIndexMap = /* @__PURE__ */ new Map();
|
|
8269
|
+
for (let index = 0; index < source.bumps.length; index++) {
|
|
8270
|
+
if (!selection.bumpIndexes.has(index)) continue;
|
|
8271
|
+
bumpIndexMap.set(index, beef.bumps.length);
|
|
8272
|
+
beef.bumps.push(cloneMerklePath(source.bumps[index]));
|
|
8273
|
+
}
|
|
8274
|
+
for (const sourceTx of source.txs) {
|
|
8275
|
+
if (!selection.transactions.has(sourceTx)) continue;
|
|
8276
|
+
const bumpIndex = sourceTx.bumpIndex == null ? void 0 : bumpIndexMap.get(sourceTx.bumpIndex);
|
|
8277
|
+
const rawTx = sourceTx.rawTxUint8Array;
|
|
8278
|
+
const copy = rawTx == null ? BeefTx.fromTxid(sourceTx.txid, bumpIndex) : new BeefTx(Uint8Array.from(rawTx), bumpIndex, Array.from(sourceTx.inputTxids));
|
|
8279
|
+
beef.txs.push(copy);
|
|
8280
|
+
}
|
|
8242
8281
|
return beef;
|
|
8243
8282
|
}
|
|
8283
|
+
function cloneMerklePath(source) {
|
|
8284
|
+
return new MerklePath(source.blockHeight, source.path.map((level) => level.map((leaf) => ({ ...leaf }))), false, false);
|
|
8285
|
+
}
|
|
8244
8286
|
//#endregion
|
|
8245
8287
|
//#region ../src/storage/methods/offsetKey.ts
|
|
8246
8288
|
function keyOffsetToHashedSecret(pub, keyOffset) {
|
|
@@ -10938,7 +10980,7 @@ function validateRequiredOutputs(storage, userId, vargs) {
|
|
|
10938
10980
|
* @returns {xinputs} extended validated required inputs.
|
|
10939
10981
|
*/
|
|
10940
10982
|
async function validateRequiredInputs(storage, userId, vargs) {
|
|
10941
|
-
|
|
10983
|
+
let beef = new Beef();
|
|
10942
10984
|
if (vargs.inputs.length === 0) return {
|
|
10943
10985
|
storageBeef: beef,
|
|
10944
10986
|
beef,
|
|
@@ -10964,6 +11006,7 @@ async function validateRequiredInputs(storage, userId, vargs) {
|
|
|
10964
11006
|
inputsByTxid[input.outpoint.txid] ||= [];
|
|
10965
11007
|
inputsByTxid[input.outpoint.txid].push(input);
|
|
10966
11008
|
}
|
|
11009
|
+
beef = beefForTxids(beef, Object.keys(inputsByTxid));
|
|
10967
11010
|
const localKnownInputTxids = {};
|
|
10968
11011
|
for (const [txid, txInputs] of Object.entries(inputsByTxid)) localKnownInputTxids[txid] = txInputs.every((input) => {
|
|
10969
11012
|
const output = preloadedOutputsByOutpoint[`${input.outpoint.txid}.${input.outpoint.vout}`];
|
|
@@ -15225,11 +15268,19 @@ var StorageProvider = class StorageProvider extends StorageReaderWriter {
|
|
|
15225
15268
|
return await this.updateOutput(output.outputId, { basketId: void 0 });
|
|
15226
15269
|
}
|
|
15227
15270
|
async processSyncChunk(args, chunk) {
|
|
15228
|
-
|
|
15229
|
-
|
|
15230
|
-
|
|
15231
|
-
|
|
15232
|
-
|
|
15271
|
+
return await this.transaction(async (trx) => {
|
|
15272
|
+
const user = verifyTruthy(verifyOneOrNone(await this.findUsers({
|
|
15273
|
+
partial: { identityKey: args.identityKey },
|
|
15274
|
+
trx
|
|
15275
|
+
})));
|
|
15276
|
+
return await new EntitySyncState(verifyOne(await this.findSyncStates({
|
|
15277
|
+
partial: {
|
|
15278
|
+
storageIdentityKey: args.fromStorageIdentityKey,
|
|
15279
|
+
userId: user.userId
|
|
15280
|
+
},
|
|
15281
|
+
trx
|
|
15282
|
+
}))).processSyncChunk(this, args, chunk, trx);
|
|
15283
|
+
});
|
|
15233
15284
|
}
|
|
15234
15285
|
/**
|
|
15235
15286
|
* Handles storage changes when a valid MerklePath and mined block header are found for a ProvenTxReq txid.
|
|
@@ -17294,8 +17345,10 @@ var StorageIdb = class extends StorageProvider {
|
|
|
17294
17345
|
await tx.done;
|
|
17295
17346
|
return r;
|
|
17296
17347
|
} catch (err) {
|
|
17297
|
-
|
|
17298
|
-
|
|
17348
|
+
try {
|
|
17349
|
+
tx.abort();
|
|
17350
|
+
await tx.done;
|
|
17351
|
+
} catch {}
|
|
17299
17352
|
throw err;
|
|
17300
17353
|
}
|
|
17301
17354
|
}
|
|
@@ -18262,6 +18315,25 @@ var StorageClientBase = class {
|
|
|
18262
18315
|
* @returns `StorageCreateActionResults` supporting additional wallet processing to yield `createAction` results.
|
|
18263
18316
|
*/
|
|
18264
18317
|
async createAction(auth, args) {
|
|
18318
|
+
if (args.inputBEEF != null) if (args.inputs.length === 0) args = {
|
|
18319
|
+
...args,
|
|
18320
|
+
inputBEEF: void 0
|
|
18321
|
+
};
|
|
18322
|
+
else {
|
|
18323
|
+
let source;
|
|
18324
|
+
try {
|
|
18325
|
+
source = Beef.fromBinary(args.inputBEEF);
|
|
18326
|
+
} catch {
|
|
18327
|
+
source = void 0;
|
|
18328
|
+
}
|
|
18329
|
+
if (source != null) {
|
|
18330
|
+
const pruned = pruneBeefForTxids(source, args.inputs.map((input) => input.outpoint.txid));
|
|
18331
|
+
if (pruned != null) args = {
|
|
18332
|
+
...args,
|
|
18333
|
+
inputBEEF: pruned.toBinary()
|
|
18334
|
+
};
|
|
18335
|
+
}
|
|
18336
|
+
}
|
|
18265
18337
|
return await this.rpcCall("createAction", [auth, args]);
|
|
18266
18338
|
}
|
|
18267
18339
|
/**
|
|
@@ -30932,6 +31004,7 @@ var SetupClient = class SetupClient {
|
|
|
30932
31004
|
};
|
|
30933
31005
|
//#endregion
|
|
30934
31006
|
//#region ../src/CWIStyleWalletManager.ts
|
|
31007
|
+
const CWI_COMPONENT = "wallet-toolbox.cwi-manager";
|
|
30935
31008
|
/**
|
|
30936
31009
|
* Number of rounds used in PBKDF2 for deriving password keys.
|
|
30937
31010
|
*/
|
|
@@ -31164,11 +31237,11 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31164
31237
|
* @param hash The 32-byte SHA-256 hash of the presentation key.
|
|
31165
31238
|
* @returns A UMPToken object (including currentOutpoint) if found, otherwise undefined.
|
|
31166
31239
|
*/
|
|
31167
|
-
async findByPresentationKeyHash(hash) {
|
|
31168
|
-
return
|
|
31240
|
+
async findByPresentationKeyHash(hash, options) {
|
|
31241
|
+
return this.findToken({
|
|
31169
31242
|
service: "ls_users",
|
|
31170
31243
|
query: { presentationHash: Utils.toHex(hash) }
|
|
31171
|
-
}, "presentation");
|
|
31244
|
+
}, "presentation", options);
|
|
31172
31245
|
}
|
|
31173
31246
|
/**
|
|
31174
31247
|
* Finds a UMP token on-chain by the given recovery key hash, if it exists.
|
|
@@ -31177,13 +31250,13 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31177
31250
|
* @param hash The 32-byte SHA-256 hash of the recovery key.
|
|
31178
31251
|
* @returns A UMPToken object (including currentOutpoint) if found, otherwise undefined.
|
|
31179
31252
|
*/
|
|
31180
|
-
async findByRecoveryKeyHash(hash) {
|
|
31181
|
-
return
|
|
31253
|
+
async findByRecoveryKeyHash(hash, options) {
|
|
31254
|
+
return this.findToken({
|
|
31182
31255
|
service: "ls_users",
|
|
31183
31256
|
query: { recoveryHash: Utils.toHex(hash) }
|
|
31184
|
-
}, "recovery");
|
|
31257
|
+
}, "recovery", options);
|
|
31185
31258
|
}
|
|
31186
|
-
async findToken(question, lookupKind) {
|
|
31259
|
+
async findToken(question, lookupKind, options) {
|
|
31187
31260
|
const correlationId = this.telemetry.enabled === true ? this.telemetry.createCorrelationId() : void 0;
|
|
31188
31261
|
const startedAt = Date.now();
|
|
31189
31262
|
this.telemetry.capture({
|
|
@@ -31200,34 +31273,39 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31200
31273
|
correlationId
|
|
31201
31274
|
});
|
|
31202
31275
|
} catch (error) {
|
|
31203
|
-
const diagnostics = this.
|
|
31204
|
-
this.
|
|
31276
|
+
const diagnostics = this.emptyStats(correlationId);
|
|
31277
|
+
this.lookupFailed(lookupKind, "lookup-unavailable", diagnostics, startedAt, error);
|
|
31205
31278
|
throw new UMPTokenLookupError("lookup-unavailable", diagnostics, { cause: error });
|
|
31206
31279
|
}
|
|
31207
|
-
const diagnostics = this.
|
|
31280
|
+
const diagnostics = this.diagnosticsFor(resolution);
|
|
31208
31281
|
const tokens = this.parseLookupAnswers(resolution.answer);
|
|
31209
31282
|
const expectedHash = question.query[lookupKind === "presentation" ? "presentationHash" : "recoveryHash"].toLowerCase();
|
|
31210
31283
|
const matchingTokens = tokens.filter((token) => Utils.toHex(lookupKind === "presentation" ? token.presentationHash : token.recoveryHash).toLowerCase() === expectedHash);
|
|
31211
31284
|
if (matchingTokens.length > 1) {
|
|
31212
31285
|
const newest = this.resolveNewestToken(matchingTokens, resolution.answer.outputs);
|
|
31213
31286
|
if (newest != null) {
|
|
31214
|
-
this.
|
|
31287
|
+
this.lookupDone(lookupKind, "found", diagnostics, startedAt, { supersededTokens: matchingTokens.length - 1 });
|
|
31215
31288
|
return newest;
|
|
31216
31289
|
}
|
|
31290
|
+
const pinned = options?.pinnedOutpoint ? matchingTokens.find((token) => token.currentOutpoint === options.pinnedOutpoint) : void 0;
|
|
31291
|
+
if (pinned != null) {
|
|
31292
|
+
this.lookupDone(lookupKind, "found", diagnostics, startedAt);
|
|
31293
|
+
return pinned;
|
|
31294
|
+
}
|
|
31217
31295
|
const reason = "token-ambiguous";
|
|
31218
|
-
this.
|
|
31296
|
+
this.lookupFailed(lookupKind, reason, diagnostics, startedAt);
|
|
31219
31297
|
throw new UMPTokenLookupError(reason, diagnostics);
|
|
31220
31298
|
}
|
|
31221
31299
|
if (matchingTokens.length === 1) {
|
|
31222
|
-
this.
|
|
31300
|
+
this.lookupDone(lookupKind, "found", diagnostics, startedAt);
|
|
31223
31301
|
return matchingTokens[0];
|
|
31224
31302
|
}
|
|
31225
31303
|
if (resolution.progress.emptyHosts > 0) {
|
|
31226
|
-
this.
|
|
31304
|
+
this.lookupDone(lookupKind, "not-found", diagnostics, startedAt);
|
|
31227
31305
|
return;
|
|
31228
31306
|
}
|
|
31229
31307
|
const reason = resolution.answer.outputs.length > 0 ? "token-malformed" : "lookup-incomplete";
|
|
31230
|
-
this.
|
|
31308
|
+
this.lookupFailed(lookupKind, reason, diagnostics, startedAt);
|
|
31231
31309
|
throw new UMPTokenLookupError(reason, diagnostics);
|
|
31232
31310
|
}
|
|
31233
31311
|
/**
|
|
@@ -31258,7 +31336,7 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31258
31336
|
spent: /* @__PURE__ */ new Set()
|
|
31259
31337
|
};
|
|
31260
31338
|
evidence.txs.push(tx);
|
|
31261
|
-
this.
|
|
31339
|
+
this.collectSpends(tx, evidence.spent, /* @__PURE__ */ new Set());
|
|
31262
31340
|
evidenceByCandidate.set(outpoint, evidence);
|
|
31263
31341
|
} catch {}
|
|
31264
31342
|
if (evidenceByCandidate.size !== candidates.size) return void 0;
|
|
@@ -31267,7 +31345,7 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31267
31345
|
const provenContinuations = survivors.filter((outpoint) => {
|
|
31268
31346
|
const evidence = evidenceByCandidate.get(outpoint);
|
|
31269
31347
|
const token = candidates.get(outpoint);
|
|
31270
|
-
return evidence != null && token != null && evidence.txs.some((tx) => this.
|
|
31348
|
+
return evidence != null && token != null && evidence.txs.some((tx) => this.consumesIdentity(tx, token));
|
|
31271
31349
|
});
|
|
31272
31350
|
if (provenContinuations.length !== 1) return void 0;
|
|
31273
31351
|
return candidates.get(provenContinuations[0]);
|
|
@@ -31278,7 +31356,7 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31278
31356
|
* hash — on-chain proof that the candidate is an update of a same-identity
|
|
31279
31357
|
* predecessor rather than an independently minted token.
|
|
31280
31358
|
*/
|
|
31281
|
-
|
|
31359
|
+
consumesIdentity(tx, token) {
|
|
31282
31360
|
const presentationHash = Utils.toHex(token.presentationHash);
|
|
31283
31361
|
const recoveryHash = Utils.toHex(token.recoveryHash);
|
|
31284
31362
|
for (const input of tx.inputs) {
|
|
@@ -31304,7 +31382,7 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31304
31382
|
* renditions are absent from the lookup answer. Iterative so arbitrarily
|
|
31305
31383
|
* long update chains cannot exhaust the call stack.
|
|
31306
31384
|
*/
|
|
31307
|
-
|
|
31385
|
+
collectSpends(tx, spent, visited) {
|
|
31308
31386
|
const pending = [tx];
|
|
31309
31387
|
while (pending.length > 0) {
|
|
31310
31388
|
const current = pending.pop();
|
|
@@ -31319,7 +31397,7 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31319
31397
|
}
|
|
31320
31398
|
}
|
|
31321
31399
|
}
|
|
31322
|
-
|
|
31400
|
+
emptyStats(correlationId) {
|
|
31323
31401
|
return {
|
|
31324
31402
|
hostCount: 0,
|
|
31325
31403
|
completedHosts: 0,
|
|
@@ -31332,7 +31410,7 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31332
31410
|
...correlationId !== void 0 ? { correlationId } : {}
|
|
31333
31411
|
};
|
|
31334
31412
|
}
|
|
31335
|
-
|
|
31413
|
+
diagnosticsFor(resolution) {
|
|
31336
31414
|
const progress = resolution.progress;
|
|
31337
31415
|
return {
|
|
31338
31416
|
hostCount: progress.hostCount,
|
|
@@ -31346,7 +31424,7 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31346
31424
|
...progress.correlationId !== void 0 ? { correlationId: progress.correlationId } : {}
|
|
31347
31425
|
};
|
|
31348
31426
|
}
|
|
31349
|
-
|
|
31427
|
+
lookupAttrs(diagnostics) {
|
|
31350
31428
|
return {
|
|
31351
31429
|
hostCount: diagnostics.hostCount,
|
|
31352
31430
|
completedHosts: diagnostics.completedHosts,
|
|
@@ -31358,7 +31436,7 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31358
31436
|
outputCount: diagnostics.outputCount
|
|
31359
31437
|
};
|
|
31360
31438
|
}
|
|
31361
|
-
|
|
31439
|
+
lookupDone(lookupKind, result, diagnostics, startedAt, extraAttributes = {}) {
|
|
31362
31440
|
this.telemetry.capture({
|
|
31363
31441
|
name: "wallet-toolbox.ump.lookup.completed",
|
|
31364
31442
|
component: "wallet-toolbox.ump",
|
|
@@ -31368,12 +31446,12 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31368
31446
|
lookupKind,
|
|
31369
31447
|
result,
|
|
31370
31448
|
durationMs: Date.now() - startedAt,
|
|
31371
|
-
...this.
|
|
31449
|
+
...this.lookupAttrs(diagnostics),
|
|
31372
31450
|
...extraAttributes
|
|
31373
31451
|
}
|
|
31374
31452
|
});
|
|
31375
31453
|
}
|
|
31376
|
-
|
|
31454
|
+
lookupFailed(lookupKind, reason, diagnostics, startedAt, error) {
|
|
31377
31455
|
this.telemetry.capture({
|
|
31378
31456
|
name: "wallet-toolbox.ump.lookup.indeterminate",
|
|
31379
31457
|
component: "wallet-toolbox.ump",
|
|
@@ -31383,7 +31461,7 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31383
31461
|
lookupKind,
|
|
31384
31462
|
reason,
|
|
31385
31463
|
durationMs: Date.now() - startedAt,
|
|
31386
|
-
...this.
|
|
31464
|
+
...this.lookupAttrs(diagnostics)
|
|
31387
31465
|
},
|
|
31388
31466
|
error
|
|
31389
31467
|
});
|
|
@@ -31401,27 +31479,27 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31401
31479
|
* @returns The outpoint of the newly created UMP token (e.g. "abcd1234...ef.0").
|
|
31402
31480
|
*/
|
|
31403
31481
|
async buildAndSend(wallet, adminOriginator, token, oldTokenToConsume) {
|
|
31404
|
-
const fields = this.
|
|
31482
|
+
const fields = this.tokenFields(token);
|
|
31405
31483
|
const tokenOutput = [{
|
|
31406
31484
|
lockingScript: (await new PushDrop(wallet, adminOriginator).lock(fields, [2, "admin user management token"], "1", "self", true, true)).toHex(),
|
|
31407
31485
|
satoshis: 1,
|
|
31408
31486
|
outputDescription: "New UMP token output"
|
|
31409
31487
|
}];
|
|
31410
|
-
const { resolvedOldToken, inputToken } = await this.
|
|
31488
|
+
const { resolvedOldToken, inputToken } = await this.resolveOldInput(oldTokenToConsume);
|
|
31411
31489
|
const inputs = resolvedOldToken?.currentOutpoint ? [{
|
|
31412
31490
|
outpoint: resolvedOldToken.currentOutpoint,
|
|
31413
31491
|
unlockingScriptLength: 73,
|
|
31414
31492
|
inputDescription: "Consume old UMP token"
|
|
31415
31493
|
}] : [];
|
|
31416
|
-
const createResult = await this.
|
|
31417
|
-
if (!createResult.signableTransaction) return
|
|
31494
|
+
const createResult = await this.createAction(wallet, adminOriginator, inputs, tokenOutput, inputToken, resolvedOldToken);
|
|
31495
|
+
if (!createResult.signableTransaction) return this.broadcastFinal(createResult);
|
|
31418
31496
|
const reference = createResult.signableTransaction.reference;
|
|
31419
31497
|
const partialTx = Transaction.fromBEEF(createResult.signableTransaction.tx);
|
|
31420
|
-
if (resolvedOldToken?.currentOutpoint) return
|
|
31421
|
-
return
|
|
31498
|
+
if (resolvedOldToken?.currentOutpoint) return this.renewToken(wallet, adminOriginator, reference, partialTx);
|
|
31499
|
+
return this.broadcastNew(wallet, adminOriginator, reference);
|
|
31422
31500
|
}
|
|
31423
31501
|
/** Assembles the ordered number[][] fields array from a UMPToken. */
|
|
31424
|
-
|
|
31502
|
+
tokenFields(token) {
|
|
31425
31503
|
const fields = [];
|
|
31426
31504
|
fields[0] = token.passwordSalt;
|
|
31427
31505
|
fields[1] = token.passwordPresentationPrimary;
|
|
@@ -31448,20 +31526,20 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31448
31526
|
return fields;
|
|
31449
31527
|
}
|
|
31450
31528
|
/** Looks up the old token on the overlay; returns undefined resolved token if not found. */
|
|
31451
|
-
async
|
|
31529
|
+
async resolveOldInput(oldTokenToConsume) {
|
|
31452
31530
|
if (!oldTokenToConsume?.currentOutpoint) return {
|
|
31453
31531
|
resolvedOldToken: void 0,
|
|
31454
31532
|
inputToken: void 0
|
|
31455
31533
|
};
|
|
31456
31534
|
const inputToken = await this.findByOutpoint(oldTokenToConsume.currentOutpoint);
|
|
31457
|
-
if (inputToken == null) throw new Error("
|
|
31535
|
+
if (inputToken == null) throw new Error("Previous UMP token unavailable; update refused.");
|
|
31458
31536
|
return {
|
|
31459
31537
|
resolvedOldToken: oldTokenToConsume,
|
|
31460
31538
|
inputToken
|
|
31461
31539
|
};
|
|
31462
31540
|
}
|
|
31463
31541
|
/** Creates the UMP action without dropping a required old-token input on failure. */
|
|
31464
|
-
async
|
|
31542
|
+
async createAction(wallet, adminOriginator, inputs, outputs, inputToken, resolvedOldToken) {
|
|
31465
31543
|
try {
|
|
31466
31544
|
return await wallet.createAction({
|
|
31467
31545
|
description: resolvedOldToken == null ? "Create new UMP token" : "Renew UMP token (consume old, create new)",
|
|
@@ -31488,43 +31566,43 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31488
31566
|
}
|
|
31489
31567
|
}
|
|
31490
31568
|
/** Handles a fully-finalized (no signable tx) createAction result — broadcasts and returns outpoint. */
|
|
31491
|
-
async
|
|
31569
|
+
async broadcastFinal(createResult) {
|
|
31492
31570
|
const finalTxid = createResult.txid || (createResult.tx != null ? Transaction.fromAtomicBEEF(createResult.tx).id("hex") : void 0);
|
|
31493
|
-
if (!finalTxid) throw new Error("
|
|
31494
|
-
if (createResult.tx == null) throw new Error("
|
|
31571
|
+
if (!finalTxid) throw new Error("UMP transaction was not finalized.");
|
|
31572
|
+
if (createResult.tx == null) throw new Error("UMP transaction data missing.");
|
|
31495
31573
|
const broadcastTx = Transaction.fromAtomicBEEF(createResult.tx);
|
|
31496
31574
|
const result = await this.broadcaster.broadcast(broadcastTx);
|
|
31497
|
-
this.
|
|
31575
|
+
this.assertBroadcast(result, "create-finalized");
|
|
31498
31576
|
return `${finalTxid}.0`;
|
|
31499
31577
|
}
|
|
31500
31578
|
/** Signs the old-token input and broadcasts — used during UMP token renewal. */
|
|
31501
|
-
async
|
|
31579
|
+
async renewToken(wallet, adminOriginator, reference, partialTx) {
|
|
31502
31580
|
const unlockingScript = await new PushDrop(wallet, adminOriginator).unlock([2, "admin user management token"], "1", "self").sign(partialTx, 0);
|
|
31503
31581
|
const signResult = await wallet.signAction({
|
|
31504
31582
|
reference,
|
|
31505
31583
|
spends: { 0: { unlockingScript: unlockingScript.toHex() } }
|
|
31506
31584
|
}, adminOriginator);
|
|
31507
31585
|
const finalTxid = signResult.txid || (signResult.tx == null ? "" : Transaction.fromAtomicBEEF(signResult.tx).id("hex"));
|
|
31508
|
-
if (!finalTxid) throw new Error("Could not finalize
|
|
31509
|
-
if (signResult.tx == null) throw new Error("
|
|
31586
|
+
if (!finalTxid) throw new Error("Could not finalize renewed UMP token.");
|
|
31587
|
+
if (signResult.tx == null) throw new Error("Renewed UMP token transaction data missing.");
|
|
31510
31588
|
const result = await this.broadcaster.broadcast(Transaction.fromAtomicBEEF(signResult.tx));
|
|
31511
|
-
this.
|
|
31589
|
+
this.assertBroadcast(result, "renew");
|
|
31512
31590
|
return `${finalTxid}.0`;
|
|
31513
31591
|
}
|
|
31514
31592
|
/** Signs without input spending and broadcasts — used when creating a brand-new UMP token. */
|
|
31515
|
-
async
|
|
31593
|
+
async broadcastNew(wallet, adminOriginator, reference) {
|
|
31516
31594
|
const signResult = await wallet.signAction({
|
|
31517
31595
|
reference,
|
|
31518
31596
|
spends: {}
|
|
31519
31597
|
}, adminOriginator);
|
|
31520
31598
|
const finalTxid = signResult.txid || (signResult.tx == null ? "" : Transaction.fromAtomicBEEF(signResult.tx).id("hex"));
|
|
31521
|
-
if (!finalTxid) throw new Error("
|
|
31522
|
-
if (signResult.tx == null) throw new Error("
|
|
31599
|
+
if (!finalTxid) throw new Error("Could not finalize new UMP token.");
|
|
31600
|
+
if (signResult.tx == null) throw new Error("New UMP token transaction data missing.");
|
|
31523
31601
|
const result = await this.broadcaster.broadcast(Transaction.fromAtomicBEEF(signResult.tx));
|
|
31524
|
-
this.
|
|
31602
|
+
this.assertBroadcast(result, "create");
|
|
31525
31603
|
return `${finalTxid}.0`;
|
|
31526
31604
|
}
|
|
31527
|
-
|
|
31605
|
+
assertBroadcast(result, operation) {
|
|
31528
31606
|
const succeeded = result.status === "success";
|
|
31529
31607
|
this.telemetry.capture({
|
|
31530
31608
|
name: succeeded ? "wallet-toolbox.ump.broadcast.completed" : "wallet-toolbox.ump.broadcast.failed",
|
|
@@ -31553,12 +31631,12 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31553
31631
|
if (answer.type !== "output-list" || answer.outputs.length === 0) return [];
|
|
31554
31632
|
const tokens = [];
|
|
31555
31633
|
for (const output of answer.outputs) {
|
|
31556
|
-
const token = this.
|
|
31634
|
+
const token = this.parseOutput(output);
|
|
31557
31635
|
if (token != null) tokens.push(token);
|
|
31558
31636
|
}
|
|
31559
31637
|
return tokens;
|
|
31560
31638
|
}
|
|
31561
|
-
|
|
31639
|
+
parseOutput(output) {
|
|
31562
31640
|
try {
|
|
31563
31641
|
const tx = Transaction.fromBEEF(output.beef);
|
|
31564
31642
|
const txOutput = tx.outputs[output.outputIndex];
|
|
@@ -31608,14 +31686,14 @@ var OverlayUMPTokenInteractor = class {
|
|
|
31608
31686
|
correlationId
|
|
31609
31687
|
});
|
|
31610
31688
|
} catch (error) {
|
|
31611
|
-
const diagnostics = this.
|
|
31612
|
-
this.
|
|
31689
|
+
const diagnostics = this.emptyStats(correlationId);
|
|
31690
|
+
this.lookupFailed("outpoint", "lookup-unavailable", diagnostics, startedAt, error);
|
|
31613
31691
|
throw new UMPTokenLookupError("lookup-unavailable", diagnostics, { cause: error });
|
|
31614
31692
|
}
|
|
31615
31693
|
if (resolution.answer.outputs.length === 0) {
|
|
31616
31694
|
if (resolution.progress.emptyHosts === 0) {
|
|
31617
|
-
const diagnostics = this.
|
|
31618
|
-
this.
|
|
31695
|
+
const diagnostics = this.diagnosticsFor(resolution);
|
|
31696
|
+
this.lookupFailed("outpoint", "lookup-incomplete", diagnostics, startedAt);
|
|
31619
31697
|
throw new UMPTokenLookupError("lookup-incomplete", diagnostics);
|
|
31620
31698
|
}
|
|
31621
31699
|
return;
|
|
@@ -31757,20 +31835,20 @@ var CWIStyleWalletManager = class {
|
|
|
31757
31835
|
if (this._initSnapshot !== void 0) {
|
|
31758
31836
|
this.telemetry.capture({
|
|
31759
31837
|
name: "wallet-toolbox.snapshot.initialization.started",
|
|
31760
|
-
component:
|
|
31838
|
+
component: CWI_COMPONENT,
|
|
31761
31839
|
severity: "debug"
|
|
31762
31840
|
});
|
|
31763
31841
|
try {
|
|
31764
31842
|
await this.loadSnapshot(this._initSnapshot);
|
|
31765
31843
|
this.telemetry.capture({
|
|
31766
31844
|
name: "wallet-toolbox.snapshot.initialization.completed",
|
|
31767
|
-
component:
|
|
31845
|
+
component: CWI_COMPONENT,
|
|
31768
31846
|
severity: "info"
|
|
31769
31847
|
});
|
|
31770
31848
|
} catch (error) {
|
|
31771
31849
|
this.telemetry.capture({
|
|
31772
31850
|
name: "wallet-toolbox.snapshot.initialization.failed",
|
|
31773
|
-
component:
|
|
31851
|
+
component: CWI_COMPONENT,
|
|
31774
31852
|
severity: "error",
|
|
31775
31853
|
error
|
|
31776
31854
|
});
|
|
@@ -31779,9 +31857,11 @@ var CWIStyleWalletManager = class {
|
|
|
31779
31857
|
}
|
|
31780
31858
|
}
|
|
31781
31859
|
/**
|
|
31782
|
-
* Provides the presentation key.
|
|
31860
|
+
* Provides the presentation key. A WAB operator pin may be supplied by the
|
|
31861
|
+
* authentication manager; normal lookup and lineage resolution always run
|
|
31862
|
+
* before this ambiguity-only fallback.
|
|
31783
31863
|
*/
|
|
31784
|
-
async providePresentationKey(key) {
|
|
31864
|
+
async providePresentationKey(key, lookupOptions) {
|
|
31785
31865
|
if (this.authenticated) throw new Error("User is already authenticated");
|
|
31786
31866
|
if (this.authenticationMode === "recovery-key-and-password") throw new Error("Presentation key is not needed in this mode");
|
|
31787
31867
|
if (key.length !== 32 || key.some((byte) => !Number.isInteger(byte) || byte < 0 || byte > 255)) throw new TypeError("Presentation key must contain exactly 32 bytes.");
|
|
@@ -31790,17 +31870,17 @@ var CWIStyleWalletManager = class {
|
|
|
31790
31870
|
const startedAt = Date.now();
|
|
31791
31871
|
this.telemetry.capture({
|
|
31792
31872
|
name: "wallet-toolbox.authentication.account-lookup.started",
|
|
31793
|
-
component:
|
|
31873
|
+
component: CWI_COMPONENT,
|
|
31794
31874
|
severity: "debug",
|
|
31795
31875
|
attributes: { lookupKind: "presentation" }
|
|
31796
31876
|
});
|
|
31797
31877
|
let token;
|
|
31798
31878
|
try {
|
|
31799
|
-
token = await this.UMPTokenInteractor.findByPresentationKeyHash(hash);
|
|
31879
|
+
token = await this.UMPTokenInteractor.findByPresentationKeyHash(hash, lookupOptions);
|
|
31800
31880
|
} catch (error) {
|
|
31801
31881
|
this.telemetry.capture({
|
|
31802
31882
|
name: "wallet-toolbox.authentication.account-lookup.failed",
|
|
31803
|
-
component:
|
|
31883
|
+
component: CWI_COMPONENT,
|
|
31804
31884
|
severity: "warn",
|
|
31805
31885
|
attributes: {
|
|
31806
31886
|
lookupKind: "presentation",
|
|
@@ -31820,7 +31900,7 @@ var CWIStyleWalletManager = class {
|
|
|
31820
31900
|
}
|
|
31821
31901
|
this.telemetry.capture({
|
|
31822
31902
|
name: "wallet-toolbox.authentication.account-lookup.completed",
|
|
31823
|
-
component:
|
|
31903
|
+
component: CWI_COMPONENT,
|
|
31824
31904
|
severity: "info",
|
|
31825
31905
|
attributes: {
|
|
31826
31906
|
lookupKind: "presentation",
|
|
@@ -31836,11 +31916,11 @@ var CWIStyleWalletManager = class {
|
|
|
31836
31916
|
if (this.authenticated) throw new Error("User is already authenticated");
|
|
31837
31917
|
if (this.authenticationMode === "presentation-key-and-recovery-key") throw new Error("Password is not needed in this mode");
|
|
31838
31918
|
if (this.authenticationFlow === "unknown") throw new Error("Determine account status with a presentation or recovery key before providing a password.");
|
|
31839
|
-
if (this.authenticationFlow === "existing-user") await this.
|
|
31840
|
-
else await this.
|
|
31919
|
+
if (this.authenticationFlow === "existing-user") await this.unlockExisting(password);
|
|
31920
|
+
else await this.createNewUser(password);
|
|
31841
31921
|
}
|
|
31842
31922
|
/** Handles the password step for an existing user — derives keys, sets up infrastructure. */
|
|
31843
|
-
async
|
|
31923
|
+
async unlockExisting(password) {
|
|
31844
31924
|
if (this.currentUMPToken == null) throw new Error("Provide presentation or recovery key first.");
|
|
31845
31925
|
const derivedPasswordKey = await derivePasswordKey(this.currentUMPToken, Utils.toArray(password, "utf8"));
|
|
31846
31926
|
let rootPrimaryKey;
|
|
@@ -31853,11 +31933,11 @@ var CWIStyleWalletManager = class {
|
|
|
31853
31933
|
rootPrimaryKey = new SymmetricKey(this.XOR(this.recoveryKey, derivedPasswordKey)).decrypt(this.currentUMPToken.passwordRecoveryPrimary);
|
|
31854
31934
|
rootPrivilegedKey = new SymmetricKey(this.XOR(rootPrimaryKey, derivedPasswordKey)).decrypt(this.currentUMPToken.passwordPrimaryPrivileged);
|
|
31855
31935
|
}
|
|
31856
|
-
await this.
|
|
31936
|
+
await this.setupRoot(rootPrimaryKey, rootPrivilegedKey);
|
|
31857
31937
|
await this.switchProfile(this.activeProfileId);
|
|
31858
31938
|
}
|
|
31859
31939
|
/** Handles the password step for a new user — generates keys, builds UMP token, publishes on-chain. */
|
|
31860
|
-
async
|
|
31940
|
+
async createNewUser(password) {
|
|
31861
31941
|
if (this.authenticationMode !== "presentation-key-and-password") throw new Error("New-user flow requires presentation key and password mode.");
|
|
31862
31942
|
if (this.presentationKey == null) throw new Error("No presentation key provided for new-user flow.");
|
|
31863
31943
|
const recoveryKey = Random(32);
|
|
@@ -31896,14 +31976,14 @@ var CWIStyleWalletManager = class {
|
|
|
31896
31976
|
passwordKdf: this.kdfConfig
|
|
31897
31977
|
};
|
|
31898
31978
|
this.currentUMPToken = newToken;
|
|
31899
|
-
await this.
|
|
31979
|
+
await this.setupRoot(rootPrimaryKey);
|
|
31900
31980
|
await this.switchProfile(DEFAULT_PROFILE_ID);
|
|
31901
31981
|
if (this.newWalletFunder != null && this.underlying != null) try {
|
|
31902
31982
|
await this.newWalletFunder(this.presentationKey, this.underlying, this.adminOriginator);
|
|
31903
31983
|
} catch (error) {
|
|
31904
31984
|
this.telemetry.capture({
|
|
31905
31985
|
name: "wallet-toolbox.authentication.new-wallet-funding.failed",
|
|
31906
|
-
component:
|
|
31986
|
+
component: CWI_COMPONENT,
|
|
31907
31987
|
severity: "error",
|
|
31908
31988
|
error: /* @__PURE__ */ new Error("New wallet funding failed.")
|
|
31909
31989
|
});
|
|
@@ -31934,7 +32014,7 @@ var CWIStyleWalletManager = class {
|
|
|
31934
32014
|
const xorKey = this.XOR(this.presentationKey, recoveryKey);
|
|
31935
32015
|
const rootPrimaryKey = new SymmetricKey(xorKey).decrypt(this.currentUMPToken.presentationRecoveryPrimary);
|
|
31936
32016
|
const rootPrivilegedKey = new SymmetricKey(xorKey).decrypt(this.currentUMPToken.presentationRecoveryPrivileged);
|
|
31937
|
-
await this.
|
|
32017
|
+
await this.setupRoot(rootPrimaryKey, rootPrivilegedKey);
|
|
31938
32018
|
await this.switchProfile(this.activeProfileId);
|
|
31939
32019
|
}
|
|
31940
32020
|
}
|
|
@@ -31965,7 +32045,7 @@ var CWIStyleWalletManager = class {
|
|
|
31965
32045
|
if (snapshot.length > 16777216) throw new Error("Snapshot exceeds the maximum supported size.");
|
|
31966
32046
|
this.telemetry.capture({
|
|
31967
32047
|
name: "wallet-toolbox.snapshot.saved",
|
|
31968
|
-
component:
|
|
32048
|
+
component: CWI_COMPONENT,
|
|
31969
32049
|
severity: "info",
|
|
31970
32050
|
attributes: {
|
|
31971
32051
|
formatVersion: 2,
|
|
@@ -32003,12 +32083,12 @@ var CWIStyleWalletManager = class {
|
|
|
32003
32083
|
const tokenBytes = payloadReader.read(tokenLen);
|
|
32004
32084
|
const token = this.deserializeUMPToken(tokenBytes);
|
|
32005
32085
|
this.currentUMPToken = token;
|
|
32006
|
-
await this.
|
|
32086
|
+
await this.setupRoot(rootPrimaryKey);
|
|
32007
32087
|
await this.switchProfile(activeProfileId);
|
|
32008
32088
|
this.authenticationFlow = "existing-user";
|
|
32009
32089
|
this.telemetry.capture({
|
|
32010
32090
|
name: "wallet-toolbox.snapshot.loaded",
|
|
32011
|
-
component:
|
|
32091
|
+
component: CWI_COMPONENT,
|
|
32012
32092
|
severity: "info",
|
|
32013
32093
|
attributes: {
|
|
32014
32094
|
formatVersion: version,
|
|
@@ -32019,7 +32099,7 @@ var CWIStyleWalletManager = class {
|
|
|
32019
32099
|
this.destroy();
|
|
32020
32100
|
this.telemetry.capture({
|
|
32021
32101
|
name: "wallet-toolbox.snapshot.load-failed",
|
|
32022
|
-
component:
|
|
32102
|
+
component: CWI_COMPONENT,
|
|
32023
32103
|
severity: "error",
|
|
32024
32104
|
error
|
|
32025
32105
|
});
|
|
@@ -32036,7 +32116,7 @@ var CWIStyleWalletManager = class {
|
|
|
32036
32116
|
if (refreshed == null) return false;
|
|
32037
32117
|
if (refreshed.currentOutpoint && currentToken.currentOutpoint && refreshed.currentOutpoint === currentToken.currentOutpoint) return false;
|
|
32038
32118
|
this.currentUMPToken = refreshed;
|
|
32039
|
-
await this.
|
|
32119
|
+
await this.setupRoot(this.rootPrimaryKey);
|
|
32040
32120
|
this.saveSnapshot();
|
|
32041
32121
|
return true;
|
|
32042
32122
|
}
|
|
@@ -32095,7 +32175,7 @@ var CWIStyleWalletManager = class {
|
|
|
32095
32175
|
createdAt: Math.floor(Date.now() / 1e3)
|
|
32096
32176
|
};
|
|
32097
32177
|
this.profiles.push(newProfile);
|
|
32098
|
-
await this.
|
|
32178
|
+
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);
|
|
32099
32179
|
return newProfile.id;
|
|
32100
32180
|
}
|
|
32101
32181
|
/**
|
|
@@ -32112,7 +32192,7 @@ var CWIStyleWalletManager = class {
|
|
|
32112
32192
|
if (profileIndex === -1) throw new Error("Profile not found.");
|
|
32113
32193
|
this.profiles.splice(profileIndex, 1);
|
|
32114
32194
|
if (this.activeProfileId.every((x, i) => x === profileId[i])) await this.switchProfile(DEFAULT_PROFILE_ID);
|
|
32115
|
-
await this.
|
|
32195
|
+
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);
|
|
32116
32196
|
}
|
|
32117
32197
|
/**
|
|
32118
32198
|
* Switches the active profile. This re-derives keys and rebuilds the underlying wallet.
|
|
@@ -32153,14 +32233,14 @@ var CWIStyleWalletManager = class {
|
|
|
32153
32233
|
const recoveryKey = await this.getFactor("recoveryKey");
|
|
32154
32234
|
const presentationKey = await this.getFactor("presentationKey");
|
|
32155
32235
|
const rootPrivilegedKey = await this.getFactor("privilegedKey");
|
|
32156
|
-
await this.
|
|
32236
|
+
await this.updateFactors(passwordSalt, newPasswordKey, presentationKey, recoveryKey, this.rootPrimaryKey, rootPrivilegedKey, this.profiles);
|
|
32157
32237
|
}
|
|
32158
32238
|
/**
|
|
32159
32239
|
* Retrieves the current recovery key. Requires privileged access.
|
|
32160
32240
|
*/
|
|
32161
32241
|
async getRecoveryKey() {
|
|
32162
32242
|
if (!this.authenticated || this.currentUMPToken == null || this.rootPrivilegedKeyManager == null) throw new Error("Not authenticated or missing required data.");
|
|
32163
|
-
return
|
|
32243
|
+
return this.getFactor("recoveryKey");
|
|
32164
32244
|
}
|
|
32165
32245
|
/**
|
|
32166
32246
|
* Changes the user's recovery key. Prompts user to save the new key.
|
|
@@ -32172,7 +32252,7 @@ var CWIStyleWalletManager = class {
|
|
|
32172
32252
|
const rootPrivilegedKey = await this.getFactor("privilegedKey");
|
|
32173
32253
|
const newRecoveryKey = Random(32);
|
|
32174
32254
|
await this.recoveryKeySaver(newRecoveryKey);
|
|
32175
|
-
await this.
|
|
32255
|
+
await this.updateFactors(this.currentUMPToken.passwordSalt, passwordKey, presentationKey, newRecoveryKey, this.rootPrimaryKey, rootPrivilegedKey, this.profiles);
|
|
32176
32256
|
}
|
|
32177
32257
|
/**
|
|
32178
32258
|
* Changes the user's presentation key.
|
|
@@ -32183,7 +32263,7 @@ var CWIStyleWalletManager = class {
|
|
|
32183
32263
|
const recoveryKey = await this.getFactor("recoveryKey");
|
|
32184
32264
|
const passwordKey = await this.getFactor("passwordKey");
|
|
32185
32265
|
const rootPrivilegedKey = await this.getFactor("privilegedKey");
|
|
32186
|
-
await this.
|
|
32266
|
+
await this.updateFactors(this.currentUMPToken.passwordSalt, passwordKey, newPresentationKey, recoveryKey, this.rootPrimaryKey, rootPrivilegedKey, this.profiles);
|
|
32187
32267
|
if (this.presentationKey != null) this.presentationKey = newPresentationKey;
|
|
32188
32268
|
}
|
|
32189
32269
|
/**
|
|
@@ -32229,7 +32309,7 @@ var CWIStyleWalletManager = class {
|
|
|
32229
32309
|
} catch (error) {
|
|
32230
32310
|
this.telemetry.capture({
|
|
32231
32311
|
name: "wallet-toolbox.authentication.factor-decryption.failed",
|
|
32232
|
-
component:
|
|
32312
|
+
component: CWI_COMPONENT,
|
|
32233
32313
|
severity: "error",
|
|
32234
32314
|
attributes: { factor: factorName },
|
|
32235
32315
|
error
|
|
@@ -32242,7 +32322,7 @@ var CWIStyleWalletManager = class {
|
|
|
32242
32322
|
* Recomputes UMP token fields with updated factors and profiles, then publishes the update.
|
|
32243
32323
|
* This operation requires the *root* privileged key and the *default* profile wallet.
|
|
32244
32324
|
*/
|
|
32245
|
-
async
|
|
32325
|
+
async updateFactors(passwordSalt, passwordKey, presentationKey, recoveryKey, rootPrimaryKey, rootPrivilegedKey, profiles) {
|
|
32246
32326
|
if (!this.authenticated || this.rootPrimaryKey == null || this.currentUMPToken == null) throw new Error("Wallet is not properly authenticated or missing data for update.");
|
|
32247
32327
|
const oldTokenToConsume = { ...this.currentUMPToken };
|
|
32248
32328
|
if (!oldTokenToConsume.currentOutpoint) throw new Error("Cannot update UMP token: Old token has no outpoint.");
|
|
@@ -32293,7 +32373,7 @@ var CWIStyleWalletManager = class {
|
|
|
32293
32373
|
if (!currentActiveId.every((x) => x === 0)) {
|
|
32294
32374
|
this.telemetry.capture({
|
|
32295
32375
|
name: "wallet-toolbox.ump.profile-switch.started",
|
|
32296
|
-
component:
|
|
32376
|
+
component: CWI_COMPONENT,
|
|
32297
32377
|
severity: "debug",
|
|
32298
32378
|
attributes: { reason: "token-update" }
|
|
32299
32379
|
});
|
|
@@ -32309,7 +32389,7 @@ var CWIStyleWalletManager = class {
|
|
|
32309
32389
|
await this.switchProfile(currentActiveId);
|
|
32310
32390
|
this.telemetry.capture({
|
|
32311
32391
|
name: "wallet-toolbox.ump.profile-switch.completed",
|
|
32312
|
-
component:
|
|
32392
|
+
component: CWI_COMPONENT,
|
|
32313
32393
|
severity: "debug",
|
|
32314
32394
|
attributes: { reason: "token-update" }
|
|
32315
32395
|
});
|
|
@@ -32436,9 +32516,9 @@ var CWIStyleWalletManager = class {
|
|
|
32436
32516
|
* @param rootPrimaryKey The user's root primary key (32 bytes).
|
|
32437
32517
|
* @param ephemeralRootPrivilegedKey Optional root privileged key (e.g., during recovery flows).
|
|
32438
32518
|
*/
|
|
32439
|
-
async
|
|
32519
|
+
async setupRoot(rootKey, ephemeralRootPrivilegedKey) {
|
|
32440
32520
|
if (this.currentUMPToken == null) throw new Error("A UMP token must exist before setting up root infrastructure!");
|
|
32441
|
-
this.rootPrimaryKey =
|
|
32521
|
+
this.rootPrimaryKey = rootKey;
|
|
32442
32522
|
let oneTimePrivilegedKey = ephemeralRootPrivilegedKey == null ? void 0 : new PrivateKey(ephemeralRootPrivilegedKey);
|
|
32443
32523
|
this.rootPrivilegedKeyManager = new PrivilegedKeyManager(async (reason) => {
|
|
32444
32524
|
if (oneTimePrivilegedKey != null) {
|
|
@@ -32459,7 +32539,7 @@ var CWIStyleWalletManager = class {
|
|
|
32459
32539
|
});
|
|
32460
32540
|
this.profiles = [];
|
|
32461
32541
|
if (this.currentUMPToken.profilesEncrypted != null && this.currentUMPToken.profilesEncrypted.length > 0) try {
|
|
32462
|
-
const decryptedProfileBytes = new SymmetricKey(
|
|
32542
|
+
const decryptedProfileBytes = new SymmetricKey(rootKey).decrypt(this.currentUMPToken.profilesEncrypted);
|
|
32463
32543
|
const profilesJson = Utils.toUTF8(decryptedProfileBytes);
|
|
32464
32544
|
const profiles = JSON.parse(profilesJson);
|
|
32465
32545
|
if (!Array.isArray(profiles) || profiles.length > 1e3 || !profiles.every(isValidProfile)) throw new Error("Decrypted profile data is invalid or exceeds supported bounds.");
|
|
@@ -32468,7 +32548,7 @@ var CWIStyleWalletManager = class {
|
|
|
32468
32548
|
this.profiles = [];
|
|
32469
32549
|
this.telemetry.capture({
|
|
32470
32550
|
name: "wallet-toolbox.profile.load-failed",
|
|
32471
|
-
component:
|
|
32551
|
+
component: CWI_COMPONENT,
|
|
32472
32552
|
severity: "error",
|
|
32473
32553
|
error
|
|
32474
32554
|
});
|
|
@@ -32477,98 +32557,98 @@ var CWIStyleWalletManager = class {
|
|
|
32477
32557
|
}
|
|
32478
32558
|
this.authenticated = true;
|
|
32479
32559
|
}
|
|
32480
|
-
|
|
32560
|
+
assertReady(originator) {
|
|
32481
32561
|
if (!this.authenticated) throw new Error("User is not authenticated.");
|
|
32482
32562
|
if (this.underlying == null) throw new Error("Underlying wallet for the active profile is not initialized.");
|
|
32483
32563
|
if (originator === this.adminOriginator) throw new Error("External applications are not allowed to use the admin originator.");
|
|
32484
32564
|
}
|
|
32485
32565
|
async getPublicKey(args, originator) {
|
|
32486
|
-
this.
|
|
32487
|
-
return
|
|
32566
|
+
this.assertReady(originator);
|
|
32567
|
+
return this.underlying.getPublicKey(args, originator);
|
|
32488
32568
|
}
|
|
32489
32569
|
async revealCounterpartyKeyLinkage(args, originator) {
|
|
32490
|
-
this.
|
|
32491
|
-
return
|
|
32570
|
+
this.assertReady(originator);
|
|
32571
|
+
return this.underlying.revealCounterpartyKeyLinkage(args, originator);
|
|
32492
32572
|
}
|
|
32493
32573
|
async revealSpecificKeyLinkage(args, originator) {
|
|
32494
|
-
this.
|
|
32495
|
-
return
|
|
32574
|
+
this.assertReady(originator);
|
|
32575
|
+
return this.underlying.revealSpecificKeyLinkage(args, originator);
|
|
32496
32576
|
}
|
|
32497
32577
|
async encrypt(args, originator) {
|
|
32498
|
-
this.
|
|
32499
|
-
return
|
|
32578
|
+
this.assertReady(originator);
|
|
32579
|
+
return this.underlying.encrypt(args, originator);
|
|
32500
32580
|
}
|
|
32501
32581
|
async decrypt(args, originator) {
|
|
32502
|
-
this.
|
|
32503
|
-
return
|
|
32582
|
+
this.assertReady(originator);
|
|
32583
|
+
return this.underlying.decrypt(args, originator);
|
|
32504
32584
|
}
|
|
32505
32585
|
async createHmac(args, originator) {
|
|
32506
|
-
this.
|
|
32507
|
-
return
|
|
32586
|
+
this.assertReady(originator);
|
|
32587
|
+
return this.underlying.createHmac(args, originator);
|
|
32508
32588
|
}
|
|
32509
32589
|
async verifyHmac(args, originator) {
|
|
32510
|
-
this.
|
|
32511
|
-
return
|
|
32590
|
+
this.assertReady(originator);
|
|
32591
|
+
return this.underlying.verifyHmac(args, originator);
|
|
32512
32592
|
}
|
|
32513
32593
|
async createSignature(args, originator) {
|
|
32514
|
-
this.
|
|
32515
|
-
return
|
|
32594
|
+
this.assertReady(originator);
|
|
32595
|
+
return this.underlying.createSignature(args, originator);
|
|
32516
32596
|
}
|
|
32517
32597
|
async verifySignature(args, originator) {
|
|
32518
|
-
this.
|
|
32519
|
-
return
|
|
32598
|
+
this.assertReady(originator);
|
|
32599
|
+
return this.underlying.verifySignature(args, originator);
|
|
32520
32600
|
}
|
|
32521
32601
|
async createAction(args, originator) {
|
|
32522
|
-
this.
|
|
32523
|
-
return
|
|
32602
|
+
this.assertReady(originator);
|
|
32603
|
+
return this.underlying.createAction(args, originator);
|
|
32524
32604
|
}
|
|
32525
32605
|
async signAction(args, originator) {
|
|
32526
|
-
this.
|
|
32527
|
-
return
|
|
32606
|
+
this.assertReady(originator);
|
|
32607
|
+
return this.underlying.signAction(args, originator);
|
|
32528
32608
|
}
|
|
32529
32609
|
async abortAction(args, originator) {
|
|
32530
|
-
this.
|
|
32531
|
-
return
|
|
32610
|
+
this.assertReady(originator);
|
|
32611
|
+
return this.underlying.abortAction(args, originator);
|
|
32532
32612
|
}
|
|
32533
32613
|
async listActions(args, originator) {
|
|
32534
|
-
this.
|
|
32535
|
-
return
|
|
32614
|
+
this.assertReady(originator);
|
|
32615
|
+
return this.underlying.listActions(args, originator);
|
|
32536
32616
|
}
|
|
32537
32617
|
async internalizeAction(args, originator) {
|
|
32538
|
-
this.
|
|
32539
|
-
return
|
|
32618
|
+
this.assertReady(originator);
|
|
32619
|
+
return this.underlying.internalizeAction(args, originator);
|
|
32540
32620
|
}
|
|
32541
32621
|
async listOutputs(args, originator) {
|
|
32542
|
-
this.
|
|
32543
|
-
return
|
|
32622
|
+
this.assertReady(originator);
|
|
32623
|
+
return this.underlying.listOutputs(args, originator);
|
|
32544
32624
|
}
|
|
32545
32625
|
async relinquishOutput(args, originator) {
|
|
32546
|
-
this.
|
|
32547
|
-
return
|
|
32626
|
+
this.assertReady(originator);
|
|
32627
|
+
return this.underlying.relinquishOutput(args, originator);
|
|
32548
32628
|
}
|
|
32549
32629
|
async acquireCertificate(args, originator) {
|
|
32550
|
-
this.
|
|
32551
|
-
return
|
|
32630
|
+
this.assertReady(originator);
|
|
32631
|
+
return this.underlying.acquireCertificate(args, originator);
|
|
32552
32632
|
}
|
|
32553
32633
|
async listCertificates(args, originator) {
|
|
32554
|
-
this.
|
|
32555
|
-
return
|
|
32634
|
+
this.assertReady(originator);
|
|
32635
|
+
return this.underlying.listCertificates(args, originator);
|
|
32556
32636
|
}
|
|
32557
32637
|
async proveCertificate(args, originator) {
|
|
32558
|
-
this.
|
|
32559
|
-
return
|
|
32638
|
+
this.assertReady(originator);
|
|
32639
|
+
return this.underlying.proveCertificate(args, originator);
|
|
32560
32640
|
}
|
|
32561
32641
|
async relinquishCertificate(args, originator) {
|
|
32562
|
-
this.
|
|
32563
|
-
return
|
|
32642
|
+
this.assertReady(originator);
|
|
32643
|
+
return this.underlying.relinquishCertificate(args, originator);
|
|
32564
32644
|
}
|
|
32565
32645
|
async discoverByIdentityKey(args, originator) {
|
|
32566
|
-
this.
|
|
32567
|
-
return
|
|
32646
|
+
this.assertReady(originator);
|
|
32647
|
+
return this.underlying.discoverByIdentityKey(args, originator);
|
|
32568
32648
|
}
|
|
32569
32649
|
async discoverByAttributes(args, originator) {
|
|
32570
|
-
this.
|
|
32571
|
-
return
|
|
32650
|
+
this.assertReady(originator);
|
|
32651
|
+
return this.underlying.discoverByAttributes(args, originator);
|
|
32572
32652
|
}
|
|
32573
32653
|
async isAuthenticated(_, originator) {
|
|
32574
32654
|
if (!this.authenticated) throw new Error("User is not authenticated.");
|
|
@@ -32578,23 +32658,23 @@ var CWIStyleWalletManager = class {
|
|
|
32578
32658
|
async waitForAuthentication(_, originator) {
|
|
32579
32659
|
if (originator === this.adminOriginator) throw new Error("External applications are not allowed to use the admin originator.");
|
|
32580
32660
|
while (!this.authenticated || this.underlying == null) await new Promise((resolve) => setTimeout(resolve, 100));
|
|
32581
|
-
return
|
|
32661
|
+
return this.underlying.waitForAuthentication({}, originator);
|
|
32582
32662
|
}
|
|
32583
32663
|
async getHeight(_, originator) {
|
|
32584
|
-
this.
|
|
32585
|
-
return
|
|
32664
|
+
this.assertReady(originator);
|
|
32665
|
+
return this.underlying.getHeight({}, originator);
|
|
32586
32666
|
}
|
|
32587
32667
|
async getHeaderForHeight(args, originator) {
|
|
32588
|
-
this.
|
|
32589
|
-
return
|
|
32668
|
+
this.assertReady(originator);
|
|
32669
|
+
return this.underlying.getHeaderForHeight(args, originator);
|
|
32590
32670
|
}
|
|
32591
32671
|
async getNetwork(_, originator) {
|
|
32592
|
-
this.
|
|
32593
|
-
return
|
|
32672
|
+
this.assertReady(originator);
|
|
32673
|
+
return this.underlying.getNetwork({}, originator);
|
|
32594
32674
|
}
|
|
32595
32675
|
async getVersion(_, originator) {
|
|
32596
|
-
this.
|
|
32597
|
-
return
|
|
32676
|
+
this.assertReady(originator);
|
|
32677
|
+
return this.underlying.getVersion({}, originator);
|
|
32598
32678
|
}
|
|
32599
32679
|
};
|
|
32600
32680
|
//#endregion
|
|
@@ -32938,6 +33018,8 @@ const DEFAULT_MAX_RESPONSE_BYTES = 1024 * 1024;
|
|
|
32938
33018
|
const MAX_CONFIGURED_TIMEOUT_MS = 12e4;
|
|
32939
33019
|
const MAX_CONFIGURED_REQUEST_BYTES = 10 * 1024 * 1024;
|
|
32940
33020
|
const MAX_CONFIGURED_RESPONSE_BYTES = 10 * 1024 * 1024;
|
|
33021
|
+
const WAB_COMPONENT = "wallet-toolbox.wab-transport";
|
|
33022
|
+
const WAB_REQUEST_EVENT = "wallet-toolbox.wab.request.";
|
|
32941
33023
|
const defaultFetch = typeof globalThis !== "undefined" && typeof globalThis.fetch === "function" ? globalThis.fetch.bind(globalThis) : void 0;
|
|
32942
33024
|
/**
|
|
32943
33025
|
* A privacy-safe WAB transport failure. Response bodies and request payloads
|
|
@@ -32976,8 +33058,8 @@ function normalizeServerUrl(serverUrl) {
|
|
|
32976
33058
|
} catch {
|
|
32977
33059
|
throw new WABClientError("WAB_INVALID_CONFIGURATION", "WAB server URL must be an absolute URL.", false);
|
|
32978
33060
|
}
|
|
32979
|
-
if (parsed.username !== "" || parsed.password !== "" || parsed.search !== "" || parsed.hash !== "") throw new WABClientError("WAB_INVALID_CONFIGURATION", "WAB
|
|
32980
|
-
if (parsed.protocol !== "https:" && !(parsed.protocol === "http:" && isLocalHostname(parsed.hostname))) throw new WABClientError("WAB_INVALID_CONFIGURATION", "WAB
|
|
33061
|
+
if (parsed.username !== "" || parsed.password !== "" || parsed.search !== "" || parsed.hash !== "") throw new WABClientError("WAB_INVALID_CONFIGURATION", "WAB URL cannot include credentials, query, or fragment.", false);
|
|
33062
|
+
if (parsed.protocol !== "https:" && !(parsed.protocol === "http:" && isLocalHostname(parsed.hostname))) throw new WABClientError("WAB_INVALID_CONFIGURATION", "WAB URL requires HTTPS except on localhost.", false);
|
|
32981
33063
|
let pathname = parsed.pathname;
|
|
32982
33064
|
while (pathname.endsWith("/")) pathname = pathname.slice(0, -1);
|
|
32983
33065
|
return {
|
|
@@ -32987,7 +33069,7 @@ function normalizeServerUrl(serverUrl) {
|
|
|
32987
33069
|
}
|
|
32988
33070
|
function normalizePositiveInteger(value, fallback, maximum, name) {
|
|
32989
33071
|
const resolved = value ?? fallback;
|
|
32990
|
-
if (!Number.isInteger(resolved) || resolved <= 0 || resolved > maximum) throw new WABClientError("WAB_INVALID_CONFIGURATION", `${name} must be
|
|
33072
|
+
if (!Number.isInteger(resolved) || resolved <= 0 || resolved > maximum) throw new WABClientError("WAB_INVALID_CONFIGURATION", `${name} must be an integer from 1 to ${maximum}.`, false);
|
|
32991
33073
|
return resolved;
|
|
32992
33074
|
}
|
|
32993
33075
|
function assertSafePath(path) {
|
|
@@ -33012,20 +33094,20 @@ var WABTransport = class {
|
|
|
33012
33094
|
serverUrl;
|
|
33013
33095
|
serverOrigin;
|
|
33014
33096
|
telemetry;
|
|
33015
|
-
|
|
33016
|
-
|
|
33017
|
-
|
|
33018
|
-
|
|
33097
|
+
fetcher;
|
|
33098
|
+
timeout;
|
|
33099
|
+
requestLimit;
|
|
33100
|
+
responseLimit;
|
|
33019
33101
|
constructor(serverUrl, options = {}) {
|
|
33020
33102
|
const normalized = normalizeServerUrl(serverUrl);
|
|
33021
33103
|
this.serverUrl = normalized.baseUrl;
|
|
33022
33104
|
this.serverOrigin = normalized.origin;
|
|
33023
|
-
const
|
|
33024
|
-
if (typeof
|
|
33025
|
-
this.
|
|
33026
|
-
this.
|
|
33027
|
-
this.
|
|
33028
|
-
this.
|
|
33105
|
+
const fetcher = options.fetch ?? defaultFetch;
|
|
33106
|
+
if (typeof fetcher !== "function") throw new WABClientError("WAB_INVALID_CONFIGURATION", "WABClient requires a fetch implementation.", false);
|
|
33107
|
+
this.fetcher = fetcher;
|
|
33108
|
+
this.timeout = normalizePositiveInteger(options.timeoutMs, DEFAULT_TIMEOUT_MS, MAX_CONFIGURED_TIMEOUT_MS, "timeoutMs");
|
|
33109
|
+
this.requestLimit = normalizePositiveInteger(options.maxRequestBytes, DEFAULT_MAX_REQUEST_BYTES, MAX_CONFIGURED_REQUEST_BYTES, "maxRequestBytes");
|
|
33110
|
+
this.responseLimit = normalizePositiveInteger(options.maxResponseBytes, DEFAULT_MAX_RESPONSE_BYTES, MAX_CONFIGURED_RESPONSE_BYTES, "maxResponseBytes");
|
|
33029
33111
|
this.telemetry = new Telemetry(options.telemetry);
|
|
33030
33112
|
}
|
|
33031
33113
|
createCorrelationId() {
|
|
@@ -33033,38 +33115,9 @@ var WABTransport = class {
|
|
|
33033
33115
|
return isSafeCorrelationId(correlationId) ? correlationId : new Telemetry().createCorrelationId();
|
|
33034
33116
|
}
|
|
33035
33117
|
async request(path, options) {
|
|
33036
|
-
const metadata = this.createRequestMetadata(path, options);
|
|
33037
|
-
this.captureRequestStarted(metadata);
|
|
33038
|
-
const body = this.encodeRequestBody(options, metadata);
|
|
33039
|
-
const timeout = this.startRequestTimeout(metadata.errorContext);
|
|
33040
|
-
const response = await this.fetchResponse(metadata, body, timeout);
|
|
33041
|
-
const responseContext = this.createResponseContext(response, metadata);
|
|
33042
|
-
this.assertSuccessfulResponse(response, responseContext, metadata, timeout);
|
|
33043
|
-
const responseText = await this.readResponseText(response, responseContext, metadata, timeout);
|
|
33044
|
-
const parsed = this.parseResponseObject(responseText, response, responseContext, metadata);
|
|
33045
|
-
this.telemetry.capture({
|
|
33046
|
-
name: "wallet-toolbox.wab.request.completed",
|
|
33047
|
-
component: "wallet-toolbox.wab-transport",
|
|
33048
|
-
severity: "info",
|
|
33049
|
-
correlationId: metadata.correlationId,
|
|
33050
|
-
attributes: {
|
|
33051
|
-
operation: metadata.operation,
|
|
33052
|
-
method: metadata.method,
|
|
33053
|
-
route: metadata.path,
|
|
33054
|
-
serverOrigin: this.serverOrigin,
|
|
33055
|
-
status: response.status,
|
|
33056
|
-
endpointMarkerPresent: responseContext.endpointMarkerPresent,
|
|
33057
|
-
responseCorrelationMatched: responseContext.responseCorrelationMatched,
|
|
33058
|
-
responseBytes: new TextEncoder().encode(responseText).byteLength,
|
|
33059
|
-
durationMs: Date.now() - metadata.startedAt
|
|
33060
|
-
}
|
|
33061
|
-
});
|
|
33062
|
-
return parsed;
|
|
33063
|
-
}
|
|
33064
|
-
createRequestMetadata(path, options) {
|
|
33065
33118
|
assertSafePath(path);
|
|
33066
33119
|
const correlationId = options.correlationId != null && isSafeCorrelationId(options.correlationId) ? options.correlationId : this.createCorrelationId();
|
|
33067
|
-
|
|
33120
|
+
const metadata = {
|
|
33068
33121
|
method: options.method ?? "POST",
|
|
33069
33122
|
path,
|
|
33070
33123
|
operation: options.operation,
|
|
@@ -33076,46 +33129,73 @@ var WABTransport = class {
|
|
|
33076
33129
|
route: path
|
|
33077
33130
|
}
|
|
33078
33131
|
};
|
|
33079
|
-
}
|
|
33080
|
-
captureRequestStarted(metadata) {
|
|
33081
33132
|
this.telemetry.capture({
|
|
33082
|
-
name:
|
|
33083
|
-
component:
|
|
33133
|
+
name: `${WAB_REQUEST_EVENT}started`,
|
|
33134
|
+
component: WAB_COMPONENT,
|
|
33084
33135
|
severity: "debug",
|
|
33136
|
+
correlationId,
|
|
33137
|
+
attributes: {
|
|
33138
|
+
operation: metadata.operation,
|
|
33139
|
+
method: metadata.method,
|
|
33140
|
+
route: path,
|
|
33141
|
+
serverOrigin: this.serverOrigin
|
|
33142
|
+
}
|
|
33143
|
+
});
|
|
33144
|
+
const body = this.bodyFor(options, metadata);
|
|
33145
|
+
const timeout = this.startTimer(metadata.errorContext);
|
|
33146
|
+
const response = await this.fetch(metadata, body, timeout);
|
|
33147
|
+
const responseContext = {
|
|
33148
|
+
...metadata.errorContext,
|
|
33149
|
+
endpointMarkerPresent: isWabResponse(response),
|
|
33150
|
+
responseCorrelationMatched: response.headers.get("X-Correlation-ID") === correlationId
|
|
33151
|
+
};
|
|
33152
|
+
this.checkResponse(response, responseContext, metadata, timeout);
|
|
33153
|
+
const responseText = await this.readText(response, responseContext, metadata, timeout);
|
|
33154
|
+
const parsed = this.parse(responseText, response, responseContext, metadata);
|
|
33155
|
+
this.telemetry.capture({
|
|
33156
|
+
name: `${WAB_REQUEST_EVENT}completed`,
|
|
33157
|
+
component: WAB_COMPONENT,
|
|
33158
|
+
severity: "info",
|
|
33085
33159
|
correlationId: metadata.correlationId,
|
|
33086
33160
|
attributes: {
|
|
33087
33161
|
operation: metadata.operation,
|
|
33088
33162
|
method: metadata.method,
|
|
33089
33163
|
route: metadata.path,
|
|
33090
|
-
serverOrigin: this.serverOrigin
|
|
33164
|
+
serverOrigin: this.serverOrigin,
|
|
33165
|
+
status: response.status,
|
|
33166
|
+
endpointMarkerPresent: responseContext.endpointMarkerPresent,
|
|
33167
|
+
responseCorrelationMatched: responseContext.responseCorrelationMatched,
|
|
33168
|
+
responseBytes: new TextEncoder().encode(responseText).byteLength,
|
|
33169
|
+
durationMs: Date.now() - metadata.startedAt
|
|
33091
33170
|
}
|
|
33092
33171
|
});
|
|
33172
|
+
return parsed;
|
|
33093
33173
|
}
|
|
33094
|
-
|
|
33174
|
+
bodyFor(options, metadata) {
|
|
33095
33175
|
let body;
|
|
33096
33176
|
try {
|
|
33097
33177
|
body = options.body === void 0 ? void 0 : JSON.stringify(options.body);
|
|
33098
33178
|
} catch (cause) {
|
|
33099
|
-
const error = new WABClientError("WAB_INVALID_REQUEST", "WAB request
|
|
33179
|
+
const error = new WABClientError("WAB_INVALID_REQUEST", "WAB request encoding failed.", false, void 0, {
|
|
33100
33180
|
...metadata.errorContext,
|
|
33101
33181
|
cause
|
|
33102
33182
|
});
|
|
33103
|
-
this.
|
|
33183
|
+
this.report(metadata, error);
|
|
33104
33184
|
throw error;
|
|
33105
33185
|
}
|
|
33106
33186
|
if (options.body !== void 0 && body === void 0) {
|
|
33107
|
-
const error = new WABClientError("WAB_INVALID_REQUEST", "WAB request
|
|
33108
|
-
this.
|
|
33187
|
+
const error = new WABClientError("WAB_INVALID_REQUEST", "WAB request is not JSON-serializable.", false, void 0, metadata.errorContext);
|
|
33188
|
+
this.report(metadata, error);
|
|
33109
33189
|
throw error;
|
|
33110
33190
|
}
|
|
33111
|
-
if (body != null && new TextEncoder().encode(body).byteLength > this.
|
|
33112
|
-
const error = new WABClientError("WAB_REQUEST_TOO_LARGE", "WAB request
|
|
33113
|
-
this.
|
|
33191
|
+
if (body != null && new TextEncoder().encode(body).byteLength > this.requestLimit) {
|
|
33192
|
+
const error = new WABClientError("WAB_REQUEST_TOO_LARGE", "WAB request exceeds its size limit.", false, void 0, metadata.errorContext);
|
|
33193
|
+
this.report(metadata, error);
|
|
33114
33194
|
throw error;
|
|
33115
33195
|
}
|
|
33116
33196
|
return body;
|
|
33117
33197
|
}
|
|
33118
|
-
|
|
33198
|
+
startTimer(errorContext) {
|
|
33119
33199
|
const timeout = {
|
|
33120
33200
|
controller: new AbortController(),
|
|
33121
33201
|
timedOut: false
|
|
@@ -33125,12 +33205,12 @@ var WABTransport = class {
|
|
|
33125
33205
|
timeout.timedOut = true;
|
|
33126
33206
|
timeout.controller.abort();
|
|
33127
33207
|
reject(new WABClientError("WAB_TIMEOUT", "WAB request timed out.", true, void 0, errorContext));
|
|
33128
|
-
}, this.
|
|
33208
|
+
}, this.timeout);
|
|
33129
33209
|
});
|
|
33130
33210
|
return timeout;
|
|
33131
33211
|
}
|
|
33132
|
-
async
|
|
33133
|
-
const requestPromise = Promise.resolve().then(() => this.
|
|
33212
|
+
async fetch(metadata, body, timeout) {
|
|
33213
|
+
const requestPromise = Promise.resolve().then(() => this.fetcher(`${this.serverUrl}${metadata.path}`, {
|
|
33134
33214
|
method: metadata.method,
|
|
33135
33215
|
headers: {
|
|
33136
33216
|
Accept: "application/json",
|
|
@@ -33154,33 +33234,26 @@ var WABTransport = class {
|
|
|
33154
33234
|
...metadata.errorContext,
|
|
33155
33235
|
cause
|
|
33156
33236
|
});
|
|
33157
|
-
else error = new WABClientError("WAB_NETWORK_ERROR", "WAB request failed before
|
|
33237
|
+
else error = new WABClientError("WAB_NETWORK_ERROR", "WAB request failed before response.", true, void 0, {
|
|
33158
33238
|
...metadata.errorContext,
|
|
33159
33239
|
cause
|
|
33160
33240
|
});
|
|
33161
|
-
this.
|
|
33241
|
+
this.report(metadata, error);
|
|
33162
33242
|
throw error;
|
|
33163
33243
|
}
|
|
33164
33244
|
}
|
|
33165
|
-
|
|
33166
|
-
return {
|
|
33167
|
-
...metadata.errorContext,
|
|
33168
|
-
endpointMarkerPresent: isWabResponse(response),
|
|
33169
|
-
responseCorrelationMatched: response.headers.get("X-Correlation-ID") === metadata.correlationId
|
|
33170
|
-
};
|
|
33171
|
-
}
|
|
33172
|
-
assertSuccessfulResponse(response, responseContext, metadata, timeout) {
|
|
33245
|
+
checkResponse(response, responseContext, metadata, timeout) {
|
|
33173
33246
|
if (response.ok) return;
|
|
33174
33247
|
if (timeout.timer !== void 0) clearTimeout(timeout.timer);
|
|
33175
33248
|
const endpointMismatch = response.status === 404 && responseContext.endpointMarkerPresent !== true;
|
|
33176
|
-
const error = new WABClientError(endpointMismatch ? "WAB_ENDPOINT_MISMATCH" : "WAB_HTTP_ERROR", endpointMismatch ? "
|
|
33177
|
-
this.
|
|
33249
|
+
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);
|
|
33250
|
+
this.report(metadata, error);
|
|
33178
33251
|
response.body?.cancel().catch(() => {});
|
|
33179
33252
|
throw error;
|
|
33180
33253
|
}
|
|
33181
|
-
async
|
|
33254
|
+
async readText(response, responseContext, metadata, timeout) {
|
|
33182
33255
|
try {
|
|
33183
|
-
return await Promise.race([this.
|
|
33256
|
+
return await Promise.race([this.read(response, responseContext), timeout.promise]);
|
|
33184
33257
|
} catch (cause) {
|
|
33185
33258
|
let error;
|
|
33186
33259
|
if (cause instanceof WABClientError) error = cause;
|
|
@@ -33188,17 +33261,17 @@ var WABTransport = class {
|
|
|
33188
33261
|
...responseContext,
|
|
33189
33262
|
cause
|
|
33190
33263
|
});
|
|
33191
|
-
else error = new WABClientError("WAB_INVALID_RESPONSE", "WAB response
|
|
33264
|
+
else error = new WABClientError("WAB_INVALID_RESPONSE", "WAB response read failed.", true, response.status, {
|
|
33192
33265
|
...responseContext,
|
|
33193
33266
|
cause
|
|
33194
33267
|
});
|
|
33195
|
-
this.
|
|
33268
|
+
this.report(metadata, error);
|
|
33196
33269
|
throw error;
|
|
33197
33270
|
} finally {
|
|
33198
33271
|
if (timeout.timer !== void 0) clearTimeout(timeout.timer);
|
|
33199
33272
|
}
|
|
33200
33273
|
}
|
|
33201
|
-
|
|
33274
|
+
parse(responseText, response, responseContext, metadata) {
|
|
33202
33275
|
let parsed;
|
|
33203
33276
|
try {
|
|
33204
33277
|
parsed = JSON.parse(responseText);
|
|
@@ -33207,37 +33280,32 @@ var WABTransport = class {
|
|
|
33207
33280
|
...responseContext,
|
|
33208
33281
|
cause
|
|
33209
33282
|
});
|
|
33210
|
-
this.
|
|
33283
|
+
this.report(metadata, error);
|
|
33211
33284
|
throw error;
|
|
33212
33285
|
}
|
|
33213
33286
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
33214
33287
|
const error = new WABClientError("WAB_INVALID_RESPONSE", "WAB response must be a JSON object.", true, response.status, responseContext);
|
|
33215
|
-
this.
|
|
33288
|
+
this.report(metadata, error);
|
|
33216
33289
|
throw error;
|
|
33217
33290
|
}
|
|
33218
33291
|
return parsed;
|
|
33219
33292
|
}
|
|
33220
|
-
|
|
33221
|
-
this.captureFailure(metadata.operation, metadata.method, metadata.path, metadata.correlationId, metadata.startedAt, error);
|
|
33222
|
-
}
|
|
33223
|
-
async readBoundedResponse(response, responseContext) {
|
|
33224
|
-
await this.rejectOversizedDeclaredResponse(response, responseContext);
|
|
33225
|
-
const reader = response.body?.getReader();
|
|
33226
|
-
if (reader == null) return await this.readBoundedArrayBuffer(response, responseContext);
|
|
33227
|
-
return await this.readBoundedStream(reader, response, responseContext);
|
|
33228
|
-
}
|
|
33229
|
-
async rejectOversizedDeclaredResponse(response, responseContext) {
|
|
33293
|
+
async read(response, responseContext) {
|
|
33230
33294
|
const contentLength = Number(response.headers.get("content-length"));
|
|
33231
|
-
if (
|
|
33232
|
-
|
|
33233
|
-
|
|
33295
|
+
if (Number.isFinite(contentLength) && contentLength > this.responseLimit) {
|
|
33296
|
+
await this.stopBody(response);
|
|
33297
|
+
throw this.sizeError(response, responseContext);
|
|
33298
|
+
}
|
|
33299
|
+
const reader = response.body?.getReader();
|
|
33300
|
+
if (reader == null) return this.readBuffer(response, responseContext);
|
|
33301
|
+
return this.readStream(reader, response, responseContext);
|
|
33234
33302
|
}
|
|
33235
|
-
async
|
|
33303
|
+
async readBuffer(response, responseContext) {
|
|
33236
33304
|
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
33237
|
-
if (bytes.byteLength > this.
|
|
33305
|
+
if (bytes.byteLength > this.responseLimit) throw this.sizeError(response, responseContext);
|
|
33238
33306
|
return new TextDecoder().decode(bytes);
|
|
33239
33307
|
}
|
|
33240
|
-
async
|
|
33308
|
+
async readStream(reader, response, responseContext) {
|
|
33241
33309
|
const chunks = [];
|
|
33242
33310
|
let total = 0;
|
|
33243
33311
|
while (true) {
|
|
@@ -33245,15 +33313,12 @@ var WABTransport = class {
|
|
|
33245
33313
|
if (done) break;
|
|
33246
33314
|
if (value == null) continue;
|
|
33247
33315
|
total += value.byteLength;
|
|
33248
|
-
if (total > this.
|
|
33249
|
-
await this.
|
|
33250
|
-
throw this.
|
|
33316
|
+
if (total > this.responseLimit) {
|
|
33317
|
+
await this.stopReader(reader);
|
|
33318
|
+
throw this.sizeError(response, responseContext);
|
|
33251
33319
|
}
|
|
33252
33320
|
chunks.push(value);
|
|
33253
33321
|
}
|
|
33254
|
-
return this.decodeChunks(chunks, total);
|
|
33255
|
-
}
|
|
33256
|
-
decodeChunks(chunks, total) {
|
|
33257
33322
|
const bytes = new Uint8Array(total);
|
|
33258
33323
|
let offset = 0;
|
|
33259
33324
|
for (const chunk of chunks) {
|
|
@@ -33262,35 +33327,35 @@ var WABTransport = class {
|
|
|
33262
33327
|
}
|
|
33263
33328
|
return new TextDecoder().decode(bytes);
|
|
33264
33329
|
}
|
|
33265
|
-
|
|
33266
|
-
return new WABClientError("WAB_RESPONSE_TOO_LARGE", "WAB response
|
|
33330
|
+
sizeError(response, responseContext) {
|
|
33331
|
+
return new WABClientError("WAB_RESPONSE_TOO_LARGE", "WAB response exceeds its size limit.", false, response.status, responseContext);
|
|
33267
33332
|
}
|
|
33268
|
-
async
|
|
33333
|
+
async stopBody(response) {
|
|
33269
33334
|
try {
|
|
33270
33335
|
await response.body?.cancel();
|
|
33271
33336
|
} catch {}
|
|
33272
33337
|
}
|
|
33273
|
-
async
|
|
33338
|
+
async stopReader(reader) {
|
|
33274
33339
|
try {
|
|
33275
33340
|
await reader.cancel();
|
|
33276
33341
|
} catch {}
|
|
33277
33342
|
}
|
|
33278
|
-
|
|
33343
|
+
report(metadata, error) {
|
|
33279
33344
|
this.telemetry.capture({
|
|
33280
|
-
name:
|
|
33281
|
-
component:
|
|
33345
|
+
name: `${WAB_REQUEST_EVENT}failed`,
|
|
33346
|
+
component: WAB_COMPONENT,
|
|
33282
33347
|
severity: error.retryable ? "warn" : "error",
|
|
33283
|
-
correlationId,
|
|
33348
|
+
correlationId: metadata.correlationId,
|
|
33284
33349
|
attributes: {
|
|
33285
|
-
operation,
|
|
33286
|
-
method,
|
|
33287
|
-
route: path,
|
|
33350
|
+
operation: metadata.operation,
|
|
33351
|
+
method: metadata.method,
|
|
33352
|
+
route: metadata.path,
|
|
33288
33353
|
serverOrigin: this.serverOrigin,
|
|
33289
33354
|
retryable: error.retryable,
|
|
33290
33355
|
...error.status !== void 0 ? { status: error.status } : {},
|
|
33291
33356
|
...error.endpointMarkerPresent !== void 0 ? { endpointMarkerPresent: error.endpointMarkerPresent } : {},
|
|
33292
33357
|
...error.responseCorrelationMatched !== void 0 ? { responseCorrelationMatched: error.responseCorrelationMatched } : {},
|
|
33293
|
-
durationMs: Date.now() - startedAt
|
|
33358
|
+
durationMs: Date.now() - metadata.startedAt
|
|
33294
33359
|
},
|
|
33295
33360
|
error
|
|
33296
33361
|
});
|
|
@@ -33399,8 +33464,8 @@ var WABClient = class {
|
|
|
33399
33464
|
constructor(serverUrl, options = {}) {
|
|
33400
33465
|
this.transport = new WABTransport(serverUrl, options);
|
|
33401
33466
|
}
|
|
33402
|
-
|
|
33403
|
-
return
|
|
33467
|
+
getInfo() {
|
|
33468
|
+
return this.transport.request("/info", {
|
|
33404
33469
|
method: "GET",
|
|
33405
33470
|
operation: "get-info"
|
|
33406
33471
|
});
|
|
@@ -33410,15 +33475,15 @@ var WABClient = class {
|
|
|
33410
33475
|
}
|
|
33411
33476
|
async startAuthMethod(authMethod, presentationKey, payload, correlationId) {
|
|
33412
33477
|
assertHexIdentifier(presentationKey, "presentationKey");
|
|
33413
|
-
return
|
|
33478
|
+
return authMethod.startAuth(this.transport.serverUrl, presentationKey, payload, this.transport, correlationId);
|
|
33414
33479
|
}
|
|
33415
33480
|
async completeAuthMethod(authMethod, presentationKey, payload, correlationId) {
|
|
33416
33481
|
assertHexIdentifier(presentationKey, "presentationKey");
|
|
33417
|
-
return
|
|
33482
|
+
return authMethod.completeAuth(this.transport.serverUrl, presentationKey, payload, this.transport, correlationId);
|
|
33418
33483
|
}
|
|
33419
33484
|
async listLinkedMethods(presentationKey) {
|
|
33420
33485
|
assertHexIdentifier(presentationKey, "presentationKey");
|
|
33421
|
-
return
|
|
33486
|
+
return this.transport.request("/user/linkedMethods", {
|
|
33422
33487
|
operation: "list-linked-methods",
|
|
33423
33488
|
body: { presentationKey }
|
|
33424
33489
|
});
|
|
@@ -33426,7 +33491,7 @@ var WABClient = class {
|
|
|
33426
33491
|
async unlinkMethod(presentationKey, authMethodId) {
|
|
33427
33492
|
assertHexIdentifier(presentationKey, "presentationKey");
|
|
33428
33493
|
if (!Number.isSafeInteger(authMethodId) || authMethodId <= 0) throw new TypeError("authMethodId must be a positive safe integer.");
|
|
33429
|
-
return
|
|
33494
|
+
return this.transport.request("/user/unlinkMethod", {
|
|
33430
33495
|
operation: "unlink-method",
|
|
33431
33496
|
body: {
|
|
33432
33497
|
presentationKey,
|
|
@@ -33436,14 +33501,14 @@ var WABClient = class {
|
|
|
33436
33501
|
}
|
|
33437
33502
|
async requestFaucet(presentationKey) {
|
|
33438
33503
|
assertHexIdentifier(presentationKey, "presentationKey");
|
|
33439
|
-
return
|
|
33504
|
+
return this.transport.request("/faucet/request", {
|
|
33440
33505
|
operation: "request-faucet",
|
|
33441
33506
|
body: { presentationKey }
|
|
33442
33507
|
});
|
|
33443
33508
|
}
|
|
33444
33509
|
async deleteUser(presentationKey) {
|
|
33445
33510
|
assertHexIdentifier(presentationKey, "presentationKey");
|
|
33446
|
-
return
|
|
33511
|
+
return this.transport.request("/user/delete", {
|
|
33447
33512
|
operation: "delete-user",
|
|
33448
33513
|
body: { presentationKey }
|
|
33449
33514
|
});
|
|
@@ -33452,7 +33517,7 @@ var WABClient = class {
|
|
|
33452
33517
|
assertMethodType(methodType);
|
|
33453
33518
|
assertHexIdentifier(userIdHash, "userIdHash");
|
|
33454
33519
|
const normalizedPayload = normalizeAuthPayload(methodType, payload);
|
|
33455
|
-
return
|
|
33520
|
+
return this.transport.request("/auth/start", {
|
|
33456
33521
|
operation: "start-share-auth",
|
|
33457
33522
|
body: {
|
|
33458
33523
|
methodType,
|
|
@@ -33465,7 +33530,7 @@ var WABClient = class {
|
|
|
33465
33530
|
assertMethodType(methodType);
|
|
33466
33531
|
assertHexIdentifier(userIdHash, "userIdHash");
|
|
33467
33532
|
const normalizedPayload = normalizeAuthPayload(methodType, payload);
|
|
33468
|
-
return
|
|
33533
|
+
return this.transport.request("/share/store", {
|
|
33469
33534
|
operation: "store-share",
|
|
33470
33535
|
body: {
|
|
33471
33536
|
methodType,
|
|
@@ -33479,7 +33544,7 @@ var WABClient = class {
|
|
|
33479
33544
|
assertMethodType(methodType);
|
|
33480
33545
|
assertHexIdentifier(userIdHash, "userIdHash");
|
|
33481
33546
|
const normalizedPayload = normalizeAuthPayload(methodType, payload);
|
|
33482
|
-
return
|
|
33547
|
+
return this.transport.request("/share/retrieve", {
|
|
33483
33548
|
operation: "retrieve-share",
|
|
33484
33549
|
body: {
|
|
33485
33550
|
methodType,
|
|
@@ -33492,7 +33557,7 @@ var WABClient = class {
|
|
|
33492
33557
|
assertMethodType(methodType);
|
|
33493
33558
|
assertHexIdentifier(userIdHash, "userIdHash");
|
|
33494
33559
|
const normalizedPayload = normalizeAuthPayload(methodType, payload);
|
|
33495
|
-
return
|
|
33560
|
+
return this.transport.request("/share/update", {
|
|
33496
33561
|
operation: "update-share",
|
|
33497
33562
|
body: {
|
|
33498
33563
|
methodType,
|
|
@@ -33506,7 +33571,7 @@ var WABClient = class {
|
|
|
33506
33571
|
assertMethodType(methodType);
|
|
33507
33572
|
assertHexIdentifier(userIdHash, "userIdHash");
|
|
33508
33573
|
const normalizedPayload = normalizeAuthPayload(methodType, payload);
|
|
33509
|
-
return
|
|
33574
|
+
return this.transport.request("/share/delete", {
|
|
33510
33575
|
operation: "delete-share-user",
|
|
33511
33576
|
body: {
|
|
33512
33577
|
methodType,
|
|
@@ -33520,9 +33585,13 @@ var WABClient = class {
|
|
|
33520
33585
|
//#region ../src/WalletAuthenticationManager.ts
|
|
33521
33586
|
const DEFAULT_AUTH_SESSION_TTL_MS = 600 * 1e3;
|
|
33522
33587
|
const MAX_AUTH_SESSION_TTL_MS = 3600 * 1e3;
|
|
33588
|
+
const AUTH_COMPONENT = "wallet-toolbox.authentication-manager";
|
|
33589
|
+
const AUTH_EVENT = "wallet-toolbox.authentication.";
|
|
33590
|
+
const EXISTING_USER = "existing-user";
|
|
33591
|
+
const NEW_USER = "new-user";
|
|
33523
33592
|
var WABAccountContinuityError = class extends Error {
|
|
33524
33593
|
code = "WERR_WAB_ACCOUNT_CONTINUITY";
|
|
33525
|
-
constructor(message = "WAB and UMP
|
|
33594
|
+
constructor(message = "WAB and UMP accounts disagree; retry or recover.") {
|
|
33526
33595
|
super(message);
|
|
33527
33596
|
this.name = "WABAccountContinuityError";
|
|
33528
33597
|
}
|
|
@@ -33537,6 +33606,7 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
|
|
|
33537
33606
|
wabClient;
|
|
33538
33607
|
authMethod;
|
|
33539
33608
|
authSession;
|
|
33609
|
+
phoneChangeSession;
|
|
33540
33610
|
authSessionTtlMs;
|
|
33541
33611
|
constructor(...[adminOriginator, walletBuilder, interactor, recoveryKeySaver, passwordRetriever, wabClient, authMethod, stateSnapshot, options = {}]) {
|
|
33542
33612
|
super(adminOriginator, walletBuilder, interactor, recoveryKeySaver, passwordRetriever, async (presentationKey, wallet, adminOriginator) => {
|
|
@@ -33559,7 +33629,7 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
|
|
|
33559
33629
|
description: "Fund wallet",
|
|
33560
33630
|
options: { acceptDelayedBroadcast: false }
|
|
33561
33631
|
}, adminOriginator);
|
|
33562
|
-
if (faucetRedeemTXCreationResult.signableTransaction == null) throw new Error("Faucet redemption
|
|
33632
|
+
if (faucetRedeemTXCreationResult.signableTransaction == null) throw new Error("Faucet redemption was not signable.");
|
|
33563
33633
|
const faucetRedeemTX = Transaction.fromAtomicBEEF(faucetRedeemTXCreationResult.signableTransaction.tx);
|
|
33564
33634
|
const faucetRedemptionPuzzle = new RPuzzle();
|
|
33565
33635
|
const randomRedemptionPrivateKey = PrivateKey.fromRandom();
|
|
@@ -33592,7 +33662,7 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
|
|
|
33592
33662
|
* using the chosen AuthMethodInteractor.
|
|
33593
33663
|
*/
|
|
33594
33664
|
async startAuth(payload) {
|
|
33595
|
-
if (this.authMethod == null) throw new Error("No
|
|
33665
|
+
if (this.authMethod == null) throw new Error("No WAB authentication method selected.");
|
|
33596
33666
|
const authMethod = this.authMethod;
|
|
33597
33667
|
if (this.authenticated) throw new Error("User is already authenticated");
|
|
33598
33668
|
this.cancelAuth();
|
|
@@ -33605,8 +33675,8 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
|
|
|
33605
33675
|
...correlationId !== void 0 ? { correlationId } : {}
|
|
33606
33676
|
};
|
|
33607
33677
|
this.telemetry.capture({
|
|
33608
|
-
name:
|
|
33609
|
-
component:
|
|
33678
|
+
name: `${AUTH_EVENT}wab-start.started`,
|
|
33679
|
+
component: AUTH_COMPONENT,
|
|
33610
33680
|
severity: "debug",
|
|
33611
33681
|
correlationId,
|
|
33612
33682
|
attributes: { methodType: authMethod.methodType }
|
|
@@ -33618,8 +33688,8 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
|
|
|
33618
33688
|
throw new Error(message);
|
|
33619
33689
|
}
|
|
33620
33690
|
this.telemetry.capture({
|
|
33621
|
-
name:
|
|
33622
|
-
component:
|
|
33691
|
+
name: `${AUTH_EVENT}wab-start.completed`,
|
|
33692
|
+
component: AUTH_COMPONENT,
|
|
33623
33693
|
severity: "info",
|
|
33624
33694
|
correlationId,
|
|
33625
33695
|
attributes: { methodType: authMethod.methodType }
|
|
@@ -33627,8 +33697,8 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
|
|
|
33627
33697
|
} catch (error) {
|
|
33628
33698
|
this.cancelAuth();
|
|
33629
33699
|
this.telemetry.capture({
|
|
33630
|
-
name:
|
|
33631
|
-
component:
|
|
33700
|
+
name: `${AUTH_EVENT}wab-start.failed`,
|
|
33701
|
+
component: AUTH_COMPONENT,
|
|
33632
33702
|
severity: "warn",
|
|
33633
33703
|
correlationId,
|
|
33634
33704
|
attributes: { methodType: authMethod.methodType },
|
|
@@ -33641,22 +33711,22 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
|
|
|
33641
33711
|
* Completes the WAB-based flow, retrieving the final presentationKey from WAB if successful.
|
|
33642
33712
|
*/
|
|
33643
33713
|
async completeAuth(payload) {
|
|
33644
|
-
if (this.authMethod == null || this.authSession == null) throw new Error("
|
|
33714
|
+
if (this.authMethod == null || this.authSession == null) throw new Error("Start WAB authentication first.");
|
|
33645
33715
|
const authMethod = this.authMethod;
|
|
33646
33716
|
if (this.authSession.methodType !== authMethod.methodType) {
|
|
33647
33717
|
this.cancelAuth();
|
|
33648
|
-
throw new Error("
|
|
33718
|
+
throw new Error("WAB authentication method changed; restart.");
|
|
33649
33719
|
}
|
|
33650
33720
|
if (Date.now() >= this.authSession.expiresAt) {
|
|
33651
33721
|
this.cancelAuth();
|
|
33652
|
-
throw new Error("
|
|
33722
|
+
throw new Error("WAB authentication expired; restart.");
|
|
33653
33723
|
}
|
|
33654
33724
|
const session = this.authSession;
|
|
33655
33725
|
const result = await this.wabClient.completeAuthMethod(authMethod, session.presentationKey, payload, session.correlationId);
|
|
33656
33726
|
if (result.success !== true || result.presentationKey == null || result.presentationKey.length === 0) {
|
|
33657
33727
|
this.telemetry.capture({
|
|
33658
|
-
name:
|
|
33659
|
-
component:
|
|
33728
|
+
name: `${AUTH_EVENT}wab-complete.rejected`,
|
|
33729
|
+
component: AUTH_COMPONENT,
|
|
33660
33730
|
severity: "warn",
|
|
33661
33731
|
correlationId: session.correlationId,
|
|
33662
33732
|
attributes: { methodType: session.methodType }
|
|
@@ -33671,11 +33741,11 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
|
|
|
33671
33741
|
this.cancelAuth();
|
|
33672
33742
|
const wabAccountStatus = this.inferAccountStatus(result, session.presentationKey);
|
|
33673
33743
|
try {
|
|
33674
|
-
await this.
|
|
33744
|
+
await this.provideWABPresentationKey(result, wabAccountStatus);
|
|
33675
33745
|
} catch (error) {
|
|
33676
33746
|
this.telemetry.capture({
|
|
33677
|
-
name:
|
|
33678
|
-
component:
|
|
33747
|
+
name: `${AUTH_EVENT}ump-continuity.failed`,
|
|
33748
|
+
component: AUTH_COMPONENT,
|
|
33679
33749
|
severity: "warn",
|
|
33680
33750
|
correlationId: session.correlationId,
|
|
33681
33751
|
attributes: {
|
|
@@ -33686,18 +33756,18 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
|
|
|
33686
33756
|
});
|
|
33687
33757
|
throw error;
|
|
33688
33758
|
}
|
|
33689
|
-
if (wabAccountStatus ===
|
|
33759
|
+
if (wabAccountStatus === EXISTING_USER && this.authenticationFlow !== EXISTING_USER) {
|
|
33690
33760
|
super.destroy();
|
|
33691
33761
|
const error = new WABAccountContinuityError();
|
|
33692
33762
|
this.telemetry.capture({
|
|
33693
|
-
name:
|
|
33694
|
-
component:
|
|
33763
|
+
name: `${AUTH_EVENT}account-continuity.mismatch`,
|
|
33764
|
+
component: AUTH_COMPONENT,
|
|
33695
33765
|
severity: "error",
|
|
33696
33766
|
correlationId: session.correlationId,
|
|
33697
33767
|
attributes: {
|
|
33698
33768
|
methodType: session.methodType,
|
|
33699
33769
|
wabAccountStatus,
|
|
33700
|
-
umpAccountStatus:
|
|
33770
|
+
umpAccountStatus: NEW_USER
|
|
33701
33771
|
},
|
|
33702
33772
|
error
|
|
33703
33773
|
});
|
|
@@ -33705,8 +33775,8 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
|
|
|
33705
33775
|
}
|
|
33706
33776
|
const continuity = wabAccountStatus === this.authenticationFlow ? "matched" : "ump-existing";
|
|
33707
33777
|
this.telemetry.capture({
|
|
33708
|
-
name:
|
|
33709
|
-
component:
|
|
33778
|
+
name: `${AUTH_EVENT}completed`,
|
|
33779
|
+
component: AUTH_COMPONENT,
|
|
33710
33780
|
severity: continuity === "matched" ? "info" : "warn",
|
|
33711
33781
|
correlationId: session.correlationId,
|
|
33712
33782
|
attributes: {
|
|
@@ -33720,23 +33790,131 @@ var WalletAuthenticationManager = class extends CWIStyleWalletManager {
|
|
|
33720
33790
|
cancelAuth() {
|
|
33721
33791
|
this.authSession = void 0;
|
|
33722
33792
|
}
|
|
33793
|
+
readPendingPhoneChange(result) {
|
|
33794
|
+
const presentationKey = result.pendingPresentationKey;
|
|
33795
|
+
const changeId = result.pendingPhoneChangeId;
|
|
33796
|
+
if (presentationKey === void 0 && changeId === void 0) return void 0;
|
|
33797
|
+
if (!/^[0-9a-fA-F]{64}$/.test(presentationKey ?? "") || !Number.isSafeInteger(changeId) || changeId <= 0) throw new WABAccountContinuityError("WAB returned invalid pending phone-change data.");
|
|
33798
|
+
return {
|
|
33799
|
+
presentationKey,
|
|
33800
|
+
changeId
|
|
33801
|
+
};
|
|
33802
|
+
}
|
|
33803
|
+
async provideWABPresentationKey(result, wabAccountStatus) {
|
|
33804
|
+
const umpTokenOutpoint = typeof result.umpTokenOutpoint === "string" ? result.umpTokenOutpoint : void 0;
|
|
33805
|
+
const lookupOptions = umpTokenOutpoint == null ? void 0 : { pinnedOutpoint: umpTokenOutpoint };
|
|
33806
|
+
const pending = this.readPendingPhoneChange(result);
|
|
33807
|
+
let usePending = false;
|
|
33808
|
+
try {
|
|
33809
|
+
await this.providePresentationKey(Utils.toArray(result.presentationKey, "hex"), lookupOptions);
|
|
33810
|
+
} catch (error) {
|
|
33811
|
+
if (pending == null) throw error;
|
|
33812
|
+
usePending = true;
|
|
33813
|
+
}
|
|
33814
|
+
if (pending != null && (usePending || wabAccountStatus === EXISTING_USER && this.authenticationFlow !== EXISTING_USER)) {
|
|
33815
|
+
await this.providePresentationKey(Utils.toArray(pending.presentationKey, "hex"), lookupOptions);
|
|
33816
|
+
if (this.authenticationFlow === EXISTING_USER) await this.finalizePendingPhoneChange(result.presentationKey, pending);
|
|
33817
|
+
}
|
|
33818
|
+
}
|
|
33819
|
+
async finalizePendingPhoneChange(currentPresentationKey, pending) {
|
|
33820
|
+
const finalized = await this.phoneChange("finalize", {
|
|
33821
|
+
changeId: pending.changeId,
|
|
33822
|
+
presentationKey: currentPresentationKey,
|
|
33823
|
+
newPresentationKey: pending.presentationKey
|
|
33824
|
+
});
|
|
33825
|
+
if (finalized.success !== true || finalized.changeId !== pending.changeId) throw new WABAccountContinuityError(finalized.message || "WAB could not finalize the pending phone change.");
|
|
33826
|
+
}
|
|
33827
|
+
/**
|
|
33828
|
+
* Starts OTP verification for a replacement phone number. The same number
|
|
33829
|
+
* is valid and intentionally produces a fresh presentation key/hash.
|
|
33830
|
+
*/
|
|
33831
|
+
async startPhoneNumberChange(phoneNumber) {
|
|
33832
|
+
if (!this.authenticated) throw new Error("Not authenticated");
|
|
33833
|
+
const normalizedPhone = phoneNumber.trim();
|
|
33834
|
+
const currentPresentationKey = Utils.toHex(await this.getFactor("presentationKey"));
|
|
33835
|
+
const response = await this.phoneChange("start", {
|
|
33836
|
+
presentationKey: currentPresentationKey,
|
|
33837
|
+
phoneNumber: normalizedPhone
|
|
33838
|
+
});
|
|
33839
|
+
if (response.success !== true) throw new Error(response.message || "Phone change failed");
|
|
33840
|
+
this.phoneChangeSession = {
|
|
33841
|
+
phoneNumber: normalizedPhone,
|
|
33842
|
+
presentationKey: currentPresentationKey
|
|
33843
|
+
};
|
|
33844
|
+
}
|
|
33845
|
+
/**
|
|
33846
|
+
* Completes phone verification and stages the WAB association before
|
|
33847
|
+
* publishing the UMP key rotation. WAB retains both the current and pending
|
|
33848
|
+
* presentation keys until finalization, so either side of an interrupted
|
|
33849
|
+
* transition remains recoverable on the next verified login.
|
|
33850
|
+
*/
|
|
33851
|
+
async completePhoneNumberChange(otp) {
|
|
33852
|
+
const session = this.phoneChangeSession;
|
|
33853
|
+
if (session == null) throw new Error("No phone change");
|
|
33854
|
+
if (session.changeToken == null) {
|
|
33855
|
+
const authorization = await this.phoneChange("complete", {
|
|
33856
|
+
presentationKey: session.presentationKey,
|
|
33857
|
+
phoneNumber: session.phoneNumber,
|
|
33858
|
+
otp: otp.trim()
|
|
33859
|
+
});
|
|
33860
|
+
if (authorization.success !== true) throw new Error(authorization.message || "Phone change failed");
|
|
33861
|
+
if (/^[0-9a-fA-F]{64}$/.test(authorization.pendingPresentationKey ?? "") && Number.isSafeInteger(authorization.pendingPhoneChangeId) && authorization.pendingPhoneChangeId > 0) {
|
|
33862
|
+
session.newKey = Utils.toArray(authorization.pendingPresentationKey, "hex");
|
|
33863
|
+
session.changeId = authorization.pendingPhoneChangeId;
|
|
33864
|
+
} else if (typeof authorization.changeToken === "string" && authorization.changeToken.length > 0) session.changeToken = authorization.changeToken;
|
|
33865
|
+
else throw new Error(authorization.message || "Phone change failed");
|
|
33866
|
+
}
|
|
33867
|
+
session.newKey ??= Random(32);
|
|
33868
|
+
if (session.changeId == null) {
|
|
33869
|
+
const committed = await this.phoneChange("commit", {
|
|
33870
|
+
changeToken: session.changeToken,
|
|
33871
|
+
presentationKey: session.presentationKey,
|
|
33872
|
+
newPresentationKey: Utils.toHex(session.newKey)
|
|
33873
|
+
});
|
|
33874
|
+
if (committed.success !== true || !Number.isSafeInteger(committed.changeId) || committed.changeId <= 0) throw new Error(committed.message || "Phone change failed");
|
|
33875
|
+
session.changeId = committed.changeId;
|
|
33876
|
+
}
|
|
33877
|
+
const changeId = session.changeId;
|
|
33878
|
+
if (session.umpUpdated !== true) {
|
|
33879
|
+
await this.changePresentationKey(session.newKey);
|
|
33880
|
+
session.umpUpdated = true;
|
|
33881
|
+
}
|
|
33882
|
+
const finalized = await this.phoneChange("finalize", {
|
|
33883
|
+
changeId,
|
|
33884
|
+
presentationKey: session.presentationKey,
|
|
33885
|
+
newPresentationKey: Utils.toHex(session.newKey)
|
|
33886
|
+
});
|
|
33887
|
+
if (finalized.success !== true || finalized.changeId !== changeId) throw new Error(finalized.message || "Phone change failed");
|
|
33888
|
+
this.phoneChangeSession = void 0;
|
|
33889
|
+
return { changeId };
|
|
33890
|
+
}
|
|
33891
|
+
cancelPhoneNumberChange() {
|
|
33892
|
+
this.phoneChangeSession = void 0;
|
|
33893
|
+
}
|
|
33723
33894
|
destroy() {
|
|
33724
33895
|
this.cancelAuth();
|
|
33896
|
+
this.cancelPhoneNumberChange();
|
|
33725
33897
|
super.destroy();
|
|
33726
33898
|
}
|
|
33899
|
+
phoneChange(phase, body) {
|
|
33900
|
+
return this.wabClient.transport.request(`/auth/phone-change/${phase}`, {
|
|
33901
|
+
operation: "phone-change",
|
|
33902
|
+
body
|
|
33903
|
+
});
|
|
33904
|
+
}
|
|
33727
33905
|
inferAccountStatus(result, temporaryPresentationKey) {
|
|
33728
33906
|
if (result.presentationKey == null) throw new WABAccountContinuityError("WAB did not return a presentation key.");
|
|
33729
33907
|
const keyMatchesTemporary = this.constantTimeHexEqual(result.presentationKey, temporaryPresentationKey);
|
|
33730
33908
|
const rawAccountStatus = result.accountStatus;
|
|
33731
|
-
if (rawAccountStatus !== void 0 && rawAccountStatus !==
|
|
33909
|
+
if (rawAccountStatus !== void 0 && rawAccountStatus !== NEW_USER && rawAccountStatus !== EXISTING_USER) throw new WABAccountContinuityError("WAB returned an invalid account status.");
|
|
33732
33910
|
const rawExistingUser = result.existingUser;
|
|
33733
|
-
if (rawExistingUser !== void 0 && typeof rawExistingUser !== "boolean") throw new WABAccountContinuityError("WAB returned
|
|
33734
|
-
if (rawAccountStatus !== void 0 && rawExistingUser !== void 0 && rawAccountStatus ===
|
|
33911
|
+
if (rawExistingUser !== void 0 && typeof rawExistingUser !== "boolean") throw new WABAccountContinuityError("WAB returned invalid existing-user data.");
|
|
33912
|
+
if (rawAccountStatus !== void 0 && rawExistingUser !== void 0 && rawAccountStatus === EXISTING_USER !== rawExistingUser) throw new WABAccountContinuityError("WAB returned conflicting account status.");
|
|
33735
33913
|
let compatibilityStatus;
|
|
33736
|
-
if (typeof rawExistingUser === "boolean") compatibilityStatus = rawExistingUser ?
|
|
33914
|
+
if (typeof rawExistingUser === "boolean") compatibilityStatus = rawExistingUser ? EXISTING_USER : NEW_USER;
|
|
33737
33915
|
const explicitStatus = rawAccountStatus ?? compatibilityStatus;
|
|
33738
|
-
if (explicitStatus ===
|
|
33739
|
-
return explicitStatus ?? (keyMatchesTemporary ?
|
|
33916
|
+
if (explicitStatus === NEW_USER && !keyMatchesTemporary || explicitStatus === EXISTING_USER && keyMatchesTemporary) throw new WABAccountContinuityError("WAB returned conflicting account status.");
|
|
33917
|
+
return explicitStatus ?? (keyMatchesTemporary ? NEW_USER : EXISTING_USER);
|
|
33740
33918
|
}
|
|
33741
33919
|
constantTimeHexEqual(left, right) {
|
|
33742
33920
|
if (left.length !== right.length) return false;
|