@metamask/snaps-controllers 9.16.0 → 9.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,7 +13,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
13
13
  var __importDefault = (this && this.__importDefault) || function (mod) {
14
14
  return (mod && mod.__esModule) ? mod : { "default": mod };
15
15
  };
16
- var _SnapController_instances, _SnapController_closeAllConnections, _SnapController_dynamicPermissions, _SnapController_environmentEndowmentPermissions, _SnapController_excludedPermissions, _SnapController_featureFlags, _SnapController_fetchFunction, _SnapController_idleTimeCheckInterval, _SnapController_maxIdleTime, _SnapController_encryptor, _SnapController_getMnemonic, _SnapController_getFeatureFlags, _SnapController_clientCryptography, _SnapController_detectSnapLocation, _SnapController_snapsRuntimeData, _SnapController_rollbackSnapshots, _SnapController_timeoutForLastRequestStatus, _SnapController_statusMachine, _SnapController_preinstalledSnaps, _SnapController_initializeStateMachine, _SnapController_registerMessageHandlers, _SnapController_handlePreinstalledSnaps, _SnapController_pollForLastRequestStatus, _SnapController_blockSnap, _SnapController_unblockSnap, _SnapController_assertIsInstallAllowed, _SnapController_assertCanInstallSnaps, _SnapController_assertCanUsePlatform, _SnapController_stopSnapsLastRequestPastMax, _SnapController_transition, _SnapController_terminateSnap, _SnapController_getSnapEncryptionKey, _SnapController_hasCachedEncryptionKey, _SnapController_decryptSnapState, _SnapController_encryptSnapState, _SnapController_handleInitialConnections, _SnapController_addSnapToSubject, _SnapController_removeSnapFromSubjects, _SnapController_revokeAllSnapPermissions, _SnapController_createApproval, _SnapController_updateApproval, _SnapController_resolveAllowlistVersion, _SnapController_add, _SnapController_startSnap, _SnapController_getEndowments, _SnapController_set, _SnapController_validateSnapPermissions, _SnapController_validatePlatformVersion, _SnapController_getExecutionTimeout, _SnapController_getRpcRequestHandler, _SnapController_createInterface, _SnapController_assertInterfaceExists, _SnapController_transformSnapRpcRequestResult, _SnapController_assertSnapRpcRequestResult, _SnapController_recordSnapRpcRequestStart, _SnapController_recordSnapRpcRequestFinish, _SnapController_getRollbackSnapshot, _SnapController_createRollbackSnapshot, _SnapController_rollbackSnap, _SnapController_rollbackSnaps, _SnapController_getRuntime, _SnapController_getRuntimeExpect, _SnapController_setupRuntime, _SnapController_calculatePermissionsChange, _SnapController_isSubjectConnectedToSnap, _SnapController_calculateConnectionsChange, _SnapController_updatePermissions, _SnapController_isValidUpdate, _SnapController_callLifecycleHook;
16
+ var _SnapController_instances, _SnapController_closeAllConnections, _SnapController_dynamicPermissions, _SnapController_environmentEndowmentPermissions, _SnapController_excludedPermissions, _SnapController_featureFlags, _SnapController_fetchFunction, _SnapController_idleTimeCheckInterval, _SnapController_maxIdleTime, _SnapController_encryptor, _SnapController_getMnemonic, _SnapController_getFeatureFlags, _SnapController_clientCryptography, _SnapController_detectSnapLocation, _SnapController_snapsRuntimeData, _SnapController_rollbackSnapshots, _SnapController_timeoutForLastRequestStatus, _SnapController_statusMachine, _SnapController_preinstalledSnaps, _SnapController_initializeStateMachine, _SnapController_registerMessageHandlers, _SnapController_handlePreinstalledSnaps, _SnapController_pollForLastRequestStatus, _SnapController_blockSnap, _SnapController_unblockSnap, _SnapController_assertIsInstallAllowed, _SnapController_assertCanInstallSnaps, _SnapController_assertCanUsePlatform, _SnapController_stopSnapsLastRequestPastMax, _SnapController_transition, _SnapController_terminateSnap, _SnapController_getSnapEncryptionKey, _SnapController_hasCachedEncryptionKey, _SnapController_decryptSnapState, _SnapController_encryptSnapState, _SnapController_getStateToPersist, _SnapController_persistSnapState, _SnapController_handleInitialConnections, _SnapController_addSnapToSubject, _SnapController_removeSnapFromSubjects, _SnapController_revokeAllSnapPermissions, _SnapController_createApproval, _SnapController_updateApproval, _SnapController_resolveAllowlistVersion, _SnapController_add, _SnapController_startSnap, _SnapController_getEndowments, _SnapController_set, _SnapController_validateSnapPermissions, _SnapController_validatePlatformVersion, _SnapController_getExecutionTimeout, _SnapController_getRpcRequestHandler, _SnapController_createInterface, _SnapController_assertInterfaceExists, _SnapController_transformSnapRpcRequestResult, _SnapController_assertSnapRpcRequestResult, _SnapController_recordSnapRpcRequestStart, _SnapController_recordSnapRpcRequestFinish, _SnapController_getRollbackSnapshot, _SnapController_createRollbackSnapshot, _SnapController_rollbackSnap, _SnapController_rollbackSnaps, _SnapController_getRuntime, _SnapController_getRuntimeExpect, _SnapController_setupRuntime, _SnapController_calculatePermissionsChange, _SnapController_isSubjectConnectedToSnap, _SnapController_calculateConnectionsChange, _SnapController_updatePermissions, _SnapController_isValidUpdate, _SnapController_callLifecycleHook, _SnapController_handleLock;
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.SnapController = exports.SNAP_APPROVAL_RESULT = exports.SNAP_APPROVAL_UPDATE = exports.SNAP_APPROVAL_INSTALL = exports.controllerName = void 0;
19
19
  const base_controller_1 = require("@metamask/base-controller");
@@ -24,6 +24,7 @@ const snaps_sdk_1 = require("@metamask/snaps-sdk");
24
24
  const snaps_utils_1 = require("@metamask/snaps-utils");
25
25
  const utils_1 = require("@metamask/utils");
26
26
  const fsm_1 = require("@xstate/fsm");
27
+ const async_mutex_1 = require("async-mutex");
27
28
  const nanoid_1 = require("nanoid");
28
29
  const semver_1 = __importDefault(require("semver"));
29
30
  const fsm_2 = require("../fsm.cjs");
@@ -169,6 +170,7 @@ class SnapController extends base_controller_1.BaseController {
169
170
  (0, snaps_utils_1.logError)(`Error when calling \`onUpdate\` lifecycle hook for snap "${id}": ${(0, snaps_sdk_1.getErrorMessage)(error)}`);
170
171
  });
171
172
  });
173
+ this.messagingSystem.subscribe('KeyringController:lock', __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_handleLock).bind(this));
172
174
  __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_initializeStateMachine).call(this);
173
175
  __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_registerMessageHandlers).call(this);
174
176
  if (__classPrivateFieldGet(this, _SnapController_preinstalledSnaps, "f")) {
@@ -398,17 +400,18 @@ class SnapController extends base_controller_1.BaseController {
398
400
  * @param encrypted - A flag to indicate whether to use encrypted storage or not.
399
401
  */
400
402
  async updateSnapState(snapId, newSnapState, encrypted) {
403
+ const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
401
404
  if (encrypted) {
402
- const encryptedState = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_encryptSnapState).call(this, snapId, newSnapState);
403
- this.update((state) => {
404
- state.snapStates[snapId] = encryptedState;
405
- });
405
+ runtime.state = newSnapState;
406
406
  }
407
407
  else {
408
- this.update((state) => {
409
- state.unencryptedSnapStates[snapId] = JSON.stringify(newSnapState);
410
- });
408
+ runtime.unencryptedState = newSnapState;
411
409
  }
410
+ // This is intentionally run asynchronously to avoid blocking the main
411
+ // thread.
412
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_persistSnapState).call(this, snapId, newSnapState, encrypted).catch((error) => {
413
+ (0, snaps_utils_1.logError)(error);
414
+ });
412
415
  }
413
416
  /**
414
417
  * Clears the state of the snap with the given id.
@@ -418,13 +421,17 @@ class SnapController extends base_controller_1.BaseController {
418
421
  * @param encrypted - A flag to indicate whether to use encrypted storage or not.
419
422
  */
420
423
  clearSnapState(snapId, encrypted) {
421
- this.update((state) => {
422
- if (encrypted) {
423
- state.snapStates[snapId] = null;
424
- }
425
- else {
426
- state.unencryptedSnapStates[snapId] = null;
427
- }
424
+ const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
425
+ if (encrypted) {
426
+ runtime.state = null;
427
+ }
428
+ else {
429
+ runtime.unencryptedState = null;
430
+ }
431
+ // This is intentionally run asynchronously to avoid blocking the main
432
+ // thread.
433
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_persistSnapState).call(this, snapId, null, encrypted).catch((error) => {
434
+ (0, snaps_utils_1.logError)(error);
428
435
  });
429
436
  }
430
437
  /**
@@ -436,6 +443,11 @@ class SnapController extends base_controller_1.BaseController {
436
443
  * @returns The requested snap state or null if no state exists.
437
444
  */
438
445
  async getSnapState(snapId, encrypted) {
446
+ const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
447
+ const cachedState = encrypted ? runtime.state : runtime.unencryptedState;
448
+ if (cachedState !== undefined) {
449
+ return cachedState;
450
+ }
439
451
  const state = encrypted
440
452
  ? this.state.snapStates[snapId]
441
453
  : this.state.unencryptedSnapStates[snapId];
@@ -443,10 +455,14 @@ class SnapController extends base_controller_1.BaseController {
443
455
  return null;
444
456
  }
445
457
  if (!encrypted) {
446
- // For performance reasons, we do not validate that the state is JSON, since we control serialization.
447
- return JSON.parse(state);
458
+ // For performance reasons, we do not validate that the state is JSON,
459
+ // since we control serialization.
460
+ const json = JSON.parse(state);
461
+ runtime.unencryptedState = json;
462
+ return json;
448
463
  }
449
464
  const decrypted = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_decryptSnapState).call(this, snapId, state);
465
+ runtime.state = decrypted;
450
466
  return decrypted;
451
467
  }
452
468
  /**
@@ -1331,6 +1347,55 @@ async function _SnapController_encryptSnapState(snapId, state) {
1331
1347
  const encryptedState = await __classPrivateFieldGet(this, _SnapController_encryptor, "f").encryptWithKey(key, state);
1332
1348
  encryptedState.salt = salt;
1333
1349
  return JSON.stringify(encryptedState);
1350
+ }, _SnapController_getStateToPersist =
1351
+ /**
1352
+ * Get the new Snap state to persist based on the given state and encryption
1353
+ * flag.
1354
+ *
1355
+ * - If the state is null, return null.
1356
+ * - If the state should be encrypted, return the encrypted state.
1357
+ * - Otherwise, if the state should not be encrypted, return the JSON-
1358
+ * stringified state.
1359
+ *
1360
+ * @param snapId - The Snap ID.
1361
+ * @param state - The state to persist.
1362
+ * @param encrypted - A flag to indicate whether to use encrypted storage or
1363
+ * not.
1364
+ * @returns The state to persist.
1365
+ */
1366
+ async function _SnapController_getStateToPersist(snapId, state, encrypted) {
1367
+ if (state === null) {
1368
+ return null;
1369
+ }
1370
+ if (encrypted) {
1371
+ return await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_encryptSnapState).call(this, snapId, state);
1372
+ }
1373
+ return JSON.stringify(state);
1374
+ }, _SnapController_persistSnapState =
1375
+ /**
1376
+ * Persist the state of a Snap.
1377
+ *
1378
+ * This is run with a mutex to ensure that only one state update per Snap is
1379
+ * processed at a time, avoiding possible race conditions.
1380
+ *
1381
+ * @param snapId - The Snap ID.
1382
+ * @param newSnapState - The new state of the Snap.
1383
+ * @param encrypted - A flag to indicate whether to use encrypted storage or
1384
+ * not.
1385
+ */
1386
+ async function _SnapController_persistSnapState(snapId, newSnapState, encrypted) {
1387
+ const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
1388
+ await runtime.stateMutex.runExclusive(async () => {
1389
+ const newState = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getStateToPersist).call(this, snapId, newSnapState, encrypted);
1390
+ if (encrypted) {
1391
+ return this.update((state) => {
1392
+ state.snapStates[snapId] = newState;
1393
+ });
1394
+ }
1395
+ return this.update((state) => {
1396
+ state.unencryptedSnapStates[snapId] = newState;
1397
+ });
1398
+ });
1334
1399
  }, _SnapController_handleInitialConnections = function _SnapController_handleInitialConnections(snapId, previousInitialConnections, initialConnections) {
1335
1400
  if (previousInitialConnections) {
1336
1401
  const revokedInitialConnections = (0, utils_2.setDiff)(previousInitialConnections, initialConnections);
@@ -1864,6 +1929,7 @@ async function _SnapController_rollbackSnaps(snapIds) {
1864
1929
  pendingOutboundRequests: 0,
1865
1930
  interpreter,
1866
1931
  stopping: false,
1932
+ stateMutex: new async_mutex_1.Mutex(),
1867
1933
  });
1868
1934
  }, _SnapController_calculatePermissionsChange = function _SnapController_calculatePermissionsChange(snapId, desiredPermissionsSet) {
1869
1935
  const oldPermissions = this.messagingSystem.call('PermissionController:getPermissions', snapId) ?? {};
@@ -1945,5 +2011,11 @@ async function _SnapController_callLifecycleHook(origin, snapId, handler) {
1945
2011
  method: handler,
1946
2012
  },
1947
2013
  });
2014
+ }, _SnapController_handleLock = function _SnapController_handleLock() {
2015
+ for (const runtime of __classPrivateFieldGet(this, _SnapController_snapsRuntimeData, "f").values()) {
2016
+ runtime.encryptionKey = null;
2017
+ runtime.encryptionSalt = null;
2018
+ runtime.state = undefined;
2019
+ }
1948
2020
  };
1949
2021
  //# sourceMappingURL=SnapController.cjs.map