@bsv/wallet-toolbox-client 1.8.1 → 1.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/out/src/WalletPermissionsManager.d.ts +1 -75
  2. package/out/src/WalletPermissionsManager.d.ts.map +1 -1
  3. package/out/src/WalletPermissionsManager.js +214 -1026
  4. package/out/src/WalletPermissionsManager.js.map +1 -1
  5. package/out/src/sdk/WalletServices.interfaces.d.ts.map +1 -1
  6. package/out/src/sdk/validationHelpers.d.ts +303 -0
  7. package/out/src/sdk/validationHelpers.d.ts.map +1 -0
  8. package/out/src/sdk/validationHelpers.js +632 -0
  9. package/out/src/sdk/validationHelpers.js.map +1 -0
  10. package/out/src/services/chaintracker/chaintracks/Ingest/BulkIngestorWhatsOnChainCdn.d.ts +1 -1
  11. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageBase.d.ts.map +1 -1
  12. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageBase.js +1 -4
  13. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageBase.js.map +1 -1
  14. package/out/src/services/chaintracker/chaintracks/util/validBulkHeaderFilesByFileHash.d.ts.map +1 -1
  15. package/out/src/services/chaintracker/chaintracks/util/validBulkHeaderFilesByFileHash.js +0 -12
  16. package/out/src/services/chaintracker/chaintracks/util/validBulkHeaderFilesByFileHash.js.map +1 -1
  17. package/out/src/storage/StorageProvider.d.ts +2 -16
  18. package/out/src/storage/StorageProvider.d.ts.map +1 -1
  19. package/out/src/storage/StorageProvider.js +4 -33
  20. package/out/src/storage/StorageProvider.js.map +1 -1
  21. package/out/src/storage/methods/getBeefForTransaction.js +1 -1
  22. package/out/src/storage/methods/getBeefForTransaction.js.map +1 -1
  23. package/out/src/storage/methods/internalizeAction.d.ts.map +1 -1
  24. package/out/src/storage/methods/internalizeAction.js +2 -2
  25. package/out/src/storage/methods/internalizeAction.js.map +1 -1
  26. package/out/src/wab-client/WABClient.d.ts +0 -65
  27. package/out/src/wab-client/WABClient.d.ts.map +1 -1
  28. package/out/src/wab-client/WABClient.js +0 -107
  29. package/out/src/wab-client/WABClient.js.map +1 -1
  30. package/package.json +1 -1
@@ -77,8 +77,7 @@ class WalletPermissionsManager {
77
77
  onBasketAccessRequested: [],
78
78
  onCertificateAccessRequested: [],
79
79
  onSpendingAuthorizationRequested: [],
80
- onGroupedPermissionRequested: [],
81
- onCounterpartyPermissionRequested: []
80
+ onGroupedPermissionRequested: []
82
81
  };
83
82
  /**
84
83
  * We queue parallel requests for the same resource so that only one
@@ -94,9 +93,6 @@ class WalletPermissionsManager {
94
93
  /** Cache recently confirmed permissions to avoid repeated lookups. */
95
94
  this.permissionCache = new Map();
96
95
  this.recentGrants = new Map();
97
- this.manifestCache = new Map();
98
- this.manifestFetchInProgress = new Map();
99
- this.pactEstablishedCache = new Map();
100
96
  this.underlying = underlyingWallet;
101
97
  this.adminOriginator = this.normalizeOriginator(adminOriginator) || adminOriginator;
102
98
  // Default all config options to true unless specified
@@ -163,68 +159,6 @@ class WalletPermissionsManager {
163
159
  originator
164
160
  });
165
161
  }
166
- /**
167
- * Adds a permission module for the given schemeID if needed, throwing if unsupported.
168
- */
169
- addPModuleByScheme(schemeID, kind, pModulesByScheme) {
170
- var _a;
171
- if (pModulesByScheme.has(schemeID))
172
- return;
173
- const module = (_a = this.config.permissionModules) === null || _a === void 0 ? void 0 : _a[schemeID];
174
- if (!module) {
175
- throw new Error(`Unsupported P-${kind} scheme: p ${schemeID}`);
176
- }
177
- pModulesByScheme.set(schemeID, module);
178
- }
179
- /**
180
- * Splits labels into P and non-P lists, registering any P-modules encountered.
181
- *
182
- * P-labels follow BRC-111 format: `p <moduleId> <payload>`
183
- * - Must start with "p " (lowercase p + space)
184
- * - Module ID must be at least 1 character with no spaces
185
- * - Single space separates module ID from payload
186
- * - Payload must be at least 1 character
187
- *
188
- * @example Valid: "p btms token123", "p invoicing invoice 2026-02-02"
189
- * @example Invalid: "p btms" (no payload), "p btms " (empty payload), "p data" (empty moduleId)
190
- *
191
- * @param labels - Array of label strings to process
192
- * @param pModulesByScheme - Map to populate with discovered p-modules
193
- * @returns Array of non-P labels for normal permission checks
194
- * @throws Error if p-label format is invalid or module is unsupported
195
- */
196
- splitLabelsByPermissionModule(labels, pModulesByScheme) {
197
- const nonPLabels = [];
198
- if (!labels)
199
- return nonPLabels;
200
- for (const label of labels) {
201
- if (label.startsWith('p ')) {
202
- // Remove "p " prefix to get "moduleId payload"
203
- const remainder = label.slice(2);
204
- // Find the space that separates moduleId from payload
205
- const separatorIndex = remainder.indexOf(' ');
206
- // Validate: must have a space (separatorIndex > 0) and payload after it
207
- // separatorIndex <= 0 means no space found or moduleId is empty
208
- // separatorIndex === remainder.length - 1 means space is last char (no payload)
209
- if (separatorIndex <= 0 || separatorIndex === remainder.length - 1) {
210
- throw new Error(`Invalid P-label format: ${label}`);
211
- }
212
- // Reject double spaces after moduleId (payload can't start with space)
213
- if (remainder[separatorIndex + 1] === ' ') {
214
- throw new Error(`Invalid P-label format: ${label}`);
215
- }
216
- // Extract moduleId (substring before first space)
217
- const schemeID = remainder.slice(0, separatorIndex);
218
- // Register the module (throws if unsupported)
219
- this.addPModuleByScheme(schemeID, 'label', pModulesByScheme);
220
- }
221
- else {
222
- // Regular label - add to list for normal permission checks
223
- nonPLabels.push(label);
224
- }
225
- }
226
- return nonPLabels;
227
- }
228
162
  /**
229
163
  * Decrypts custom instructions in listOutputs results if encryption is configured.
230
164
  */
@@ -238,36 +172,6 @@ class WalletPermissionsManager {
238
172
  }
239
173
  return results;
240
174
  }
241
- /**
242
- * Decrypts metadata in listActions results if encryption is configured.
243
- */
244
- async decryptListActionsMetadata(results) {
245
- if (results.actions) {
246
- for (let i = 0; i < results.actions.length; i++) {
247
- if (results.actions[i].description) {
248
- results.actions[i].description = await this.maybeDecryptMetadata(results.actions[i].description);
249
- }
250
- if (results.actions[i].inputs) {
251
- for (let j = 0; j < results.actions[i].inputs.length; j++) {
252
- if (results.actions[i].inputs[j].inputDescription) {
253
- results.actions[i].inputs[j].inputDescription = await this.maybeDecryptMetadata(results.actions[i].inputs[j].inputDescription);
254
- }
255
- }
256
- }
257
- if (results.actions[i].outputs) {
258
- for (let j = 0; j < results.actions[i].outputs.length; j++) {
259
- if (results.actions[i].outputs[j].outputDescription) {
260
- results.actions[i].outputs[j].outputDescription = await this.maybeDecryptMetadata(results.actions[i].outputs[j].outputDescription);
261
- }
262
- if (results.actions[i].outputs[j].customInstructions) {
263
- results.actions[i].outputs[j].customInstructions = await this.maybeDecryptMetadata(results.actions[i].outputs[j].customInstructions);
264
- }
265
- }
266
- }
267
- }
268
- }
269
- return results;
270
- }
271
175
  /* ---------------------------------------------------------------------
272
176
  * 1) PUBLIC API FOR REGISTERING CALLBACKS (UI PROMPTS, LOGGING, ETC.)
273
177
  * --------------------------------------------------------------------- */
@@ -408,177 +312,93 @@ class WalletPermissionsManager {
408
312
  if (!matching) {
409
313
  throw new Error('Request ID not found.');
410
314
  }
411
- try {
412
- const originalRequest = matching.request;
413
- const { originator, permissions: requestedPermissions, displayOriginator } = originalRequest;
414
- const originLookupValues = this.buildOriginatorLookupValues(displayOriginator, originator);
415
- // --- Validation: Ensure granted permissions are a subset of what was requested ---
416
- if (params.granted.spendingAuthorization && !requestedPermissions.spendingAuthorization) {
417
- throw new Error('Granted spending authorization was not part of the original request.');
418
- }
419
- if ((_a = params.granted.protocolPermissions) === null || _a === void 0 ? void 0 : _a.some(g => { var _a; return !((_a = requestedPermissions.protocolPermissions) === null || _a === void 0 ? void 0 : _a.find(r => deepEqual(r, g))); })) {
420
- throw new Error('Granted protocol permissions are not a subset of the original request.');
421
- }
422
- if ((_b = params.granted.basketAccess) === null || _b === void 0 ? void 0 : _b.some(g => { var _a; return !((_a = requestedPermissions.basketAccess) === null || _a === void 0 ? void 0 : _a.find(r => deepEqual(r, g))); })) {
423
- throw new Error('Granted basket access permissions are not a subset of the original request.');
424
- }
425
- if ((_c = params.granted.certificateAccess) === null || _c === void 0 ? void 0 : _c.some(g => { var _a; return !((_a = requestedPermissions.certificateAccess) === null || _a === void 0 ? void 0 : _a.find(r => deepEqual(r, g))); })) {
426
- throw new Error('Granted certificate access permissions are not a subset of the original request.');
427
- }
428
- // --- End Validation ---
429
- const expiry = params.expiry || 0; // default: never expires
430
- const toCreate = [];
431
- const toRenew = [];
432
- if (params.granted.spendingAuthorization) {
433
- toCreate.push({
434
- request: {
435
- type: 'spending',
436
- originator,
437
- spending: { satoshis: params.granted.spendingAuthorization.amount },
438
- reason: params.granted.spendingAuthorization.description
439
- },
440
- expiry: 0,
441
- amount: params.granted.spendingAuthorization.amount
442
- });
443
- }
444
- const grantedProtocols = params.granted.protocolPermissions || [];
445
- const protocolTokens = await this.mapWithConcurrency(grantedProtocols, 8, async (p) => {
446
- const token = await this.findProtocolToken(originator, false, p.protocolID, p.counterparty || 'self', true, originLookupValues);
447
- return { p, token };
448
- });
449
- for (const { p, token } of protocolTokens) {
315
+ const originalRequest = matching.request;
316
+ const { originator, permissions: requestedPermissions, displayOriginator } = originalRequest;
317
+ const originLookupValues = this.buildOriginatorLookupValues(displayOriginator, originator);
318
+ // --- Validation: Ensure granted permissions are a subset of what was requested ---
319
+ if (params.granted.spendingAuthorization &&
320
+ !deepEqual(params.granted.spendingAuthorization, requestedPermissions.spendingAuthorization)) {
321
+ throw new Error('Granted spending authorization does not match the original request.');
322
+ }
323
+ if ((_a = params.granted.protocolPermissions) === null || _a === void 0 ? void 0 : _a.some(g => { var _a; return !((_a = requestedPermissions.protocolPermissions) === null || _a === void 0 ? void 0 : _a.find(r => deepEqual(r, g))); })) {
324
+ throw new Error('Granted protocol permissions are not a subset of the original request.');
325
+ }
326
+ if ((_b = params.granted.basketAccess) === null || _b === void 0 ? void 0 : _b.some(g => { var _a; return !((_a = requestedPermissions.basketAccess) === null || _a === void 0 ? void 0 : _a.find(r => deepEqual(r, g))); })) {
327
+ throw new Error('Granted basket access permissions are not a subset of the original request.');
328
+ }
329
+ if ((_c = params.granted.certificateAccess) === null || _c === void 0 ? void 0 : _c.some(g => { var _a; return !((_a = requestedPermissions.certificateAccess) === null || _a === void 0 ? void 0 : _a.find(r => deepEqual(r, g))); })) {
330
+ throw new Error('Granted certificate access permissions are not a subset of the original request.');
331
+ }
332
+ // --- End Validation ---
333
+ const expiry = params.expiry || 0; // default: never expires
334
+ if (params.granted.spendingAuthorization) {
335
+ await this.createPermissionOnChain({
336
+ type: 'spending',
337
+ originator,
338
+ spending: { satoshis: params.granted.spendingAuthorization.amount },
339
+ reason: params.granted.spendingAuthorization.description
340
+ }, 0, // No expiry for spending tokens
341
+ params.granted.spendingAuthorization.amount);
342
+ }
343
+ for (const p of params.granted.protocolPermissions || []) {
344
+ const token = await this.findProtocolToken(originator, false, // No privileged protocols allowed in groups for added security.
345
+ p.protocolID, p.counterparty || 'self', true, originLookupValues);
346
+ if (token) {
450
347
  const request = {
451
348
  type: 'protocol',
452
349
  originator,
453
- privileged: false,
350
+ privileged: false, // No privileged protocols allowed in groups for added security.
454
351
  protocolID: p.protocolID,
455
352
  counterparty: p.counterparty || 'self',
456
353
  reason: p.description
457
354
  };
458
- if (token) {
459
- toRenew.push({ oldToken: token, request, expiry });
460
- }
461
- else {
462
- toCreate.push({ request, expiry });
463
- }
464
- }
465
- for (const b of params.granted.basketAccess || []) {
466
- toCreate.push({
467
- request: { type: 'basket', originator, basket: b.basket, reason: b.description },
468
- expiry
469
- });
470
- }
471
- for (const c of params.granted.certificateAccess || []) {
472
- toCreate.push({
473
- request: {
474
- type: 'certificate',
475
- originator,
476
- privileged: false,
477
- certificate: {
478
- verifier: c.verifierPublicKey,
479
- certType: c.type,
480
- fields: c.fields
481
- },
482
- reason: c.description
483
- },
484
- expiry
485
- });
355
+ await this.renewPermissionOnChain(token, request, expiry);
356
+ this.markRecentGrant(request);
486
357
  }
487
- const created = await this.createPermissionTokensBestEffort(toCreate);
488
- const renewed = await this.renewPermissionTokensBestEffort(toRenew);
489
- for (const req of [...created, ...renewed]) {
490
- this.markRecentGrant(req);
491
- }
492
- // Success - resolve all pending promises for this request
493
- for (const p of matching.pending) {
494
- p.resolve(true);
495
- }
496
- }
497
- catch (error) {
498
- // Failure - reject all pending promises so callers don't hang forever
499
- for (const p of matching.pending) {
500
- p.reject(error);
358
+ else {
359
+ const request = {
360
+ type: 'protocol',
361
+ originator,
362
+ privileged: false, // No privileged protocols allowed in groups for added security.
363
+ protocolID: p.protocolID,
364
+ counterparty: p.counterparty || 'self',
365
+ reason: p.description
366
+ };
367
+ await this.createPermissionOnChain(request, expiry);
368
+ this.markRecentGrant(request);
501
369
  }
502
- throw error;
503
- }
504
- finally {
505
- // Always clean up the request entry
506
- this.activeRequests.delete(params.requestID);
507
- }
508
- }
509
- /**
510
- * Denies a previously requested grouped permission.
511
- * @param requestID The ID of the request being denied.
512
- */
513
- async denyGroupedPermission(requestID) {
514
- const matching = this.activeRequests.get(requestID);
515
- if (!matching) {
516
- throw new Error('Request ID not found.');
517
- }
518
- const err = new Error('The user has denied the request for permission.');
519
- err.code = 'ERR_PERMISSION_DENIED';
520
- for (const p of matching.pending) {
521
- p.reject(err);
522
- }
523
- this.activeRequests.delete(requestID);
524
- }
525
- async dismissGroupedPermission(requestID) {
526
- const matching = this.activeRequests.get(requestID);
527
- if (!matching) {
528
- throw new Error('Request ID not found.');
529
370
  }
530
- for (const p of matching.pending) {
531
- p.resolve(true);
371
+ for (const b of params.granted.basketAccess || []) {
372
+ const request = { type: 'basket', originator, basket: b.basket, reason: b.description };
373
+ await this.createPermissionOnChain(request, expiry);
374
+ this.markRecentGrant(request);
532
375
  }
533
- this.activeRequests.delete(requestID);
534
- }
535
- async grantCounterpartyPermission(params) {
536
- var _a;
537
- const matching = this.activeRequests.get(params.requestID);
538
- if (!matching) {
539
- throw new Error('Request ID not found.');
540
- }
541
- const originalRequest = matching.request;
542
- const { originator, counterparty, permissions: requestedPermissions, displayOriginator } = originalRequest;
543
- const originLookupValues = this.buildOriginatorLookupValues(displayOriginator, originator);
544
- if ((_a = params.granted.protocols) === null || _a === void 0 ? void 0 : _a.some(g => !requestedPermissions.protocols.find(r => deepEqual(r, g)))) {
545
- throw new Error('Granted protocol permissions are not a subset of the original request.');
546
- }
547
- const expiry = params.expiry || 0;
548
- const toCreate = [];
549
- const toRenew = [];
550
- const grantedProtocols = params.granted.protocols || [];
551
- const protocolTokens = await this.mapWithConcurrency(grantedProtocols, 8, async (p) => {
552
- const token = await this.findProtocolToken(originator, false, p.protocolID, counterparty, true, originLookupValues);
553
- return { p, token };
554
- });
555
- for (const { p, token } of protocolTokens) {
376
+ for (const c of params.granted.certificateAccess || []) {
556
377
  const request = {
557
- type: 'protocol',
378
+ type: 'certificate',
558
379
  originator,
559
- privileged: false,
560
- protocolID: p.protocolID,
561
- counterparty,
562
- reason: p.description
380
+ privileged: false, // No certificates on the privileged identity are allowed as part of groups.
381
+ certificate: {
382
+ verifier: c.verifierPublicKey,
383
+ certType: c.type,
384
+ fields: c.fields
385
+ },
386
+ reason: c.description
563
387
  };
564
- if (token) {
565
- toRenew.push({ oldToken: token, request, expiry });
566
- }
567
- else {
568
- toCreate.push({ request, expiry });
569
- }
570
- }
571
- const created = await this.createPermissionTokensBestEffort(toCreate);
572
- const renewed = await this.renewPermissionTokensBestEffort(toRenew);
573
- for (const req of [...created, ...renewed]) {
574
- this.markRecentGrant(req);
388
+ await this.createPermissionOnChain(request, expiry);
389
+ this.markRecentGrant(request);
575
390
  }
391
+ // Resolve all pending promises for this request
576
392
  for (const p of matching.pending) {
577
393
  p.resolve(true);
578
394
  }
579
395
  this.activeRequests.delete(params.requestID);
580
396
  }
581
- async denyCounterpartyPermission(requestID) {
397
+ /**
398
+ * Denies a previously requested grouped permission.
399
+ * @param requestID The ID of the request being denied.
400
+ */
401
+ async denyGroupedPermission(requestID) {
582
402
  const matching = this.activeRequests.get(requestID);
583
403
  if (!matching) {
584
404
  throw new Error('Request ID not found.');
@@ -908,469 +728,6 @@ class WalletPermissionsManager {
908
728
  usageType: 'generic'
909
729
  });
910
730
  }
911
- validateCounterpartyPermissions(raw) {
912
- if (!raw || !Array.isArray(raw.protocols) || raw.protocols.length === 0)
913
- return null;
914
- const validProtocols = raw.protocols.filter((p) => {
915
- return (Array.isArray(p === null || p === void 0 ? void 0 : p.protocolID) &&
916
- p.protocolID[0] === 2 &&
917
- typeof p.protocolID[1] === 'string' &&
918
- typeof (p === null || p === void 0 ? void 0 : p.description) === 'string');
919
- });
920
- if (validProtocols.length === 0)
921
- return null;
922
- return {
923
- description: typeof raw.description === 'string' ? raw.description : undefined,
924
- protocols: validProtocols
925
- };
926
- }
927
- async fetchManifestPermissions(originator) {
928
- const cached = this.manifestCache.get(originator);
929
- if (cached && Date.now() - cached.fetchedAt < WalletPermissionsManager.MANIFEST_CACHE_TTL_MS) {
930
- return {
931
- groupPermissions: cached.groupPermissions,
932
- counterpartyPermissions: cached.counterpartyPermissions
933
- };
934
- }
935
- const inProgress = this.manifestFetchInProgress.get(originator);
936
- if (inProgress) {
937
- return inProgress;
938
- }
939
- const fetchPromise = (async () => {
940
- var _a, _b;
941
- try {
942
- const proto = originator.startsWith('localhost:') ? 'http' : 'https';
943
- const response = await fetch(`${proto}://${originator}/manifest.json`);
944
- if (response.ok) {
945
- const manifest = await response.json();
946
- const groupPermissions = ((_a = manifest === null || manifest === void 0 ? void 0 : manifest.babbage) === null || _a === void 0 ? void 0 : _a.groupPermissions) || null;
947
- const counterpartyPermissions = this.validateCounterpartyPermissions((_b = manifest === null || manifest === void 0 ? void 0 : manifest.babbage) === null || _b === void 0 ? void 0 : _b.counterpartyPermissions);
948
- this.manifestCache.set(originator, { groupPermissions, counterpartyPermissions, fetchedAt: Date.now() });
949
- return { groupPermissions, counterpartyPermissions };
950
- }
951
- }
952
- catch (e) { }
953
- const result = { groupPermissions: null, counterpartyPermissions: null };
954
- this.manifestCache.set(originator, { ...result, fetchedAt: Date.now() });
955
- return result;
956
- })();
957
- this.manifestFetchInProgress.set(originator, fetchPromise);
958
- try {
959
- return await fetchPromise;
960
- }
961
- finally {
962
- this.manifestFetchInProgress.delete(originator);
963
- }
964
- }
965
- async fetchManifestGroupPermissions(originator) {
966
- const { groupPermissions } = await this.fetchManifestPermissions(originator);
967
- return groupPermissions;
968
- }
969
- async filterAlreadyGrantedPermissions(originator, groupPermissions) {
970
- const permissionsToRequest = {
971
- description: groupPermissions.description,
972
- protocolPermissions: [],
973
- basketAccess: [],
974
- certificateAccess: []
975
- };
976
- if (groupPermissions.spendingAuthorization) {
977
- const hasAuth = await this.hasSpendingAuthorization({
978
- originator,
979
- satoshis: groupPermissions.spendingAuthorization.amount
980
- });
981
- if (!hasAuth) {
982
- permissionsToRequest.spendingAuthorization = groupPermissions.spendingAuthorization;
983
- }
984
- }
985
- for (const p of groupPermissions.protocolPermissions || []) {
986
- const hasPerm = await this.hasProtocolPermission({
987
- originator,
988
- privileged: false,
989
- protocolID: p.protocolID,
990
- counterparty: p.counterparty || 'self'
991
- });
992
- if (!hasPerm) {
993
- permissionsToRequest.protocolPermissions.push(p);
994
- }
995
- }
996
- for (const b of groupPermissions.basketAccess || []) {
997
- const hasAccess = await this.hasBasketAccess({
998
- originator,
999
- basket: b.basket
1000
- });
1001
- if (!hasAccess) {
1002
- permissionsToRequest.basketAccess.push(b);
1003
- }
1004
- }
1005
- for (const c of groupPermissions.certificateAccess || []) {
1006
- const hasAccess = await this.hasCertificateAccess({
1007
- originator,
1008
- privileged: false,
1009
- verifier: c.verifierPublicKey,
1010
- certType: c.type,
1011
- fields: c.fields
1012
- });
1013
- if (!hasAccess) {
1014
- permissionsToRequest.certificateAccess.push(c);
1015
- }
1016
- }
1017
- return permissionsToRequest;
1018
- }
1019
- hasAnyPermissionsToRequest(permissions) {
1020
- var _a, _b, _c, _d, _e, _f;
1021
- return !!(permissions.spendingAuthorization ||
1022
- ((_b = (_a = permissions.protocolPermissions) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0 ||
1023
- ((_d = (_c = permissions.basketAccess) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0) > 0 ||
1024
- ((_f = (_e = permissions.certificateAccess) === null || _e === void 0 ? void 0 : _e.length) !== null && _f !== void 0 ? _f : 0) > 0);
1025
- }
1026
- hasGroupedPermissionRequestedHandlers() {
1027
- const handlers = this.callbacks.onGroupedPermissionRequested || [];
1028
- return handlers.some(h => typeof h === 'function');
1029
- }
1030
- hasCounterpartyPermissionRequestedHandlers() {
1031
- const handlers = this.callbacks.onCounterpartyPermissionRequested || [];
1032
- return handlers.some(h => typeof h === 'function');
1033
- }
1034
- async hasPactEstablished(originator, counterparty) {
1035
- var _a;
1036
- if (counterparty === 'self' || counterparty === 'anyone') {
1037
- return true;
1038
- }
1039
- const cacheKey = `${originator}:${counterparty}`;
1040
- if (this.pactEstablishedCache.has(cacheKey)) {
1041
- return true;
1042
- }
1043
- const { counterpartyPermissions } = await this.fetchManifestPermissions(originator);
1044
- if (!((_a = counterpartyPermissions === null || counterpartyPermissions === void 0 ? void 0 : counterpartyPermissions.protocols) === null || _a === void 0 ? void 0 : _a.length)) {
1045
- return true;
1046
- }
1047
- const firstProtocol = counterpartyPermissions.protocols[0];
1048
- const hasToken = await this.hasProtocolPermission({
1049
- originator,
1050
- privileged: false,
1051
- protocolID: firstProtocol.protocolID,
1052
- counterparty
1053
- });
1054
- if (hasToken) {
1055
- this.pactEstablishedCache.set(cacheKey, Date.now());
1056
- return true;
1057
- }
1058
- return false;
1059
- }
1060
- markPactEstablished(originator, counterparty) {
1061
- const cacheKey = `${originator}:${counterparty}`;
1062
- this.pactEstablishedCache.set(cacheKey, Date.now());
1063
- }
1064
- async maybeRequestPact(currentRequest) {
1065
- var _a;
1066
- if (!this.config.seekGroupedPermission) {
1067
- return null;
1068
- }
1069
- if (!this.hasCounterpartyPermissionRequestedHandlers()) {
1070
- return null;
1071
- }
1072
- if (currentRequest.type !== 'protocol') {
1073
- return null;
1074
- }
1075
- if (currentRequest.privileged) {
1076
- return null;
1077
- }
1078
- const [level] = currentRequest.protocolID;
1079
- if (level !== 2) {
1080
- return null;
1081
- }
1082
- const originator = currentRequest.originator;
1083
- const counterparty = currentRequest.counterparty;
1084
- if (!counterparty || counterparty === 'self' || counterparty === 'anyone') {
1085
- return null;
1086
- }
1087
- if (!/^[0-9a-fA-F]{66}$/.test(counterparty)) {
1088
- return null;
1089
- }
1090
- if (await this.hasPactEstablished(originator, counterparty)) {
1091
- return null;
1092
- }
1093
- const { counterpartyPermissions } = await this.fetchManifestPermissions(originator);
1094
- if (!((_a = counterpartyPermissions === null || counterpartyPermissions === void 0 ? void 0 : counterpartyPermissions.protocols) === null || _a === void 0 ? void 0 : _a.length)) {
1095
- return null;
1096
- }
1097
- const protocolsToRequest = [];
1098
- for (const p of counterpartyPermissions.protocols) {
1099
- const hasPerm = await this.hasProtocolPermission({
1100
- originator,
1101
- privileged: false,
1102
- protocolID: p.protocolID,
1103
- counterparty
1104
- });
1105
- if (!hasPerm) {
1106
- protocolsToRequest.push(p);
1107
- }
1108
- }
1109
- if (protocolsToRequest.length === 0) {
1110
- this.markPactEstablished(originator, counterparty);
1111
- return null;
1112
- }
1113
- const permissionsToRequest = {
1114
- description: counterpartyPermissions.description,
1115
- protocols: protocolsToRequest
1116
- };
1117
- const key = `pact:${originator}:${counterparty}`;
1118
- const existing = this.activeRequests.get(key);
1119
- if (existing) {
1120
- const existingRequest = existing.request;
1121
- for (const p of permissionsToRequest.protocols) {
1122
- if (!existingRequest.permissions.protocols.find(x => deepEqual(x, p))) {
1123
- existingRequest.permissions.protocols.push(p);
1124
- }
1125
- }
1126
- await new Promise((resolve, reject) => {
1127
- existing.pending.push({ resolve, reject });
1128
- });
1129
- }
1130
- else {
1131
- await new Promise(async (resolve, reject) => {
1132
- this.activeRequests.set(key, {
1133
- request: {
1134
- originator,
1135
- counterparty,
1136
- permissions: permissionsToRequest,
1137
- displayOriginator: currentRequest.displayOriginator
1138
- },
1139
- pending: [{ resolve, reject }]
1140
- });
1141
- await this.callEvent('onCounterpartyPermissionRequested', {
1142
- requestID: key,
1143
- originator,
1144
- counterparty,
1145
- permissions: permissionsToRequest
1146
- });
1147
- });
1148
- }
1149
- this.markPactEstablished(originator, counterparty);
1150
- const satisfied = await this.hasProtocolPermission({
1151
- originator,
1152
- privileged: false,
1153
- protocolID: currentRequest.protocolID,
1154
- counterparty
1155
- });
1156
- return satisfied ? true : null;
1157
- }
1158
- async maybeRequestPeerGroupedLevel2ProtocolPermissions(currentRequest) {
1159
- var _a, _b;
1160
- if (!this.config.seekGroupedPermission) {
1161
- return null;
1162
- }
1163
- if (!this.hasGroupedPermissionRequestedHandlers()) {
1164
- return null;
1165
- }
1166
- if (currentRequest.type !== 'protocol') {
1167
- return null;
1168
- }
1169
- const [level] = currentRequest.protocolID;
1170
- if (level !== 2) {
1171
- return null;
1172
- }
1173
- const originator = currentRequest.originator;
1174
- const privileged = (_a = currentRequest.privileged) !== null && _a !== void 0 ? _a : false;
1175
- const counterparty = (_b = currentRequest.counterparty) !== null && _b !== void 0 ? _b : 'self';
1176
- const groupPermissions = await this.fetchManifestGroupPermissions(originator);
1177
- if (!groupPermissions) {
1178
- return null;
1179
- }
1180
- const normalizeManifestCounterparty = (cp) => {
1181
- if (cp === '')
1182
- return counterparty;
1183
- return cp !== null && cp !== void 0 ? cp : 'self';
1184
- };
1185
- const manifestLevel2ForThisPeer = (groupPermissions.protocolPermissions || [])
1186
- .filter(p => { var _a, _b; return ((_b = (_a = p.protocolID) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : 0) === 2; })
1187
- .map(p => ({
1188
- protocolID: p.protocolID,
1189
- counterparty: normalizeManifestCounterparty(p.counterparty),
1190
- description: p.description
1191
- }))
1192
- .filter(p => p.counterparty === counterparty);
1193
- const isCurrentRequestInManifest = manifestLevel2ForThisPeer.some(p => deepEqual(p.protocolID, currentRequest.protocolID));
1194
- if (!isCurrentRequestInManifest) {
1195
- return null;
1196
- }
1197
- const permissionsToRequest = {
1198
- protocolPermissions: []
1199
- };
1200
- for (const p of manifestLevel2ForThisPeer) {
1201
- const hasPerm = await this.hasProtocolPermission({
1202
- originator,
1203
- privileged,
1204
- protocolID: p.protocolID,
1205
- counterparty: p.counterparty
1206
- });
1207
- if (!hasPerm) {
1208
- permissionsToRequest.protocolPermissions.push({
1209
- protocolID: p.protocolID,
1210
- counterparty: p.counterparty,
1211
- description: p.description
1212
- });
1213
- }
1214
- }
1215
- if (!this.hasAnyPermissionsToRequest(permissionsToRequest)) {
1216
- return null;
1217
- }
1218
- const key = `group-peer:${originator}:${privileged}:${counterparty}`;
1219
- const existing = this.activeRequests.get(key);
1220
- if (existing) {
1221
- const existingRequest = existing.request;
1222
- if (!existingRequest.permissions.protocolPermissions) {
1223
- existingRequest.permissions.protocolPermissions = [];
1224
- }
1225
- for (const p of permissionsToRequest.protocolPermissions || []) {
1226
- if (!existingRequest.permissions.protocolPermissions.find(x => deepEqual(x, p))) {
1227
- existingRequest.permissions.protocolPermissions.push(p);
1228
- }
1229
- }
1230
- await new Promise((resolve, reject) => {
1231
- existing.pending.push({ resolve, reject });
1232
- });
1233
- }
1234
- else {
1235
- await new Promise(async (resolve, reject) => {
1236
- const permissions = permissionsToRequest;
1237
- this.activeRequests.set(key, {
1238
- request: {
1239
- originator,
1240
- permissions,
1241
- displayOriginator: currentRequest.displayOriginator
1242
- },
1243
- pending: [{ resolve, reject }]
1244
- });
1245
- await this.callEvent('onGroupedPermissionRequested', {
1246
- requestID: key,
1247
- originator,
1248
- permissions
1249
- });
1250
- });
1251
- }
1252
- const satisfied = await this.checkSpecificPermissionAfterGroupFlow(currentRequest);
1253
- return satisfied ? true : null;
1254
- }
1255
- async checkSpecificPermissionAfterGroupFlow(request) {
1256
- var _a, _b, _c;
1257
- switch (request.type) {
1258
- case 'protocol':
1259
- return await this.hasProtocolPermission({
1260
- originator: request.originator,
1261
- privileged: (_a = request.privileged) !== null && _a !== void 0 ? _a : false,
1262
- protocolID: request.protocolID,
1263
- counterparty: (_b = request.counterparty) !== null && _b !== void 0 ? _b : 'self'
1264
- });
1265
- case 'basket':
1266
- return await this.hasBasketAccess({
1267
- originator: request.originator,
1268
- basket: request.basket
1269
- });
1270
- case 'certificate':
1271
- return await this.hasCertificateAccess({
1272
- originator: request.originator,
1273
- privileged: (_c = request.privileged) !== null && _c !== void 0 ? _c : false,
1274
- verifier: request.certificate.verifier,
1275
- certType: request.certificate.certType,
1276
- fields: request.certificate.fields
1277
- });
1278
- case 'spending':
1279
- return await this.hasSpendingAuthorization({
1280
- originator: request.originator,
1281
- satoshis: request.spending.satoshis
1282
- });
1283
- default:
1284
- return false;
1285
- }
1286
- }
1287
- isRequestIncludedInGroupPermissions(request, groupPermissions) {
1288
- var _a, _b, _c, _d;
1289
- switch (request.type) {
1290
- case 'protocol': {
1291
- if (request.privileged)
1292
- return false;
1293
- const pid = request.protocolID;
1294
- if (!pid)
1295
- return false;
1296
- const cp = (_a = request.counterparty) !== null && _a !== void 0 ? _a : 'self';
1297
- return !!((_b = groupPermissions.protocolPermissions) === null || _b === void 0 ? void 0 : _b.some(p => {
1298
- var _a;
1299
- const manifestCp = p.counterparty === '' ? cp : ((_a = p.counterparty) !== null && _a !== void 0 ? _a : 'self');
1300
- return deepEqual(p.protocolID, pid) && manifestCp === cp;
1301
- }));
1302
- }
1303
- case 'basket': {
1304
- const basket = request.basket;
1305
- if (!basket)
1306
- return false;
1307
- return !!((_c = groupPermissions.basketAccess) === null || _c === void 0 ? void 0 : _c.some(b => b.basket === basket));
1308
- }
1309
- case 'certificate': {
1310
- if (request.privileged)
1311
- return false;
1312
- const cert = request.certificate;
1313
- if (!cert)
1314
- return false;
1315
- return !!((_d = groupPermissions.certificateAccess) === null || _d === void 0 ? void 0 : _d.some(c => {
1316
- const fieldsA = new Set(c.fields || []);
1317
- const fieldsB = new Set(cert.fields || []);
1318
- if (fieldsA.size !== fieldsB.size)
1319
- return false;
1320
- for (const f of fieldsA)
1321
- if (!fieldsB.has(f))
1322
- return false;
1323
- return c.type === cert.certType && c.verifierPublicKey === cert.verifier;
1324
- }));
1325
- }
1326
- case 'spending':
1327
- return !!groupPermissions.spendingAuthorization;
1328
- default:
1329
- return false;
1330
- }
1331
- }
1332
- async maybeRequestGroupedPermissions(currentRequest) {
1333
- if (!this.config.seekGroupedPermission) {
1334
- return null;
1335
- }
1336
- const originator = currentRequest.originator;
1337
- const groupPermissions = await this.fetchManifestGroupPermissions(originator);
1338
- if (!groupPermissions) {
1339
- return null;
1340
- }
1341
- if (!this.isRequestIncludedInGroupPermissions(currentRequest, groupPermissions)) {
1342
- return null;
1343
- }
1344
- const permissionsToRequest = await this.filterAlreadyGrantedPermissions(originator, groupPermissions);
1345
- if (!this.hasAnyPermissionsToRequest(permissionsToRequest)) {
1346
- return null;
1347
- }
1348
- const key = `group:${originator}`;
1349
- if (this.activeRequests.has(key)) {
1350
- await new Promise((resolve, reject) => {
1351
- this.activeRequests.get(key).pending.push({ resolve, reject });
1352
- });
1353
- }
1354
- else {
1355
- await new Promise(async (resolve, reject) => {
1356
- this.activeRequests.set(key, {
1357
- request: {
1358
- originator,
1359
- permissions: permissionsToRequest,
1360
- displayOriginator: currentRequest.displayOriginator
1361
- },
1362
- pending: [{ resolve, reject }]
1363
- });
1364
- await this.callEvent('onGroupedPermissionRequested', {
1365
- requestID: key,
1366
- originator,
1367
- permissions: permissionsToRequest
1368
- });
1369
- });
1370
- }
1371
- const satisfied = await this.checkSpecificPermissionAfterGroupFlow(currentRequest);
1372
- return satisfied ? true : null;
1373
- }
1374
731
  /**
1375
732
  * A central method that triggers the permission request flow.
1376
733
  * - It checks if there's already an active request for the same key
@@ -1379,25 +736,13 @@ class WalletPermissionsManager {
1379
736
  * and return a promise that resolves once permission is granted or rejects if denied.
1380
737
  */
1381
738
  async requestPermissionFlow(r) {
1382
- var _a, _b, _c;
739
+ var _a;
1383
740
  const normalizedOriginator = this.normalizeOriginator(r.originator) || r.originator;
1384
741
  const preparedRequest = {
1385
742
  ...r,
1386
743
  originator: normalizedOriginator,
1387
- displayOriginator: (_c = (_a = r.displayOriginator) !== null && _a !== void 0 ? _a : (_b = r.previousToken) === null || _b === void 0 ? void 0 : _b.rawOriginator) !== null && _c !== void 0 ? _c : r.originator
744
+ displayOriginator: (_a = r.displayOriginator) !== null && _a !== void 0 ? _a : r.originator
1388
745
  };
1389
- const pactResult = await this.maybeRequestPact(preparedRequest);
1390
- if (pactResult !== null) {
1391
- return pactResult;
1392
- }
1393
- const peerGroupResult = await this.maybeRequestPeerGroupedLevel2ProtocolPermissions(preparedRequest);
1394
- if (peerGroupResult !== null) {
1395
- return peerGroupResult;
1396
- }
1397
- const groupResult = await this.maybeRequestGroupedPermissions(preparedRequest);
1398
- if (groupResult !== null) {
1399
- return groupResult;
1400
- }
1401
746
  const key = this.buildRequestKey(preparedRequest);
1402
747
  // If there's already a queue for the same resource, we piggyback on it
1403
748
  const existingQueue = this.activeRequests.get(key);
@@ -1851,128 +1196,10 @@ class WalletPermissionsManager {
1851
1196
  }
1852
1197
  ],
1853
1198
  options: {
1854
- acceptDelayedBroadcast: true
1199
+ acceptDelayedBroadcast: false
1855
1200
  }
1856
1201
  }, this.adminOriginator);
1857
1202
  }
1858
- async mapWithConcurrency(items, concurrency, fn) {
1859
- if (!items.length)
1860
- return [];
1861
- const results = new Array(items.length);
1862
- let i = 0;
1863
- const worker = async () => {
1864
- while (true) {
1865
- const idx = i++;
1866
- if (idx >= items.length)
1867
- return;
1868
- results[idx] = await fn(items[idx]);
1869
- }
1870
- };
1871
- await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, () => worker()));
1872
- return results;
1873
- }
1874
- async runBestEffortBatches(items, chunkSize, runChunk) {
1875
- if (!items.length)
1876
- return [];
1877
- const out = [];
1878
- for (let i = 0; i < items.length; i += chunkSize) {
1879
- const chunk = items.slice(i, i + chunkSize);
1880
- out.push(...(await this.runBestEffortChunk(chunk, runChunk)));
1881
- }
1882
- return out;
1883
- }
1884
- async runBestEffortChunk(chunk, runChunk) {
1885
- try {
1886
- return await runChunk(chunk);
1887
- }
1888
- catch (e) {
1889
- if (chunk.length <= 1) {
1890
- console.error('Permission batch failed:', e);
1891
- return [];
1892
- }
1893
- const mid = Math.ceil(chunk.length / 2);
1894
- const left = await this.runBestEffortChunk(chunk.slice(0, mid), runChunk);
1895
- const right = await this.runBestEffortChunk(chunk.slice(mid), runChunk);
1896
- return [...left, ...right];
1897
- }
1898
- }
1899
- async buildPermissionOutput(r, expiry, amount) {
1900
- const normalizedOriginator = this.normalizeOriginator(r.originator) || r.originator;
1901
- r.originator = normalizedOriginator;
1902
- const basketName = BASKET_MAP[r.type];
1903
- if (!basketName) {
1904
- throw new Error(`Unsupported permission type: ${r.type}`);
1905
- }
1906
- const fields = await this.buildPushdropFields(r, expiry, amount);
1907
- const script = await new sdk_1.PushDrop(this.underlying).lock(fields, WalletPermissionsManager.PERM_TOKEN_ENCRYPTION_PROTOCOL, '1', 'self', true, true);
1908
- const tags = this.buildTagsForRequest(r);
1909
- return {
1910
- request: r,
1911
- output: {
1912
- lockingScript: script.toHex(),
1913
- satoshis: 1,
1914
- outputDescription: `${r.type} permission token`,
1915
- basket: basketName,
1916
- tags
1917
- }
1918
- };
1919
- }
1920
- async createPermissionTokensBestEffort(items) {
1921
- const CHUNK = 25;
1922
- return this.runBestEffortBatches(items, CHUNK, async (chunk) => {
1923
- const built = await this.mapWithConcurrency(chunk, 8, c => this.buildPermissionOutput(c.request, c.expiry, c.amount));
1924
- await this.createAction({
1925
- description: `Grant ${built.length} permissions`,
1926
- outputs: built.map(b => b.output),
1927
- options: { acceptDelayedBroadcast: true }
1928
- }, this.adminOriginator);
1929
- return built.map(b => b.request);
1930
- });
1931
- }
1932
- async renewPermissionTokensBestEffort(items) {
1933
- const CHUNK = 15;
1934
- return this.runBestEffortBatches(items, CHUNK, async (chunk) => {
1935
- const built = await this.mapWithConcurrency(chunk, 8, c => this.buildPermissionOutput(c.request, c.expiry, c.amount));
1936
- const inputBeef = new sdk_1.Beef();
1937
- for (const c of chunk) {
1938
- inputBeef.mergeBeef(sdk_1.Beef.fromBinary(c.oldToken.tx));
1939
- }
1940
- const { signableTransaction } = await this.createAction({
1941
- description: `Renew ${chunk.length} permissions`,
1942
- inputBEEF: inputBeef.toBinary(),
1943
- inputs: chunk.map((c, i) => ({
1944
- outpoint: `${c.oldToken.txid}.${c.oldToken.outputIndex}`,
1945
- unlockingScriptLength: 73,
1946
- inputDescription: `Consume old permission token #${i + 1}`
1947
- })),
1948
- outputs: built.map(b => b.output),
1949
- options: {
1950
- acceptDelayedBroadcast: true,
1951
- randomizeOutputs: false,
1952
- signAndProcess: false
1953
- }
1954
- }, this.adminOriginator);
1955
- if (!(signableTransaction === null || signableTransaction === void 0 ? void 0 : signableTransaction.reference) || !signableTransaction.tx) {
1956
- throw new Error('Failed to create signable transaction');
1957
- }
1958
- const partialTx = sdk_1.Transaction.fromAtomicBEEF(signableTransaction.tx);
1959
- const pushdrop = new sdk_1.PushDrop(this.underlying);
1960
- const spends = {};
1961
- for (let i = 0; i < chunk.length; i++) {
1962
- const token = chunk[i].oldToken;
1963
- const unlocker = pushdrop.unlock(WalletPermissionsManager.PERM_TOKEN_ENCRYPTION_PROTOCOL, '1', 'self', 'all', false, 1, sdk_1.LockingScript.fromHex(token.outputScript));
1964
- const unlockingScript = await unlocker.sign(partialTx, i);
1965
- spends[i] = { unlockingScript: unlockingScript.toHex() };
1966
- }
1967
- const { txid } = await this.underlying.signAction({
1968
- reference: signableTransaction.reference,
1969
- spends
1970
- });
1971
- if (!txid)
1972
- throw new Error('Failed to finalize renewal transaction');
1973
- return built.map(b => b.request);
1974
- });
1975
- }
1976
1203
  async coalescePermissionTokens(oldTokens, newScript, opts) {
1977
1204
  var _a;
1978
1205
  if (!(oldTokens === null || oldTokens === void 0 ? void 0 : oldTokens.length))
@@ -2003,7 +1230,7 @@ class WalletPermissionsManager {
2003
1230
  }
2004
1231
  ],
2005
1232
  options: {
2006
- acceptDelayedBroadcast: true,
1233
+ acceptDelayedBroadcast: false,
2007
1234
  randomizeOutputs: false,
2008
1235
  signAndProcess: false
2009
1236
  }
@@ -2082,7 +1309,7 @@ class WalletPermissionsManager {
2082
1309
  }
2083
1310
  ],
2084
1311
  options: {
2085
- acceptDelayedBroadcast: true
1312
+ acceptDelayedBroadcast: false
2086
1313
  }
2087
1314
  }, this.adminOriginator);
2088
1315
  const tx = sdk_1.Transaction.fromBEEF(signableTransaction.tx);
@@ -2218,7 +1445,7 @@ class WalletPermissionsManager {
2218
1445
  tags,
2219
1446
  tagQueryMode: 'all',
2220
1447
  include: 'entire transactions',
2221
- limit: 10000
1448
+ limit: 100
2222
1449
  }, this.adminOriginator);
2223
1450
  for (const out of result.outputs) {
2224
1451
  if (seen.has(out.outpoint))
@@ -2523,60 +1750,16 @@ class WalletPermissionsManager {
2523
1750
  }
2524
1751
  ],
2525
1752
  options: {
2526
- acceptDelayedBroadcast: true
1753
+ acceptDelayedBroadcast: false
2527
1754
  }
2528
1755
  }, this.adminOriginator);
2529
1756
  const tx = sdk_1.Transaction.fromBEEF(signableTransaction.tx);
2530
- const normalizeTxid = (txid) => (txid !== null && txid !== void 0 ? txid : '').toLowerCase();
2531
- const reverseHexTxid = (txid) => {
2532
- const hex = normalizeTxid(txid);
2533
- if (!/^[0-9a-f]{64}$/.test(hex))
2534
- return hex;
2535
- const bytes = hex.match(/../g);
2536
- return bytes ? bytes.reverse().join('') : hex;
2537
- };
2538
- const matchesOutpointString = (outpoint) => {
2539
- const dot = outpoint.lastIndexOf('.');
2540
- const colon = outpoint.lastIndexOf(':');
2541
- const sep = dot > colon ? dot : colon;
2542
- if (sep === -1)
2543
- return false;
2544
- const txidPart = outpoint.slice(0, sep);
2545
- const indexPart = outpoint.slice(sep + 1);
2546
- const vout = Number(indexPart);
2547
- if (!Number.isFinite(vout))
2548
- return false;
2549
- return normalizeTxid(txidPart) === normalizeTxid(oldToken.txid) && vout === oldToken.outputIndex;
2550
- };
2551
- let permInputIndex = tx.inputs.findIndex((input) => {
2552
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
2553
- const txidCandidate = (_g = (_f = (_e = (_d = (_c = (_b = (_a = input === null || input === void 0 ? void 0 : input.sourceTXID) !== null && _a !== void 0 ? _a : input === null || input === void 0 ? void 0 : input.sourceTxid) !== null && _b !== void 0 ? _b : input === null || input === void 0 ? void 0 : input.sourceTxId) !== null && _c !== void 0 ? _c : input === null || input === void 0 ? void 0 : input.prevTxId) !== null && _d !== void 0 ? _d : input === null || input === void 0 ? void 0 : input.prevTxid) !== null && _e !== void 0 ? _e : input === null || input === void 0 ? void 0 : input.prevTXID) !== null && _f !== void 0 ? _f : input === null || input === void 0 ? void 0 : input.txid) !== null && _g !== void 0 ? _g : input === null || input === void 0 ? void 0 : input.txID;
2554
- const voutCandidate = (_l = (_k = (_j = (_h = input === null || input === void 0 ? void 0 : input.sourceOutputIndex) !== null && _h !== void 0 ? _h : input === null || input === void 0 ? void 0 : input.sourceOutput) !== null && _j !== void 0 ? _j : input === null || input === void 0 ? void 0 : input.outputIndex) !== null && _k !== void 0 ? _k : input === null || input === void 0 ? void 0 : input.vout) !== null && _l !== void 0 ? _l : input === null || input === void 0 ? void 0 : input.prevOutIndex;
2555
- if (typeof txidCandidate === 'string' && typeof voutCandidate === 'number') {
2556
- const cand = normalizeTxid(txidCandidate);
2557
- const target = normalizeTxid(oldToken.txid);
2558
- if (cand === target && voutCandidate === oldToken.outputIndex)
2559
- return true;
2560
- if (cand === reverseHexTxid(oldToken.txid) && voutCandidate === oldToken.outputIndex)
2561
- return true;
2562
- }
2563
- const outpointCandidate = (_o = (_m = input === null || input === void 0 ? void 0 : input.outpoint) !== null && _m !== void 0 ? _m : input === null || input === void 0 ? void 0 : input.sourceOutpoint) !== null && _o !== void 0 ? _o : input === null || input === void 0 ? void 0 : input.prevOutpoint;
2564
- if (typeof outpointCandidate === 'string' && matchesOutpointString(outpointCandidate))
2565
- return true;
2566
- return false;
2567
- });
2568
- if (permInputIndex === -1 && tx.inputs.length === 1) {
2569
- permInputIndex = 0;
2570
- }
2571
- if (permInputIndex === -1) {
2572
- throw new Error('Unable to locate permission token input for revocation.');
2573
- }
2574
1757
  const unlocker = new sdk_1.PushDrop(this.underlying).unlock(WalletPermissionsManager.PERM_TOKEN_ENCRYPTION_PROTOCOL, '1', 'self', 'all', false, 1, sdk_1.LockingScript.fromHex(oldToken.outputScript));
2575
- const unlockingScript = await unlocker.sign(tx, permInputIndex);
1758
+ const unlockingScript = await unlocker.sign(tx, 0);
2576
1759
  await this.underlying.signAction({
2577
1760
  reference: signableTransaction.reference,
2578
1761
  spends: {
2579
- [permInputIndex]: {
1762
+ 0: {
2580
1763
  unlockingScript: unlockingScript.toHex()
2581
1764
  }
2582
1765
  }
@@ -2586,16 +1769,22 @@ class WalletPermissionsManager {
2586
1769
  * 7) BRC-100 WALLET INTERFACE FORWARDING WITH PERMISSION CHECKS
2587
1770
  * --------------------------------------------------------------------- */
2588
1771
  async createAction(args, originator) {
2589
- // 1) Identify unique P-modules involved (one per schemeID) from both baskets and labels
1772
+ var _a;
1773
+ // 1) Identify unique P-modules involved (one per schemeID)
2590
1774
  const pModulesByScheme = new Map();
2591
1775
  const nonPBaskets = [];
2592
- // Check baskets for p modules
2593
1776
  if (args.outputs) {
2594
1777
  for (const out of args.outputs) {
2595
1778
  if (out.basket) {
2596
1779
  if (out.basket.startsWith('p ')) {
2597
1780
  const schemeID = out.basket.split(' ')[1];
2598
- this.addPModuleByScheme(schemeID, 'basket', pModulesByScheme);
1781
+ if (!pModulesByScheme.has(schemeID)) {
1782
+ const module = (_a = this.config.permissionModules) === null || _a === void 0 ? void 0 : _a[schemeID];
1783
+ if (!module) {
1784
+ throw new Error(`Unsupported P-basket scheme: p ${schemeID}`);
1785
+ }
1786
+ pModulesByScheme.set(schemeID, module);
1787
+ }
2599
1788
  }
2600
1789
  else {
2601
1790
  // Track non-P baskets for normal permission checks
@@ -2604,8 +1793,6 @@ class WalletPermissionsManager {
2604
1793
  }
2605
1794
  }
2606
1795
  }
2607
- // Check labels for p modules
2608
- const nonPLabels = this.splitLabelsByPermissionModule(args.labels, pModulesByScheme);
2609
1796
  // 2) Check permissions for non-P baskets
2610
1797
  for (const basket of nonPBaskets) {
2611
1798
  await this.ensureBasketAccess({
@@ -2615,14 +1802,15 @@ class WalletPermissionsManager {
2615
1802
  usageType: 'insertion'
2616
1803
  });
2617
1804
  }
2618
- // 3) Check permissions for non-P labels
2619
- for (const lbl of nonPLabels) {
2620
- await this.ensureLabelAccess({
2621
- originator: originator,
2622
- label: lbl,
2623
- reason: args.description,
2624
- usageType: 'apply'
2625
- });
1805
+ if (args.labels) {
1806
+ for (const lbl of args.labels) {
1807
+ await this.ensureLabelAccess({
1808
+ originator: originator,
1809
+ label: lbl,
1810
+ reason: args.description,
1811
+ usageType: 'apply'
1812
+ });
1813
+ }
2626
1814
  }
2627
1815
  /**
2628
1816
  * 4) Force signAndProcess=false unless the originator is admin and explicitly sets it to true.
@@ -2800,132 +1988,74 @@ class WalletPermissionsManager {
2800
1988
  }
2801
1989
  async listActions(...args) {
2802
1990
  const [requestArgs, originator] = args;
2803
- // 1) Identify unique P-modules involved (one per schemeID, preserving label order)
2804
- const pModulesByScheme = new Map();
2805
- const nonPLabels = this.splitLabelsByPermissionModule(requestArgs.labels, pModulesByScheme);
2806
- // 2) Check permissions for non-P labels
2807
- for (const lbl of nonPLabels) {
2808
- await this.ensureLabelAccess({
2809
- originator: originator,
2810
- label: lbl,
2811
- reason: 'listActions',
2812
- usageType: 'list'
2813
- });
2814
- }
2815
- // 3) Call underlying wallet, with P-module transformations if needed
2816
- let results;
2817
- if (pModulesByScheme.size > 0) {
2818
- // P-modules are involved - chain transformations
2819
- const pModules = Array.from(pModulesByScheme.values());
2820
- // Chain onRequest calls through all modules in order
2821
- let transformedArgs = requestArgs;
2822
- for (const module of pModules) {
2823
- const transformed = await module.onRequest({
2824
- method: 'listActions',
2825
- args: transformedArgs,
2826
- originator: originator
2827
- });
2828
- transformedArgs = transformed.args;
2829
- }
2830
- // Call underlying wallet with transformed args
2831
- results = await this.underlying.listActions(transformedArgs, originator);
2832
- // Chain onResponse calls in reverse order
2833
- for (let i = pModules.length - 1; i >= 0; i--) {
2834
- results = await pModules[i].onResponse(results, {
2835
- method: 'listActions',
2836
- originator: originator
1991
+ // for each label, ensure label access
1992
+ if (requestArgs.labels) {
1993
+ for (const lbl of requestArgs.labels) {
1994
+ await this.ensureLabelAccess({
1995
+ originator: originator,
1996
+ label: lbl,
1997
+ reason: 'listActions',
1998
+ usageType: 'list'
2837
1999
  });
2838
2000
  }
2839
2001
  }
2840
- else {
2841
- // No P-modules - call underlying wallet directly
2842
- results = await this.underlying.listActions(...args);
2002
+ const results = await this.underlying.listActions(...args);
2003
+ // Transparently decrypt transaction metadata, if configured to do so.
2004
+ if (results.actions) {
2005
+ for (let i = 0; i < results.actions.length; i++) {
2006
+ if (results.actions[i].description) {
2007
+ results.actions[i].description = await this.maybeDecryptMetadata(results.actions[i].description);
2008
+ }
2009
+ if (results.actions[i].inputs) {
2010
+ for (let j = 0; j < results.actions[i].inputs.length; j++) {
2011
+ if (results.actions[i].inputs[j].inputDescription) {
2012
+ results.actions[i].inputs[j].inputDescription = await this.maybeDecryptMetadata(results.actions[i].inputs[j].inputDescription);
2013
+ }
2014
+ }
2015
+ }
2016
+ if (results.actions[i].outputs) {
2017
+ for (let j = 0; j < results.actions[i].outputs.length; j++) {
2018
+ if (results.actions[i].outputs[j].outputDescription) {
2019
+ results.actions[i].outputs[j].outputDescription = await this.maybeDecryptMetadata(results.actions[i].outputs[j].outputDescription);
2020
+ }
2021
+ if (results.actions[i].outputs[j].customInstructions) {
2022
+ results.actions[i].outputs[j].customInstructions = await this.maybeDecryptMetadata(results.actions[i].outputs[j].customInstructions);
2023
+ }
2024
+ }
2025
+ }
2026
+ }
2843
2027
  }
2844
- // 4) Transparently decrypt transaction metadata, if configured to do so.
2845
- return await this.decryptListActionsMetadata(results);
2028
+ return results;
2846
2029
  }
2847
2030
  async internalizeAction(...args) {
2848
- var _a;
2849
2031
  const [requestArgs, originator] = args;
2850
- // 1) Identify unique P-modules involved (one per schemeID) from both baskets and labels
2851
- const pModulesByScheme = new Map();
2852
- const nonPBaskets = [];
2853
- // Check baskets for p modules
2032
+ // If the transaction is inserting outputs into baskets, we also ensure basket permission
2854
2033
  for (const outIndex in requestArgs.outputs) {
2855
2034
  const out = requestArgs.outputs[outIndex];
2856
2035
  if (out.protocol === 'basket insertion') {
2857
- const basket = out.insertionRemittance.basket;
2858
- if (basket.startsWith('p ')) {
2859
- const schemeID = basket.split(' ')[1];
2860
- this.addPModuleByScheme(schemeID, 'basket', pModulesByScheme);
2861
- }
2862
- else {
2863
- // Track non-P baskets for normal permission checks
2864
- nonPBaskets.push({
2865
- outIndex,
2866
- basket,
2867
- customInstructions: out.insertionRemittance.customInstructions
2868
- });
2869
- }
2870
- }
2871
- }
2872
- // Check labels for p modules
2873
- const nonPLabels = this.splitLabelsByPermissionModule(requestArgs.labels, pModulesByScheme);
2874
- // 2) Check permissions for non-P baskets
2875
- for (const { outIndex, basket, customInstructions } of nonPBaskets) {
2876
- await this.ensureBasketAccess({
2877
- originator: originator,
2878
- basket,
2879
- reason: requestArgs.description,
2880
- usageType: 'insertion'
2881
- });
2882
- if (customInstructions) {
2883
- requestArgs.outputs[outIndex].insertionRemittance.customInstructions =
2884
- await this.maybeEncryptMetadata(customInstructions);
2885
- }
2886
- }
2887
- // 3) Check permissions for non-P labels
2888
- for (const lbl of nonPLabels) {
2889
- await this.ensureLabelAccess({
2890
- originator: originator,
2891
- label: lbl,
2892
- reason: requestArgs.description,
2893
- usageType: 'apply'
2894
- });
2895
- }
2896
- // 4) Call underlying wallet, with P-module transformations if needed
2897
- if (pModulesByScheme.size > 0) {
2898
- // P-modules are involved - chain transformations
2899
- const pModules = Array.from(pModulesByScheme.values());
2900
- // Chain onRequest calls through all modules in order
2901
- let transformedArgs = requestArgs;
2902
- for (const module of pModules) {
2903
- const transformed = await module.onRequest({
2904
- method: 'internalizeAction',
2905
- args: transformedArgs,
2906
- originator: originator
2036
+ // Delegate to permission module if needed
2037
+ const pModuleResult = await this.delegateToPModuleIfNeeded(out.insertionRemittance.basket, 'internalizeAction', requestArgs, originator, async (transformedArgs) => {
2038
+ if (out.insertionRemittance.customInstructions) {
2039
+ ;
2040
+ transformedArgs.outputs[outIndex].insertionRemittance.customInstructions =
2041
+ await this.maybeEncryptMetadata(out.insertionRemittance.customInstructions);
2042
+ }
2043
+ return await this.underlying.internalizeAction(transformedArgs, originator);
2907
2044
  });
2908
- transformedArgs = transformed.args;
2909
- }
2910
- // Encrypt custom instructions for p basket outputs
2911
- for (const outIndex in transformedArgs.outputs) {
2912
- const out = transformedArgs.outputs[outIndex];
2913
- if (out.protocol === 'basket insertion' && ((_a = out.insertionRemittance) === null || _a === void 0 ? void 0 : _a.customInstructions)) {
2914
- out.insertionRemittance.customInstructions = await this.maybeEncryptMetadata(out.insertionRemittance.customInstructions);
2045
+ if (pModuleResult !== null) {
2046
+ return pModuleResult;
2915
2047
  }
2916
- }
2917
- // Call underlying wallet with transformed args
2918
- let results = await this.underlying.internalizeAction(transformedArgs, originator);
2919
- // Chain onResponse calls in reverse order
2920
- for (let i = pModules.length - 1; i >= 0; i--) {
2921
- results = await pModules[i].onResponse(results, {
2922
- method: 'internalizeAction',
2923
- originator: originator
2048
+ await this.ensureBasketAccess({
2049
+ originator: originator,
2050
+ basket: out.insertionRemittance.basket,
2051
+ reason: requestArgs.description,
2052
+ usageType: 'insertion'
2924
2053
  });
2054
+ if (out.insertionRemittance.customInstructions) {
2055
+ requestArgs.outputs[outIndex].insertionRemittance.customInstructions = await this.maybeEncryptMetadata(out.insertionRemittance.customInstructions);
2056
+ }
2925
2057
  }
2926
- return results;
2927
2058
  }
2928
- // No P-modules - call underlying wallet directly
2929
2059
  return this.underlying.internalizeAction(...args);
2930
2060
  }
2931
2061
  async listOutputs(...args) {
@@ -3228,17 +2358,80 @@ class WalletPermissionsManager {
3228
2358
  return this.underlying.isAuthenticated(...args);
3229
2359
  }
3230
2360
  async waitForAuthentication(...args) {
2361
+ var _a, _b, _c, _d, _e, _f, _g;
3231
2362
  let [_, originator] = args;
3232
2363
  if (this.config.seekGroupedPermission && originator) {
3233
2364
  const { normalized: normalizedOriginator } = this.prepareOriginator(originator);
3234
2365
  originator = normalizedOriginator;
3235
2366
  // 1. Fetch manifest.json from the originator
3236
- const groupPermissions = await this.fetchManifestGroupPermissions(originator);
2367
+ let groupPermissions;
2368
+ try {
2369
+ const proto = originator.startsWith('localhost:') ? 'http' : 'https';
2370
+ const response = await fetch(`${proto}://${originator}/manifest.json`);
2371
+ if (response.ok) {
2372
+ const manifest = await response.json();
2373
+ if ((_a = manifest === null || manifest === void 0 ? void 0 : manifest.babbage) === null || _a === void 0 ? void 0 : _a.groupPermissions) {
2374
+ groupPermissions = manifest.babbage.groupPermissions;
2375
+ }
2376
+ }
2377
+ }
2378
+ catch (e) {
2379
+ // Ignore fetch/parse errors, just proceed without group permissions.
2380
+ }
3237
2381
  if (groupPermissions) {
3238
2382
  // 2. Filter out already-granted permissions
3239
- const permissionsToRequest = await this.filterAlreadyGrantedPermissions(originator, groupPermissions);
2383
+ const permissionsToRequest = {
2384
+ protocolPermissions: [],
2385
+ basketAccess: [],
2386
+ certificateAccess: []
2387
+ };
2388
+ if (groupPermissions.spendingAuthorization) {
2389
+ const hasAuth = await this.hasSpendingAuthorization({
2390
+ originator,
2391
+ satoshis: groupPermissions.spendingAuthorization.amount
2392
+ });
2393
+ if (!hasAuth) {
2394
+ permissionsToRequest.spendingAuthorization = groupPermissions.spendingAuthorization;
2395
+ }
2396
+ }
2397
+ for (const p of groupPermissions.protocolPermissions || []) {
2398
+ const hasPerm = await this.hasProtocolPermission({
2399
+ originator,
2400
+ privileged: false, // Privilege is never allowed here
2401
+ protocolID: p.protocolID,
2402
+ counterparty: p.counterparty || 'self'
2403
+ });
2404
+ if (!hasPerm) {
2405
+ permissionsToRequest.protocolPermissions.push(p);
2406
+ }
2407
+ }
2408
+ for (const b of groupPermissions.basketAccess || []) {
2409
+ const hasAccess = await this.hasBasketAccess({
2410
+ originator,
2411
+ basket: b.basket
2412
+ });
2413
+ if (!hasAccess) {
2414
+ permissionsToRequest.basketAccess.push(b);
2415
+ }
2416
+ }
2417
+ for (const c of groupPermissions.certificateAccess || []) {
2418
+ const hasAccess = await this.hasCertificateAccess({
2419
+ originator,
2420
+ privileged: false, // Privilege is never allowed here for security
2421
+ verifier: c.verifierPublicKey,
2422
+ certType: c.type,
2423
+ fields: c.fields
2424
+ });
2425
+ if (!hasAccess) {
2426
+ permissionsToRequest.certificateAccess.push(c);
2427
+ }
2428
+ }
3240
2429
  // 3. If any permissions are left to request, start the flow
3241
- if (this.hasAnyPermissionsToRequest(permissionsToRequest)) {
2430
+ const hasRequests = permissionsToRequest.spendingAuthorization ||
2431
+ ((_c = (_b = permissionsToRequest.protocolPermissions) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0) > 0 ||
2432
+ ((_e = (_d = permissionsToRequest.basketAccess) === null || _d === void 0 ? void 0 : _d.length) !== null && _e !== void 0 ? _e : 0) > 0 ||
2433
+ ((_g = (_f = permissionsToRequest.certificateAccess) === null || _f === void 0 ? void 0 : _f.length) !== null && _g !== void 0 ? _g : 0) > 0;
2434
+ if (hasRequests) {
3242
2435
  const key = `group:${originator}`;
3243
2436
  if (this.activeRequests.has(key)) {
3244
2437
  // Another call is already waiting, piggyback on it
@@ -3310,7 +2503,6 @@ class WalletPermissionsManager {
3310
2503
  * Checks if the given label is admin-reserved per BRC-100 rules:
3311
2504
  *
3312
2505
  * - Must not start with `admin` (admin-reserved)
3313
- * - Must not start with `p ` (permissioned labels requiring a permission module)
3314
2506
  *
3315
2507
  * If it violates these rules and the caller is not admin, we consider it "admin-only."
3316
2508
  */
@@ -3318,9 +2510,6 @@ class WalletPermissionsManager {
3318
2510
  if (label.startsWith('admin')) {
3319
2511
  return true;
3320
2512
  }
3321
- if (label.startsWith('p ')) {
3322
- return true;
3323
- }
3324
2513
  return false;
3325
2514
  }
3326
2515
  /**
@@ -3457,7 +2646,6 @@ class WalletPermissionsManager {
3457
2646
  }
3458
2647
  }
3459
2648
  exports.WalletPermissionsManager = WalletPermissionsManager;
3460
- WalletPermissionsManager.MANIFEST_CACHE_TTL_MS = 5 * 60 * 1000;
3461
2649
  /** How long a cached permission remains valid (5 minutes). */
3462
2650
  WalletPermissionsManager.CACHE_TTL_MS = 5 * 60 * 1000;
3463
2651
  /** Window during which freshly granted permissions are auto-allowed (except spending). */