@bsv/wallet-toolbox-client 2.0.5-beta.1 → 2.0.6
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/src/Wallet.d.ts.map +1 -1
- package/out/src/Wallet.js +7 -0
- package/out/src/Wallet.js.map +1 -1
- package/out/src/WalletPermissionsManager.d.ts +13 -0
- package/out/src/WalletPermissionsManager.d.ts.map +1 -1
- package/out/src/WalletPermissionsManager.js +349 -133
- package/out/src/WalletPermissionsManager.js.map +1 -1
- package/out/src/services/chaintracker/chaintracks/util/validBulkHeaderFilesByFileHash.d.ts.map +1 -1
- package/out/src/services/chaintracker/chaintracks/util/validBulkHeaderFilesByFileHash.js +17 -4
- package/out/src/services/chaintracker/chaintracks/util/validBulkHeaderFilesByFileHash.js.map +1 -1
- package/package.json +1 -1
- package/out/src/sdk/validationHelpers.d.ts +0 -303
- package/out/src/sdk/validationHelpers.d.ts.map +0 -1
- package/out/src/sdk/validationHelpers.js +0 -632
- package/out/src/sdk/validationHelpers.js.map +0 -1
- package/out/src/utility/ReaderUint8Array.d.ts +0 -28
- package/out/src/utility/ReaderUint8Array.d.ts.map +0 -1
- package/out/src/utility/ReaderUint8Array.js +0 -166
- package/out/src/utility/ReaderUint8Array.js.map +0 -1
|
@@ -96,6 +96,7 @@ class WalletPermissionsManager {
|
|
|
96
96
|
this.recentGrants = new Map();
|
|
97
97
|
this.manifestCache = new Map();
|
|
98
98
|
this.manifestFetchInProgress = new Map();
|
|
99
|
+
this.groupedPermissionFlowTail = new Map();
|
|
99
100
|
this.pactEstablishedCache = new Map();
|
|
100
101
|
this.underlying = underlyingWallet;
|
|
101
102
|
this.adminOriginator = this.normalizeOriginator(adminOriginator) || adminOriginator;
|
|
@@ -670,6 +671,7 @@ class WalletPermissionsManager {
|
|
|
670
671
|
privileged,
|
|
671
672
|
protocolID,
|
|
672
673
|
counterparty,
|
|
674
|
+
usageType,
|
|
673
675
|
reason,
|
|
674
676
|
renewal: true,
|
|
675
677
|
previousToken: token
|
|
@@ -687,6 +689,7 @@ class WalletPermissionsManager {
|
|
|
687
689
|
privileged,
|
|
688
690
|
protocolID,
|
|
689
691
|
counterparty,
|
|
692
|
+
usageType,
|
|
690
693
|
reason,
|
|
691
694
|
renewal: false
|
|
692
695
|
});
|
|
@@ -732,6 +735,7 @@ class WalletPermissionsManager {
|
|
|
732
735
|
type: 'basket',
|
|
733
736
|
originator,
|
|
734
737
|
basket,
|
|
738
|
+
usageType,
|
|
735
739
|
reason,
|
|
736
740
|
renewal: true,
|
|
737
741
|
previousToken: token
|
|
@@ -747,6 +751,7 @@ class WalletPermissionsManager {
|
|
|
747
751
|
type: 'basket',
|
|
748
752
|
originator,
|
|
749
753
|
basket,
|
|
754
|
+
usageType,
|
|
750
755
|
reason,
|
|
751
756
|
renewal: false
|
|
752
757
|
});
|
|
@@ -796,6 +801,7 @@ class WalletPermissionsManager {
|
|
|
796
801
|
originator,
|
|
797
802
|
privileged,
|
|
798
803
|
certificate: { verifier, certType, fields },
|
|
804
|
+
usageType,
|
|
799
805
|
reason,
|
|
800
806
|
renewal: true,
|
|
801
807
|
previousToken: token
|
|
@@ -811,6 +817,7 @@ class WalletPermissionsManager {
|
|
|
811
817
|
originator,
|
|
812
818
|
privileged,
|
|
813
819
|
certificate: { verifier, certType, fields },
|
|
820
|
+
usageType,
|
|
814
821
|
reason,
|
|
815
822
|
renewal: false
|
|
816
823
|
});
|
|
@@ -977,47 +984,58 @@ class WalletPermissionsManager {
|
|
|
977
984
|
basketAccess: [],
|
|
978
985
|
certificateAccess: []
|
|
979
986
|
};
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
987
|
+
const [spendingAuthorization, protocolPermissions, basketAccess, certificateAccess] = await Promise.all([
|
|
988
|
+
(async () => {
|
|
989
|
+
if (!groupPermissions.spendingAuthorization)
|
|
990
|
+
return undefined;
|
|
991
|
+
const hasAuth = await this.hasSpendingAuthorization({
|
|
992
|
+
originator,
|
|
993
|
+
satoshis: groupPermissions.spendingAuthorization.amount
|
|
994
|
+
});
|
|
995
|
+
return hasAuth ? undefined : groupPermissions.spendingAuthorization;
|
|
996
|
+
})(),
|
|
997
|
+
(async () => {
|
|
998
|
+
const protocolChecks = await Promise.all((groupPermissions.protocolPermissions || []).map(async (p) => {
|
|
999
|
+
const hasPerm = await this.hasProtocolPermission({
|
|
1000
|
+
originator,
|
|
1001
|
+
privileged: false,
|
|
1002
|
+
protocolID: p.protocolID,
|
|
1003
|
+
counterparty: p.counterparty || 'self'
|
|
1004
|
+
});
|
|
1005
|
+
return hasPerm ? null : p;
|
|
1006
|
+
}));
|
|
1007
|
+
return protocolChecks.filter(Boolean);
|
|
1008
|
+
})(),
|
|
1009
|
+
(async () => {
|
|
1010
|
+
const basketChecks = await Promise.all((groupPermissions.basketAccess || []).map(async (b) => {
|
|
1011
|
+
const hasAccess = await this.hasBasketAccess({
|
|
1012
|
+
originator,
|
|
1013
|
+
basket: b.basket
|
|
1014
|
+
});
|
|
1015
|
+
return hasAccess ? null : b;
|
|
1016
|
+
}));
|
|
1017
|
+
return basketChecks.filter(Boolean);
|
|
1018
|
+
})(),
|
|
1019
|
+
(async () => {
|
|
1020
|
+
const certChecks = await Promise.all((groupPermissions.certificateAccess || []).map(async (c) => {
|
|
1021
|
+
const hasAccess = await this.hasCertificateAccess({
|
|
1022
|
+
originator,
|
|
1023
|
+
privileged: false,
|
|
1024
|
+
verifier: c.verifierPublicKey,
|
|
1025
|
+
certType: c.type,
|
|
1026
|
+
fields: c.fields
|
|
1027
|
+
});
|
|
1028
|
+
return hasAccess ? null : c;
|
|
1029
|
+
}));
|
|
1030
|
+
return certChecks.filter(Boolean);
|
|
1031
|
+
})()
|
|
1032
|
+
]);
|
|
1033
|
+
if (spendingAuthorization) {
|
|
1034
|
+
permissionsToRequest.spendingAuthorization = spendingAuthorization;
|
|
1035
|
+
}
|
|
1036
|
+
permissionsToRequest.protocolPermissions = protocolPermissions;
|
|
1037
|
+
permissionsToRequest.basketAccess = basketAccess;
|
|
1038
|
+
permissionsToRequest.certificateAccess = certificateAccess;
|
|
1021
1039
|
return permissionsToRequest;
|
|
1022
1040
|
}
|
|
1023
1041
|
hasAnyPermissionsToRequest(permissions) {
|
|
@@ -1098,18 +1116,16 @@ class WalletPermissionsManager {
|
|
|
1098
1116
|
if (!((_a = counterpartyPermissions === null || counterpartyPermissions === void 0 ? void 0 : counterpartyPermissions.protocols) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
1099
1117
|
return null;
|
|
1100
1118
|
}
|
|
1101
|
-
const
|
|
1102
|
-
for (const p of counterpartyPermissions.protocols) {
|
|
1119
|
+
const protocolChecks = await Promise.all(counterpartyPermissions.protocols.map(async (p) => {
|
|
1103
1120
|
const hasPerm = await this.hasProtocolPermission({
|
|
1104
1121
|
originator,
|
|
1105
1122
|
privileged: false,
|
|
1106
1123
|
protocolID: p.protocolID,
|
|
1107
1124
|
counterparty
|
|
1108
1125
|
});
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
}
|
|
1126
|
+
return hasPerm ? null : p;
|
|
1127
|
+
}));
|
|
1128
|
+
const protocolsToRequest = protocolChecks.filter(Boolean);
|
|
1113
1129
|
if (protocolsToRequest.length === 0) {
|
|
1114
1130
|
this.markPactEstablished(originator, counterparty);
|
|
1115
1131
|
return null;
|
|
@@ -1201,21 +1217,22 @@ class WalletPermissionsManager {
|
|
|
1201
1217
|
const permissionsToRequest = {
|
|
1202
1218
|
protocolPermissions: []
|
|
1203
1219
|
};
|
|
1204
|
-
|
|
1220
|
+
const protocolChecks = await Promise.all(manifestLevel2ForThisPeer.map(async (p) => {
|
|
1205
1221
|
const hasPerm = await this.hasProtocolPermission({
|
|
1206
1222
|
originator,
|
|
1207
1223
|
privileged,
|
|
1208
1224
|
protocolID: p.protocolID,
|
|
1209
1225
|
counterparty: p.counterparty
|
|
1210
1226
|
});
|
|
1211
|
-
|
|
1212
|
-
|
|
1227
|
+
return hasPerm
|
|
1228
|
+
? null
|
|
1229
|
+
: {
|
|
1213
1230
|
protocolID: p.protocolID,
|
|
1214
1231
|
counterparty: p.counterparty,
|
|
1215
1232
|
description: p.description
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
|
|
1233
|
+
};
|
|
1234
|
+
}));
|
|
1235
|
+
permissionsToRequest.protocolPermissions = protocolChecks.filter(Boolean);
|
|
1219
1236
|
if (!this.hasAnyPermissionsToRequest(permissionsToRequest)) {
|
|
1220
1237
|
return null;
|
|
1221
1238
|
}
|
|
@@ -1256,6 +1273,28 @@ class WalletPermissionsManager {
|
|
|
1256
1273
|
const satisfied = await this.checkSpecificPermissionAfterGroupFlow(currentRequest);
|
|
1257
1274
|
return satisfied ? true : null;
|
|
1258
1275
|
}
|
|
1276
|
+
async withGroupedPermissionFlowLock(originator, fn) {
|
|
1277
|
+
const priorTail = this.groupedPermissionFlowTail.get(originator) || Promise.resolve();
|
|
1278
|
+
const safePriorTail = priorTail.catch(() => { });
|
|
1279
|
+
let release;
|
|
1280
|
+
const gate = new Promise(resolve => {
|
|
1281
|
+
release = resolve;
|
|
1282
|
+
});
|
|
1283
|
+
const currentTail = safePriorTail.then(() => gate);
|
|
1284
|
+
this.groupedPermissionFlowTail.set(originator, currentTail);
|
|
1285
|
+
await safePriorTail;
|
|
1286
|
+
try {
|
|
1287
|
+
return await fn();
|
|
1288
|
+
}
|
|
1289
|
+
finally {
|
|
1290
|
+
release === null || release === void 0 ? void 0 : release();
|
|
1291
|
+
currentTail.finally(() => {
|
|
1292
|
+
if (this.groupedPermissionFlowTail.get(originator) === currentTail) {
|
|
1293
|
+
this.groupedPermissionFlowTail.delete(originator);
|
|
1294
|
+
}
|
|
1295
|
+
});
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1259
1298
|
async checkSpecificPermissionAfterGroupFlow(request) {
|
|
1260
1299
|
var _a, _b, _c;
|
|
1261
1300
|
switch (request.type) {
|
|
@@ -1390,6 +1429,14 @@ class WalletPermissionsManager {
|
|
|
1390
1429
|
originator: normalizedOriginator,
|
|
1391
1430
|
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
|
|
1392
1431
|
};
|
|
1432
|
+
const key = this.buildActiveRequestKey(preparedRequest);
|
|
1433
|
+
// If there's already a queue for the same resource, we piggyback on it
|
|
1434
|
+
const existingQueue = this.activeRequests.get(key);
|
|
1435
|
+
if (existingQueue && existingQueue.pending.length > 0) {
|
|
1436
|
+
return new Promise((resolve, reject) => {
|
|
1437
|
+
existingQueue.pending.push({ resolve, reject });
|
|
1438
|
+
});
|
|
1439
|
+
}
|
|
1393
1440
|
const pactResult = await this.maybeRequestPact(preparedRequest);
|
|
1394
1441
|
if (pactResult !== null) {
|
|
1395
1442
|
return pactResult;
|
|
@@ -1398,51 +1445,73 @@ class WalletPermissionsManager {
|
|
|
1398
1445
|
if (peerGroupResult !== null) {
|
|
1399
1446
|
return peerGroupResult;
|
|
1400
1447
|
}
|
|
1401
|
-
const
|
|
1448
|
+
const hadPendingGroupedFlowBefore = this.config.seekGroupedPermission && this.groupedPermissionFlowTail.has(preparedRequest.originator);
|
|
1449
|
+
let groupResult = null;
|
|
1450
|
+
if (this.config.seekGroupedPermission) {
|
|
1451
|
+
groupResult = await this.withGroupedPermissionFlowLock(preparedRequest.originator, async () => {
|
|
1452
|
+
return await this.maybeRequestGroupedPermissions(preparedRequest);
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
else {
|
|
1456
|
+
groupResult = await this.maybeRequestGroupedPermissions(preparedRequest);
|
|
1457
|
+
}
|
|
1402
1458
|
if (groupResult !== null) {
|
|
1403
1459
|
return groupResult;
|
|
1404
1460
|
}
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1461
|
+
if (this.config.seekGroupedPermission && hadPendingGroupedFlowBefore) {
|
|
1462
|
+
const satisfiedAfterGroup = await this.checkSpecificPermissionAfterGroupFlow(preparedRequest);
|
|
1463
|
+
if (satisfiedAfterGroup) {
|
|
1464
|
+
return true;
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
const existingQueueAfterGroups = this.activeRequests.get(key);
|
|
1468
|
+
if (existingQueueAfterGroups && existingQueueAfterGroups.pending.length > 0) {
|
|
1409
1469
|
return new Promise((resolve, reject) => {
|
|
1410
|
-
|
|
1470
|
+
existingQueueAfterGroups.pending.push({ resolve, reject });
|
|
1411
1471
|
});
|
|
1412
1472
|
}
|
|
1413
|
-
// Otherwise, create a new queue with a single entry
|
|
1414
|
-
// Return a promise that resolves or rejects once the user grants/denies
|
|
1415
1473
|
return new Promise(async (resolve, reject) => {
|
|
1416
1474
|
this.activeRequests.set(key, {
|
|
1417
1475
|
request: preparedRequest,
|
|
1418
1476
|
pending: [{ resolve, reject }]
|
|
1419
1477
|
});
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1478
|
+
try {
|
|
1479
|
+
// Fire the relevant onXXXRequested event (which one depends on r.type)
|
|
1480
|
+
switch (preparedRequest.type) {
|
|
1481
|
+
case 'protocol':
|
|
1482
|
+
await this.callEvent('onProtocolPermissionRequested', {
|
|
1483
|
+
...preparedRequest,
|
|
1484
|
+
requestID: key
|
|
1485
|
+
});
|
|
1486
|
+
break;
|
|
1487
|
+
case 'basket':
|
|
1488
|
+
await this.callEvent('onBasketAccessRequested', {
|
|
1489
|
+
...preparedRequest,
|
|
1490
|
+
requestID: key
|
|
1491
|
+
});
|
|
1492
|
+
break;
|
|
1493
|
+
case 'certificate':
|
|
1494
|
+
await this.callEvent('onCertificateAccessRequested', {
|
|
1495
|
+
...preparedRequest,
|
|
1496
|
+
requestID: key
|
|
1497
|
+
});
|
|
1498
|
+
break;
|
|
1499
|
+
case 'spending':
|
|
1500
|
+
await this.callEvent('onSpendingAuthorizationRequested', {
|
|
1501
|
+
...preparedRequest,
|
|
1502
|
+
requestID: key
|
|
1503
|
+
});
|
|
1504
|
+
break;
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
catch (e) {
|
|
1508
|
+
const matching = this.activeRequests.get(key);
|
|
1509
|
+
if (matching) {
|
|
1510
|
+
for (const p of matching.pending) {
|
|
1511
|
+
p.reject(e);
|
|
1512
|
+
}
|
|
1513
|
+
this.activeRequests.delete(key);
|
|
1514
|
+
}
|
|
1446
1515
|
}
|
|
1447
1516
|
});
|
|
1448
1517
|
}
|
|
@@ -2510,6 +2579,137 @@ class WalletPermissionsManager {
|
|
|
2510
2579
|
return false;
|
|
2511
2580
|
}
|
|
2512
2581
|
}
|
|
2582
|
+
async revokePermissions(oldTokens) {
|
|
2583
|
+
return await this.revokePermissionTokensBestEffort(oldTokens);
|
|
2584
|
+
}
|
|
2585
|
+
async revokeAllForOriginator(originator, opts) {
|
|
2586
|
+
const preparedOriginator = this.prepareOriginator(originator);
|
|
2587
|
+
const include = {
|
|
2588
|
+
protocol: true,
|
|
2589
|
+
basket: true,
|
|
2590
|
+
certificate: true,
|
|
2591
|
+
spending: true,
|
|
2592
|
+
...(opts || {})
|
|
2593
|
+
};
|
|
2594
|
+
const [protocolTokens, basketTokens, certificateTokens, spendingTokens] = await Promise.all([
|
|
2595
|
+
include.protocol ? this.listProtocolPermissions({ originator }) : Promise.resolve([]),
|
|
2596
|
+
include.basket ? this.listBasketAccess({ originator }) : Promise.resolve([]),
|
|
2597
|
+
include.certificate ? this.listCertificateAccess({ originator }) : Promise.resolve([]),
|
|
2598
|
+
include.spending
|
|
2599
|
+
? this.listSpendingAuthorizations({ originator: preparedOriginator.normalized })
|
|
2600
|
+
: Promise.resolve([])
|
|
2601
|
+
]);
|
|
2602
|
+
const spendingTokenList = spendingTokens.length ? [spendingTokens[0]] : [];
|
|
2603
|
+
const allTokens = [...protocolTokens, ...basketTokens, ...certificateTokens, ...spendingTokenList];
|
|
2604
|
+
const seen = new Set();
|
|
2605
|
+
const deduped = allTokens.filter(t => {
|
|
2606
|
+
const key = `${t.txid}.${t.outputIndex}`;
|
|
2607
|
+
if (seen.has(key))
|
|
2608
|
+
return false;
|
|
2609
|
+
seen.add(key);
|
|
2610
|
+
return true;
|
|
2611
|
+
});
|
|
2612
|
+
return await this.revokePermissions(deduped);
|
|
2613
|
+
}
|
|
2614
|
+
async revokePermissionTokensBestEffort(items) {
|
|
2615
|
+
const CHUNK = 15;
|
|
2616
|
+
return this.runBestEffortBatches(items, CHUNK, async (chunk) => {
|
|
2617
|
+
await this.revokePermissionTokensChunk(chunk);
|
|
2618
|
+
return chunk;
|
|
2619
|
+
});
|
|
2620
|
+
}
|
|
2621
|
+
async revokePermissionTokensChunk(oldTokens) {
|
|
2622
|
+
if (!oldTokens.length)
|
|
2623
|
+
return;
|
|
2624
|
+
const inputBeef = new sdk_1.Beef();
|
|
2625
|
+
for (const token of oldTokens) {
|
|
2626
|
+
inputBeef.mergeBeef(sdk_1.Beef.fromBinary(token.tx));
|
|
2627
|
+
}
|
|
2628
|
+
const { signableTransaction } = await this.createAction({
|
|
2629
|
+
description: `Revoke ${oldTokens.length} permissions`,
|
|
2630
|
+
inputBEEF: inputBeef.toBinary(),
|
|
2631
|
+
inputs: oldTokens.map((t, i) => ({
|
|
2632
|
+
outpoint: `${t.txid}.${t.outputIndex}`,
|
|
2633
|
+
unlockingScriptLength: 73,
|
|
2634
|
+
inputDescription: `Consume old permission token #${i + 1}`
|
|
2635
|
+
})),
|
|
2636
|
+
options: {
|
|
2637
|
+
acceptDelayedBroadcast: true,
|
|
2638
|
+
randomizeOutputs: false,
|
|
2639
|
+
signAndProcess: false
|
|
2640
|
+
}
|
|
2641
|
+
}, this.adminOriginator);
|
|
2642
|
+
if (!(signableTransaction === null || signableTransaction === void 0 ? void 0 : signableTransaction.reference) || !signableTransaction.tx) {
|
|
2643
|
+
throw new Error('Failed to create signable transaction');
|
|
2644
|
+
}
|
|
2645
|
+
const tx = sdk_1.Transaction.fromAtomicBEEF(signableTransaction.tx);
|
|
2646
|
+
const normalizeTxid = (txid) => (txid !== null && txid !== void 0 ? txid : '').toLowerCase();
|
|
2647
|
+
const reverseHexTxid = (txid) => {
|
|
2648
|
+
const hex = normalizeTxid(txid);
|
|
2649
|
+
if (!/^[0-9a-f]{64}$/.test(hex))
|
|
2650
|
+
return hex;
|
|
2651
|
+
const bytes = hex.match(/../g);
|
|
2652
|
+
return bytes ? bytes.reverse().join('') : hex;
|
|
2653
|
+
};
|
|
2654
|
+
const matchesOutpointString = (outpoint, token) => {
|
|
2655
|
+
const dot = outpoint.lastIndexOf('.');
|
|
2656
|
+
const colon = outpoint.lastIndexOf(':');
|
|
2657
|
+
const sep = dot > colon ? dot : colon;
|
|
2658
|
+
if (sep === -1)
|
|
2659
|
+
return false;
|
|
2660
|
+
const txidPart = outpoint.slice(0, sep);
|
|
2661
|
+
const indexPart = outpoint.slice(sep + 1);
|
|
2662
|
+
const vout = Number(indexPart);
|
|
2663
|
+
if (!Number.isFinite(vout))
|
|
2664
|
+
return false;
|
|
2665
|
+
return normalizeTxid(txidPart) === normalizeTxid(token.txid) && vout === token.outputIndex;
|
|
2666
|
+
};
|
|
2667
|
+
const findInputIndexForToken = (token) => {
|
|
2668
|
+
return tx.inputs.findIndex((input) => {
|
|
2669
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
2670
|
+
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;
|
|
2671
|
+
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;
|
|
2672
|
+
if (typeof txidCandidate === 'string' && typeof voutCandidate === 'number') {
|
|
2673
|
+
const cand = normalizeTxid(txidCandidate);
|
|
2674
|
+
const target = normalizeTxid(token.txid);
|
|
2675
|
+
if (cand === target && voutCandidate === token.outputIndex)
|
|
2676
|
+
return true;
|
|
2677
|
+
if (cand === reverseHexTxid(token.txid) && voutCandidate === token.outputIndex)
|
|
2678
|
+
return true;
|
|
2679
|
+
}
|
|
2680
|
+
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;
|
|
2681
|
+
if (typeof outpointCandidate === 'string' && matchesOutpointString(outpointCandidate, token))
|
|
2682
|
+
return true;
|
|
2683
|
+
return false;
|
|
2684
|
+
});
|
|
2685
|
+
};
|
|
2686
|
+
const inputsToSign = oldTokens.map(token => {
|
|
2687
|
+
let permInputIndex = findInputIndexForToken(token);
|
|
2688
|
+
if (permInputIndex === -1 && tx.inputs.length === 1) {
|
|
2689
|
+
permInputIndex = 0;
|
|
2690
|
+
}
|
|
2691
|
+
if (permInputIndex === -1) {
|
|
2692
|
+
throw new Error('Unable to locate permission token input for revocation.');
|
|
2693
|
+
}
|
|
2694
|
+
return { token, permInputIndex };
|
|
2695
|
+
});
|
|
2696
|
+
const pushdrop = new sdk_1.PushDrop(this.underlying);
|
|
2697
|
+
const spends = {};
|
|
2698
|
+
const signed = await this.mapWithConcurrency(inputsToSign, 8, async ({ token, permInputIndex }) => {
|
|
2699
|
+
const unlocker = pushdrop.unlock(WalletPermissionsManager.PERM_TOKEN_ENCRYPTION_PROTOCOL, '1', 'self', 'all', false, 1, sdk_1.LockingScript.fromHex(token.outputScript));
|
|
2700
|
+
const unlockingScript = await unlocker.sign(tx, permInputIndex);
|
|
2701
|
+
return { permInputIndex, unlockingScriptHex: unlockingScript.toHex() };
|
|
2702
|
+
});
|
|
2703
|
+
for (const s of signed) {
|
|
2704
|
+
spends[s.permInputIndex] = { unlockingScript: s.unlockingScriptHex };
|
|
2705
|
+
}
|
|
2706
|
+
const { txid } = await this.underlying.signAction({
|
|
2707
|
+
reference: signableTransaction.reference,
|
|
2708
|
+
spends
|
|
2709
|
+
});
|
|
2710
|
+
if (!txid)
|
|
2711
|
+
throw new Error('Failed to finalize revoke transaction');
|
|
2712
|
+
}
|
|
2513
2713
|
/**
|
|
2514
2714
|
* Revokes a permission token by spending it with no replacement output.
|
|
2515
2715
|
* The manager builds a BRC-100 transaction that consumes the token, effectively invalidating it.
|
|
@@ -2644,22 +2844,28 @@ class WalletPermissionsManager {
|
|
|
2644
2844
|
const originalDescription = args.description;
|
|
2645
2845
|
const originalInputDescriptions = {};
|
|
2646
2846
|
const originalOutputDescriptions = {};
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
args.outputs[i].outputDescription = await this.maybeEncryptMetadata(args.outputs[i].outputDescription);
|
|
2847
|
+
const inputEncryptionTasks = (args.inputs || []).map(async (input, i) => {
|
|
2848
|
+
if (!input.inputDescription)
|
|
2849
|
+
return;
|
|
2850
|
+
originalInputDescriptions[i] = input.inputDescription;
|
|
2851
|
+
input.inputDescription = await this.maybeEncryptMetadata(input.inputDescription);
|
|
2852
|
+
});
|
|
2853
|
+
const outputEncryptionTasks = (args.outputs || []).map(async (output, i) => {
|
|
2854
|
+
if (output.outputDescription) {
|
|
2855
|
+
originalOutputDescriptions[i] = output.outputDescription;
|
|
2856
|
+
output.outputDescription = await this.maybeEncryptMetadata(output.outputDescription);
|
|
2658
2857
|
}
|
|
2659
|
-
if (
|
|
2660
|
-
|
|
2858
|
+
if (output.customInstructions) {
|
|
2859
|
+
output.customInstructions = await this.maybeEncryptMetadata(output.customInstructions);
|
|
2661
2860
|
}
|
|
2662
|
-
}
|
|
2861
|
+
});
|
|
2862
|
+
await Promise.all([
|
|
2863
|
+
(async () => {
|
|
2864
|
+
args.description = await this.maybeEncryptMetadata(args.description);
|
|
2865
|
+
})(),
|
|
2866
|
+
...inputEncryptionTasks,
|
|
2867
|
+
...outputEncryptionTasks
|
|
2868
|
+
]);
|
|
2663
2869
|
/**
|
|
2664
2870
|
* 6) Call the underlying wallet's createAction.
|
|
2665
2871
|
* - If P-modules are involved, chain request transformations through them first
|
|
@@ -3235,43 +3441,45 @@ class WalletPermissionsManager {
|
|
|
3235
3441
|
let [_, originator] = args;
|
|
3236
3442
|
if (this.config.seekGroupedPermission && originator) {
|
|
3237
3443
|
const { normalized: normalizedOriginator } = this.prepareOriginator(originator);
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
}
|
|
3253
|
-
else {
|
|
3254
|
-
// This is the first call, create a new request
|
|
3255
|
-
try {
|
|
3256
|
-
await new Promise(async (resolve, reject) => {
|
|
3257
|
-
this.activeRequests.set(key, {
|
|
3258
|
-
request: { originator: originator, permissions: permissionsToRequest },
|
|
3259
|
-
pending: [{ resolve, reject }]
|
|
3260
|
-
});
|
|
3261
|
-
await this.callEvent('onGroupedPermissionRequested', {
|
|
3262
|
-
requestID: key,
|
|
3263
|
-
originator,
|
|
3264
|
-
permissions: permissionsToRequest
|
|
3265
|
-
});
|
|
3444
|
+
const normalized = normalizedOriginator;
|
|
3445
|
+
await this.withGroupedPermissionFlowLock(normalized, async () => {
|
|
3446
|
+
// 1. Fetch manifest.json from the originator
|
|
3447
|
+
const groupPermissions = await this.fetchManifestGroupPermissions(normalized);
|
|
3448
|
+
if (groupPermissions) {
|
|
3449
|
+
// 2. Filter out already-granted permissions
|
|
3450
|
+
const permissionsToRequest = await this.filterAlreadyGrantedPermissions(normalized, groupPermissions);
|
|
3451
|
+
// 3. If any permissions are left to request, start the flow
|
|
3452
|
+
if (this.hasAnyPermissionsToRequest(permissionsToRequest)) {
|
|
3453
|
+
const key = `group:${normalized}`;
|
|
3454
|
+
if (this.activeRequests.has(key)) {
|
|
3455
|
+
// Another call is already waiting, piggyback on it
|
|
3456
|
+
await new Promise((resolve, reject) => {
|
|
3457
|
+
this.activeRequests.get(key).pending.push({ resolve, reject });
|
|
3266
3458
|
});
|
|
3267
3459
|
}
|
|
3268
|
-
|
|
3269
|
-
//
|
|
3270
|
-
|
|
3460
|
+
else {
|
|
3461
|
+
// This is the first call, create a new request
|
|
3462
|
+
try {
|
|
3463
|
+
await new Promise(async (resolve, reject) => {
|
|
3464
|
+
this.activeRequests.set(key, {
|
|
3465
|
+
request: { originator: normalized, permissions: permissionsToRequest },
|
|
3466
|
+
pending: [{ resolve, reject }]
|
|
3467
|
+
});
|
|
3468
|
+
await this.callEvent('onGroupedPermissionRequested', {
|
|
3469
|
+
requestID: key,
|
|
3470
|
+
originator: normalized,
|
|
3471
|
+
permissions: permissionsToRequest
|
|
3472
|
+
});
|
|
3473
|
+
});
|
|
3474
|
+
}
|
|
3475
|
+
catch (e) {
|
|
3476
|
+
// Permission was denied, re-throw to stop execution
|
|
3477
|
+
throw e;
|
|
3478
|
+
}
|
|
3271
3479
|
}
|
|
3272
3480
|
}
|
|
3273
3481
|
}
|
|
3274
|
-
}
|
|
3482
|
+
});
|
|
3275
3483
|
}
|
|
3276
3484
|
// Finally, after handling grouped permissions, call the underlying method.
|
|
3277
3485
|
return this.underlying.waitForAuthentication(...args);
|
|
@@ -3474,6 +3682,14 @@ class WalletPermissionsManager {
|
|
|
3474
3682
|
return `spend:${normalizedOriginator}:${(_e = r.spending) === null || _e === void 0 ? void 0 : _e.satoshis}`;
|
|
3475
3683
|
}
|
|
3476
3684
|
}
|
|
3685
|
+
buildActiveRequestKey(r) {
|
|
3686
|
+
var _a;
|
|
3687
|
+
const base = this.buildRequestKey(r);
|
|
3688
|
+
if (r.type === 'protocol' || r.type === 'basket' || r.type === 'certificate') {
|
|
3689
|
+
return `${base}:${(_a = r.usageType) !== null && _a !== void 0 ? _a : ''}`;
|
|
3690
|
+
}
|
|
3691
|
+
return base;
|
|
3692
|
+
}
|
|
3477
3693
|
}
|
|
3478
3694
|
exports.WalletPermissionsManager = WalletPermissionsManager;
|
|
3479
3695
|
WalletPermissionsManager.MANIFEST_CACHE_TTL_MS = 5 * 60 * 1000;
|