@metamask/snaps-controllers 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +18 -0
- package/README.md +3 -0
- package/dist/cronjob/CronjobController.d.ts +115 -0
- package/dist/cronjob/CronjobController.js +231 -0
- package/dist/cronjob/CronjobController.js.map +1 -0
- package/dist/cronjob/index.d.ts +1 -0
- package/dist/cronjob/index.js +18 -0
- package/dist/cronjob/index.js.map +1 -0
- package/dist/fsm.d.ts +24 -0
- package/dist/fsm.js +75 -0
- package/dist/fsm.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/multichain/MultiChainController.d.ts +135 -0
- package/dist/multichain/MultiChainController.js +348 -0
- package/dist/multichain/MultiChainController.js.map +1 -0
- package/dist/multichain/index.d.ts +3 -0
- package/dist/multichain/index.js +20 -0
- package/dist/multichain/index.js.map +1 -0
- package/dist/multichain/matching.d.ts +9 -0
- package/dist/multichain/matching.js +58 -0
- package/dist/multichain/matching.js.map +1 -0
- package/dist/multichain/middleware.d.ts +14 -0
- package/dist/multichain/middleware.js +42 -0
- package/dist/multichain/middleware.js.map +1 -0
- package/dist/services/AbstractExecutionService.d.ts +124 -0
- package/dist/services/AbstractExecutionService.js +316 -0
- package/dist/services/AbstractExecutionService.js.map +1 -0
- package/dist/services/ExecutionService.d.ts +68 -0
- package/dist/services/ExecutionService.js +4 -0
- package/dist/services/ExecutionService.js.map +1 -0
- package/dist/services/browser.d.ts +3 -0
- package/dist/services/browser.js +21 -0
- package/dist/services/browser.js.map +1 -0
- package/dist/services/iframe/IframeExecutionService.d.ts +25 -0
- package/dist/services/iframe/IframeExecutionService.js +82 -0
- package/dist/services/iframe/IframeExecutionService.js.map +1 -0
- package/dist/services/iframe/index.d.ts +1 -0
- package/dist/services/iframe/index.js +18 -0
- package/dist/services/iframe/index.js.map +1 -0
- package/dist/services/iframe/test/fixJSDOMPostMessageEventSource.d.ts +3 -0
- package/dist/services/iframe/test/fixJSDOMPostMessageEventSource.js +74 -0
- package/dist/services/iframe/test/fixJSDOMPostMessageEventSource.js.map +1 -0
- package/dist/services/iframe/test/server.d.ts +11 -0
- package/dist/services/iframe/test/server.js +71 -0
- package/dist/services/iframe/test/server.js.map +1 -0
- package/dist/services/index.d.ts +4 -0
- package/dist/services/index.js +21 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/node/NodeProcessExecutionService.d.ts +11 -0
- package/dist/services/node/NodeProcessExecutionService.js +18 -0
- package/dist/services/node/NodeProcessExecutionService.js.map +1 -0
- package/dist/services/node/NodeThreadExecutionService.d.ts +11 -0
- package/dist/services/node/NodeThreadExecutionService.js +18 -0
- package/dist/services/node/NodeThreadExecutionService.js.map +1 -0
- package/dist/services/node/index.d.ts +2 -0
- package/dist/services/node/index.js +19 -0
- package/dist/services/node/index.js.map +1 -0
- package/dist/snaps/RequestQueue.d.ts +24 -0
- package/dist/snaps/RequestQueue.js +47 -0
- package/dist/snaps/RequestQueue.js.map +1 -0
- package/dist/snaps/SnapController.d.ts +600 -0
- package/dist/snaps/SnapController.js +1314 -0
- package/dist/snaps/SnapController.js.map +1 -0
- package/dist/snaps/Timer.d.ts +39 -0
- package/dist/snaps/Timer.js +86 -0
- package/dist/snaps/Timer.js.map +1 -0
- package/dist/snaps/endowments/cronjob.d.ts +49 -0
- package/dist/snaps/endowments/cronjob.js +105 -0
- package/dist/snaps/endowments/cronjob.js.map +1 -0
- package/dist/snaps/endowments/enum.d.ts +8 -0
- package/dist/snaps/endowments/enum.js +13 -0
- package/dist/snaps/endowments/enum.js.map +1 -0
- package/dist/snaps/endowments/ethereum-provider.d.ts +13 -0
- package/dist/snaps/endowments/ethereum-provider.js +31 -0
- package/dist/snaps/endowments/ethereum-provider.js.map +1 -0
- package/dist/snaps/endowments/index.d.ts +67 -0
- package/dist/snaps/endowments/index.js +39 -0
- package/dist/snaps/endowments/index.js.map +1 -0
- package/dist/snaps/endowments/keyring.d.ts +39 -0
- package/dist/snaps/endowments/keyring.js +103 -0
- package/dist/snaps/endowments/keyring.js.map +1 -0
- package/dist/snaps/endowments/long-running.d.ts +13 -0
- package/dist/snaps/endowments/long-running.js +28 -0
- package/dist/snaps/endowments/long-running.js.map +1 -0
- package/dist/snaps/endowments/network-access.d.ts +13 -0
- package/dist/snaps/endowments/network-access.js +29 -0
- package/dist/snaps/endowments/network-access.js.map +1 -0
- package/dist/snaps/endowments/transaction-insight.d.ts +38 -0
- package/dist/snaps/endowments/transaction-insight.js +106 -0
- package/dist/snaps/endowments/transaction-insight.js.map +1 -0
- package/dist/snaps/index.d.ts +4 -0
- package/dist/snaps/index.js +21 -0
- package/dist/snaps/index.js.map +1 -0
- package/dist/snaps/selectors.d.ts +2 -0
- package/dist/snaps/selectors.js +6 -0
- package/dist/snaps/selectors.js.map +1 -0
- package/dist/snaps/utils/index.d.ts +2 -0
- package/dist/snaps/utils/index.js +19 -0
- package/dist/snaps/utils/index.js.map +1 -0
- package/dist/snaps/utils/npm.d.ts +14 -0
- package/dist/snaps/utils/npm.js +81 -0
- package/dist/snaps/utils/npm.js.map +1 -0
- package/dist/snaps/utils/stream.d.ts +30 -0
- package/dist/snaps/utils/stream.js +124 -0
- package/dist/snaps/utils/stream.js.map +1 -0
- package/dist/utils.d.ts +128 -0
- package/dist/utils.js +92 -0
- package/dist/utils.js.map +1 -0
- package/package.json +99 -0
|
@@ -0,0 +1,1314 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
5
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
8
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
9
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
10
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
11
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
12
|
+
};
|
|
13
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
14
|
+
var t = {};
|
|
15
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
16
|
+
t[p] = s[p];
|
|
17
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
18
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
19
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
20
|
+
t[p[i]] = s[p[i]];
|
|
21
|
+
}
|
|
22
|
+
return t;
|
|
23
|
+
};
|
|
24
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
25
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
26
|
+
};
|
|
27
|
+
var _SnapController_instances, _SnapController_closeAllConnections, _SnapController_environmentEndowmentPermissions, _SnapController_featureFlags, _SnapController_fetchFunction, _SnapController_idleTimeCheckInterval, _SnapController_checkSnapBlockList, _SnapController_maxIdleTime, _SnapController_npmRegistryUrl, _SnapController_getAppKey, _SnapController_timeoutForLastRequestStatus, _SnapController_statusMachine, _SnapController_initializeStateMachine, _SnapController_registerMessageHandlers, _SnapController_pollForLastRequestStatus, _SnapController_blockSnap, _SnapController_unblockSnap, _SnapController_assertIsUnblocked, _SnapController_stopSnapsLastRequestPastMax, _SnapController_transition, _SnapController_terminateSnap, _SnapController_getEncryptionKey, _SnapController_encryptSnapState, _SnapController_decryptSnapState, _SnapController_add, _SnapController_startSnap, _SnapController_getEndowments, _SnapController_set, _SnapController_fetchNpmSnap, _SnapController_fetchLocalSnap, _SnapController_processSnapPermissions, _SnapController_getRpcRequestHandler, _SnapController_executeWithTimeout, _SnapController_recordSnapRpcRequestStart, _SnapController_recordSnapRpcRequestFinish, _SnapController_getRuntime, _SnapController_getRuntimeExpect, _SnapController_setupRuntime, _SnapController_calculatePermissionsChange;
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.SnapController = exports.AppKeyType = exports.SNAP_APPROVAL_UPDATE = exports.SNAP_APPROVAL_INSTALL = exports.controllerName = void 0;
|
|
30
|
+
const browser_passworder_1 = __importDefault(require("@metamask/browser-passworder"));
|
|
31
|
+
const controllers_1 = require("@metamask/controllers");
|
|
32
|
+
const snaps_utils_1 = require("@metamask/snaps-utils");
|
|
33
|
+
const utils_1 = require("@metamask/utils");
|
|
34
|
+
const fsm_1 = require("@xstate/fsm");
|
|
35
|
+
const eth_rpc_errors_1 = require("eth-rpc-errors");
|
|
36
|
+
const nanoid_1 = require("nanoid");
|
|
37
|
+
const rpc_methods_1 = require("@metamask/rpc-methods");
|
|
38
|
+
const fsm_2 = require("../fsm");
|
|
39
|
+
const utils_2 = require("../utils");
|
|
40
|
+
const endowments_1 = require("./endowments");
|
|
41
|
+
const RequestQueue_1 = require("./RequestQueue");
|
|
42
|
+
const utils_3 = require("./utils");
|
|
43
|
+
const Timer_1 = require("./Timer");
|
|
44
|
+
exports.controllerName = 'SnapController';
|
|
45
|
+
// TODO: Figure out how to name these
|
|
46
|
+
exports.SNAP_APPROVAL_INSTALL = 'wallet_installSnap';
|
|
47
|
+
exports.SNAP_APPROVAL_UPDATE = 'wallet_updateSnap';
|
|
48
|
+
const TRUNCATED_SNAP_PROPERTIES = new Set([
|
|
49
|
+
'initialPermissions',
|
|
50
|
+
'id',
|
|
51
|
+
'permissionName',
|
|
52
|
+
'version',
|
|
53
|
+
'enabled',
|
|
54
|
+
'blocked',
|
|
55
|
+
]);
|
|
56
|
+
var AppKeyType;
|
|
57
|
+
(function (AppKeyType) {
|
|
58
|
+
AppKeyType["stateEncryption"] = "stateEncryption";
|
|
59
|
+
})(AppKeyType = exports.AppKeyType || (exports.AppKeyType = {}));
|
|
60
|
+
const defaultState = {
|
|
61
|
+
snapErrors: {},
|
|
62
|
+
snaps: {},
|
|
63
|
+
snapStates: {},
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Truncates the properties of a snap to only ones that are easily serializable.
|
|
67
|
+
*
|
|
68
|
+
* @param snap - The snap to truncate.
|
|
69
|
+
* @returns Object with serializable snap properties.
|
|
70
|
+
*/
|
|
71
|
+
function truncateSnap(snap) {
|
|
72
|
+
return Object.keys(snap).reduce((serialized, key) => {
|
|
73
|
+
if (TRUNCATED_SNAP_PROPERTIES.has(key)) {
|
|
74
|
+
serialized[key] = snap[key];
|
|
75
|
+
}
|
|
76
|
+
return serialized;
|
|
77
|
+
}, {});
|
|
78
|
+
}
|
|
79
|
+
const name = 'SnapController';
|
|
80
|
+
/*
|
|
81
|
+
* A snap is initialized in three phases:
|
|
82
|
+
* - Add: Loads the snap from a remote source and parses it.
|
|
83
|
+
* - Authorize: Requests the snap's required permissions from the user.
|
|
84
|
+
* - Start: Initializes the snap in its SES realm with the authorized permissions.
|
|
85
|
+
*/
|
|
86
|
+
class SnapController extends controllers_1.BaseControllerV2 {
|
|
87
|
+
constructor({ closeAllConnections, messenger, state, getAppKey, environmentEndowmentPermissions = [], npmRegistryUrl, idleTimeCheckInterval = (0, utils_1.inMilliseconds)(5, utils_1.Duration.Second), checkBlockList, maxIdleTime = (0, utils_1.inMilliseconds)(30, utils_1.Duration.Second), maxRequestTime = (0, utils_1.inMilliseconds)(60, utils_1.Duration.Second), fetchFunction = globalThis.fetch.bind(globalThis), featureFlags = {}, }) {
|
|
88
|
+
var _a, _b;
|
|
89
|
+
super({
|
|
90
|
+
messenger,
|
|
91
|
+
metadata: {
|
|
92
|
+
snapErrors: {
|
|
93
|
+
persist: false,
|
|
94
|
+
anonymous: false,
|
|
95
|
+
},
|
|
96
|
+
snapStates: {
|
|
97
|
+
persist: () => {
|
|
98
|
+
return Object.keys(this.state.snaps).reduce((acc, cur) => {
|
|
99
|
+
acc[cur] = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, cur).state;
|
|
100
|
+
return acc;
|
|
101
|
+
}, {});
|
|
102
|
+
},
|
|
103
|
+
anonymous: false,
|
|
104
|
+
},
|
|
105
|
+
snaps: {
|
|
106
|
+
persist: (snaps) => {
|
|
107
|
+
return Object.values(snaps)
|
|
108
|
+
.map((snap) => {
|
|
109
|
+
return Object.assign(Object.assign({}, snap), { sourceCode: __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snap.id).sourceCode,
|
|
110
|
+
// At the time state is rehydrated, no snap will be running.
|
|
111
|
+
status: snaps_utils_1.SnapStatus.Stopped });
|
|
112
|
+
})
|
|
113
|
+
.reduce((memo, snap) => {
|
|
114
|
+
memo[snap.id] = snap;
|
|
115
|
+
return memo;
|
|
116
|
+
}, {});
|
|
117
|
+
},
|
|
118
|
+
anonymous: false,
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
name,
|
|
122
|
+
state: Object.assign(Object.assign({}, defaultState), Object.assign(Object.assign({}, state), { snaps: Object.values((_a = state === null || state === void 0 ? void 0 : state.snaps) !== null && _a !== void 0 ? _a : {}).reduce((memo, snap) => {
|
|
123
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
124
|
+
const { sourceCode } = snap, rest = __rest(snap, ["sourceCode"]);
|
|
125
|
+
memo[snap.id] = rest;
|
|
126
|
+
return memo;
|
|
127
|
+
}, {}) })),
|
|
128
|
+
});
|
|
129
|
+
_SnapController_instances.add(this);
|
|
130
|
+
_SnapController_closeAllConnections.set(this, void 0);
|
|
131
|
+
_SnapController_environmentEndowmentPermissions.set(this, void 0);
|
|
132
|
+
_SnapController_featureFlags.set(this, void 0);
|
|
133
|
+
_SnapController_fetchFunction.set(this, void 0);
|
|
134
|
+
_SnapController_idleTimeCheckInterval.set(this, void 0);
|
|
135
|
+
_SnapController_checkSnapBlockList.set(this, void 0);
|
|
136
|
+
_SnapController_maxIdleTime.set(this, void 0);
|
|
137
|
+
_SnapController_npmRegistryUrl.set(this, void 0);
|
|
138
|
+
_SnapController_getAppKey.set(this, void 0);
|
|
139
|
+
_SnapController_timeoutForLastRequestStatus.set(this, void 0);
|
|
140
|
+
_SnapController_statusMachine.set(this, void 0);
|
|
141
|
+
__classPrivateFieldSet(this, _SnapController_closeAllConnections, closeAllConnections, "f");
|
|
142
|
+
__classPrivateFieldSet(this, _SnapController_environmentEndowmentPermissions, environmentEndowmentPermissions, "f");
|
|
143
|
+
__classPrivateFieldSet(this, _SnapController_featureFlags, featureFlags, "f");
|
|
144
|
+
__classPrivateFieldSet(this, _SnapController_fetchFunction, fetchFunction, "f");
|
|
145
|
+
__classPrivateFieldSet(this, _SnapController_getAppKey, getAppKey, "f");
|
|
146
|
+
__classPrivateFieldSet(this, _SnapController_idleTimeCheckInterval, idleTimeCheckInterval, "f");
|
|
147
|
+
__classPrivateFieldSet(this, _SnapController_checkSnapBlockList, checkBlockList, "f");
|
|
148
|
+
__classPrivateFieldSet(this, _SnapController_maxIdleTime, maxIdleTime, "f");
|
|
149
|
+
this.maxRequestTime = maxRequestTime;
|
|
150
|
+
__classPrivateFieldSet(this, _SnapController_npmRegistryUrl, npmRegistryUrl, "f");
|
|
151
|
+
this._onUnhandledSnapError = this._onUnhandledSnapError.bind(this);
|
|
152
|
+
this._onOutboundRequest = this._onOutboundRequest.bind(this);
|
|
153
|
+
this._onOutboundResponse = this._onOutboundResponse.bind(this);
|
|
154
|
+
this.snapsRuntimeData = new Map();
|
|
155
|
+
__classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_pollForLastRequestStatus).call(this);
|
|
156
|
+
this.messagingSystem.subscribe('ExecutionService:unhandledError', this._onUnhandledSnapError);
|
|
157
|
+
this.messagingSystem.subscribe('ExecutionService:outboundRequest', this._onOutboundRequest);
|
|
158
|
+
this.messagingSystem.subscribe('ExecutionService:outboundResponse', this._onOutboundResponse);
|
|
159
|
+
__classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_initializeStateMachine).call(this);
|
|
160
|
+
__classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_registerMessageHandlers).call(this);
|
|
161
|
+
Object.values((_b = state === null || state === void 0 ? void 0 : state.snaps) !== null && _b !== void 0 ? _b : {}).forEach((snap) => {
|
|
162
|
+
var _a, _b;
|
|
163
|
+
return __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_setupRuntime).call(this, snap.id, {
|
|
164
|
+
sourceCode: snap.sourceCode,
|
|
165
|
+
state: (_b = (_a = state === null || state === void 0 ? void 0 : state.snapStates) === null || _a === void 0 ? void 0 : _a[snap.id]) !== null && _b !== void 0 ? _b : null,
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Checks all installed snaps against the block list and
|
|
171
|
+
* blocks/unblocks snaps as appropriate. See {@link SnapController.blockSnap}
|
|
172
|
+
* for more information.
|
|
173
|
+
*/
|
|
174
|
+
async updateBlockedSnaps() {
|
|
175
|
+
const blockedSnaps = await __classPrivateFieldGet(this, _SnapController_checkSnapBlockList, "f").call(this, Object.values(this.state.snaps).reduce((blockListArg, snap) => {
|
|
176
|
+
blockListArg[snap.id] = {
|
|
177
|
+
version: snap.version,
|
|
178
|
+
shasum: snap.manifest.source.shasum,
|
|
179
|
+
};
|
|
180
|
+
return blockListArg;
|
|
181
|
+
}, {}));
|
|
182
|
+
await Promise.all(Object.entries(blockedSnaps).map((_a) => {
|
|
183
|
+
var [snapId, _b] = _a, { blocked } = _b, blockData = __rest(_b, ["blocked"]);
|
|
184
|
+
if (blocked) {
|
|
185
|
+
return __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_blockSnap).call(this, snapId, blockData);
|
|
186
|
+
}
|
|
187
|
+
return __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_unblockSnap).call(this, snapId);
|
|
188
|
+
}));
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Checks the block list to determine whether a version of a snap is blocked.
|
|
192
|
+
*
|
|
193
|
+
* @param snapId - The snap id to check.
|
|
194
|
+
* @param snapInfo - Snap information containing version and shasum.
|
|
195
|
+
* @returns Whether the version of the snap is blocked or not.
|
|
196
|
+
*/
|
|
197
|
+
async isBlocked(snapId, snapInfo) {
|
|
198
|
+
const result = await __classPrivateFieldGet(this, _SnapController_checkSnapBlockList, "f").call(this, {
|
|
199
|
+
[snapId]: snapInfo,
|
|
200
|
+
});
|
|
201
|
+
return result[snapId].blocked;
|
|
202
|
+
}
|
|
203
|
+
async _onUnhandledSnapError(snapId, error) {
|
|
204
|
+
await this.stopSnap(snapId, snaps_utils_1.SnapStatusEvents.Crash);
|
|
205
|
+
this.addSnapError(error);
|
|
206
|
+
}
|
|
207
|
+
async _onOutboundRequest(snapId) {
|
|
208
|
+
const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
|
|
209
|
+
// Ideally we would only pause the pending request that is making the outbound request
|
|
210
|
+
// but right now we don't have a way to know which request initiated the outbound request
|
|
211
|
+
runtime.pendingInboundRequests
|
|
212
|
+
.filter((pendingRequest) => pendingRequest.timer.status === 'running')
|
|
213
|
+
.forEach((pendingRequest) => pendingRequest.timer.pause());
|
|
214
|
+
runtime.pendingOutboundRequests += 1;
|
|
215
|
+
}
|
|
216
|
+
async _onOutboundResponse(snapId) {
|
|
217
|
+
const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
|
|
218
|
+
runtime.pendingOutboundRequests -= 1;
|
|
219
|
+
if (runtime.pendingOutboundRequests === 0) {
|
|
220
|
+
runtime.pendingInboundRequests
|
|
221
|
+
.filter((pendingRequest) => pendingRequest.timer.status === 'paused')
|
|
222
|
+
.forEach((pendingRequest) => pendingRequest.timer.resume());
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Starts the given snap. Throws an error if no such snap exists
|
|
227
|
+
* or if it is already running.
|
|
228
|
+
*
|
|
229
|
+
* @param snapId - The id of the Snap to start.
|
|
230
|
+
*/
|
|
231
|
+
async startSnap(snapId) {
|
|
232
|
+
const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
|
|
233
|
+
if (this.state.snaps[snapId].enabled === false) {
|
|
234
|
+
throw new Error(`Snap "${snapId}" is disabled.`);
|
|
235
|
+
}
|
|
236
|
+
(0, utils_1.assert)(runtime.sourceCode);
|
|
237
|
+
await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_startSnap).call(this, {
|
|
238
|
+
snapId,
|
|
239
|
+
sourceCode: runtime.sourceCode,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Enables the given snap. A snap can only be started if it is enabled. A snap
|
|
244
|
+
* can only be enabled if it isn't blocked.
|
|
245
|
+
*
|
|
246
|
+
* @param snapId - The id of the Snap to enable.
|
|
247
|
+
*/
|
|
248
|
+
enableSnap(snapId) {
|
|
249
|
+
this.getExpect(snapId);
|
|
250
|
+
if (this.state.snaps[snapId].blocked) {
|
|
251
|
+
throw new Error(`Snap "${snapId}" is blocked and cannot be enabled.`);
|
|
252
|
+
}
|
|
253
|
+
this.update((state) => {
|
|
254
|
+
state.snaps[snapId].enabled = true;
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Disables the given snap. A snap can only be started if it is enabled.
|
|
259
|
+
*
|
|
260
|
+
* @param snapId - The id of the Snap to disable.
|
|
261
|
+
* @returns A promise that resolves once the snap has been disabled.
|
|
262
|
+
*/
|
|
263
|
+
disableSnap(snapId) {
|
|
264
|
+
if (!this.has(snapId)) {
|
|
265
|
+
throw new Error(`Snap "${snapId}" not found.`);
|
|
266
|
+
}
|
|
267
|
+
this.update((state) => {
|
|
268
|
+
state.snaps[snapId].enabled = false;
|
|
269
|
+
});
|
|
270
|
+
if (this.isRunning(snapId)) {
|
|
271
|
+
return this.stopSnap(snapId, snaps_utils_1.SnapStatusEvents.Stop);
|
|
272
|
+
}
|
|
273
|
+
return Promise.resolve();
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Stops the given snap, removes all hooks, closes all connections, and
|
|
277
|
+
* terminates its worker.
|
|
278
|
+
*
|
|
279
|
+
* @param snapId - The id of the Snap to stop.
|
|
280
|
+
* @param statusEvent - The Snap status event that caused the snap to be
|
|
281
|
+
* stopped.
|
|
282
|
+
*/
|
|
283
|
+
async stopSnap(snapId, statusEvent = snaps_utils_1.SnapStatusEvents.Stop) {
|
|
284
|
+
const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntime).call(this, snapId);
|
|
285
|
+
if (!runtime) {
|
|
286
|
+
throw new Error(`The snap "${snapId}" is not running.`);
|
|
287
|
+
}
|
|
288
|
+
// Reset request tracking
|
|
289
|
+
runtime.lastRequest = null;
|
|
290
|
+
runtime.pendingInboundRequests = [];
|
|
291
|
+
runtime.pendingOutboundRequests = 0;
|
|
292
|
+
try {
|
|
293
|
+
if (this.isRunning(snapId)) {
|
|
294
|
+
__classPrivateFieldGet(this, _SnapController_closeAllConnections, "f").call(this, snapId);
|
|
295
|
+
await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_terminateSnap).call(this, snapId);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
finally {
|
|
299
|
+
if (this.isRunning(snapId)) {
|
|
300
|
+
__classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_transition).call(this, snapId, statusEvent);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Returns whether the given snap is running.
|
|
306
|
+
* Throws an error if the snap doesn't exist.
|
|
307
|
+
*
|
|
308
|
+
* @param snapId - The id of the Snap to check.
|
|
309
|
+
* @returns `true` if the snap is running, otherwise `false`.
|
|
310
|
+
*/
|
|
311
|
+
isRunning(snapId) {
|
|
312
|
+
return this.getExpect(snapId).status === 'running';
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Returns whether the given snap has been added to state.
|
|
316
|
+
*
|
|
317
|
+
* @param snapId - The id of the Snap to check for.
|
|
318
|
+
* @returns `true` if the snap exists in the controller state, otherwise `false`.
|
|
319
|
+
*/
|
|
320
|
+
has(snapId) {
|
|
321
|
+
return Boolean(this.get(snapId));
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Gets the snap with the given id if it exists, including all data.
|
|
325
|
+
* This should not be used if the snap is to be serializable, as e.g.
|
|
326
|
+
* the snap sourceCode may be quite large.
|
|
327
|
+
*
|
|
328
|
+
* @param snapId - The id of the Snap to get.
|
|
329
|
+
* @returns The entire snap object from the controller state.
|
|
330
|
+
*/
|
|
331
|
+
get(snapId) {
|
|
332
|
+
return this.state.snaps[snapId];
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Gets the snap with the given id, throws if doesn't.
|
|
336
|
+
* This should not be used if the snap is to be serializable, as e.g.
|
|
337
|
+
* the snap sourceCode may be quite large.
|
|
338
|
+
*
|
|
339
|
+
* @see {@link SnapController.get}
|
|
340
|
+
* @throws {@link Error}. If the snap doesn't exist
|
|
341
|
+
* @param snapId - The id of the snap to get.
|
|
342
|
+
* @returns The entire snap object.
|
|
343
|
+
*/
|
|
344
|
+
getExpect(snapId) {
|
|
345
|
+
const snap = this.get(snapId);
|
|
346
|
+
(0, utils_1.assert)(snap !== undefined, new Error(`Snap "${snapId}" not found.`));
|
|
347
|
+
return snap;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Gets the snap with the given id if it exists, excluding any
|
|
351
|
+
* non-serializable or expensive-to-serialize data.
|
|
352
|
+
*
|
|
353
|
+
* @param snapId - The id of the Snap to get.
|
|
354
|
+
* @returns A truncated version of the snap state, that is less expensive to serialize.
|
|
355
|
+
*/
|
|
356
|
+
// TODO(ritave): this.get returns undefined, this.getTruncated returns null
|
|
357
|
+
getTruncated(snapId) {
|
|
358
|
+
const snap = this.get(snapId);
|
|
359
|
+
return snap ? truncateSnap(snap) : null;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Gets the snap with the given id, throw if it doesn't exist.
|
|
363
|
+
*
|
|
364
|
+
* @throws {@link Error}. If snap doesn't exist
|
|
365
|
+
* @param snapId - The id of the snap to get.
|
|
366
|
+
* @returns A truncated version of the snap state, that is less expensive to serialize.
|
|
367
|
+
*/
|
|
368
|
+
getTruncatedExpect(snapId) {
|
|
369
|
+
return truncateSnap(this.getExpect(snapId));
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Updates the own state of the snap with the given id.
|
|
373
|
+
* This is distinct from the state MetaMask uses to manage snaps.
|
|
374
|
+
*
|
|
375
|
+
* @param snapId - The id of the Snap whose state should be updated.
|
|
376
|
+
* @param newSnapState - The new state of the snap.
|
|
377
|
+
*/
|
|
378
|
+
async updateSnapState(snapId, newSnapState) {
|
|
379
|
+
const encrypted = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_encryptSnapState).call(this, snapId, newSnapState);
|
|
380
|
+
const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
|
|
381
|
+
runtime.state = encrypted;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Clears the state of the snap with the given id.
|
|
385
|
+
* This is distinct from the state MetaMask uses to manage snaps.
|
|
386
|
+
*
|
|
387
|
+
* @param snapId - The id of the Snap whose state should be cleared.
|
|
388
|
+
*/
|
|
389
|
+
async clearSnapState(snapId) {
|
|
390
|
+
const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
|
|
391
|
+
runtime.state = null;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Adds error from a snap to the SnapController state.
|
|
395
|
+
*
|
|
396
|
+
* @param snapError - The error to store on the SnapController.
|
|
397
|
+
*/
|
|
398
|
+
addSnapError(snapError) {
|
|
399
|
+
this.update((state) => {
|
|
400
|
+
const id = (0, nanoid_1.nanoid)();
|
|
401
|
+
state.snapErrors[id] = Object.assign(Object.assign({}, snapError), { internalID: id });
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Removes an error by internalID from a the SnapControllers state.
|
|
406
|
+
*
|
|
407
|
+
* @param internalID - The internal error ID to remove on the SnapController.
|
|
408
|
+
*/
|
|
409
|
+
async removeSnapError(internalID) {
|
|
410
|
+
this.update((state) => {
|
|
411
|
+
delete state.snapErrors[internalID];
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Clears all errors from the SnapControllers state.
|
|
416
|
+
*
|
|
417
|
+
*/
|
|
418
|
+
async clearSnapErrors() {
|
|
419
|
+
this.update((state) => {
|
|
420
|
+
state.snapErrors = {};
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Gets the own state of the snap with the given id.
|
|
425
|
+
* This is distinct from the state MetaMask uses to manage snaps.
|
|
426
|
+
*
|
|
427
|
+
* @param snapId - The id of the Snap whose state to get.
|
|
428
|
+
* @returns A promise that resolves with the decrypted snap state or null if no state exists.
|
|
429
|
+
* @throws If the snap state decryption fails.
|
|
430
|
+
*/
|
|
431
|
+
async getSnapState(snapId) {
|
|
432
|
+
const { state } = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
|
|
433
|
+
return state ? __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_decryptSnapState).call(this, snapId, state) : null;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Completely clear the controller's state: delete all associated data,
|
|
437
|
+
* handlers, event listeners, and permissions; tear down all snap providers.
|
|
438
|
+
*/
|
|
439
|
+
clearState() {
|
|
440
|
+
const snapIds = Object.keys(this.state.snaps);
|
|
441
|
+
snapIds.forEach((snapId) => {
|
|
442
|
+
__classPrivateFieldGet(this, _SnapController_closeAllConnections, "f").call(this, snapId);
|
|
443
|
+
});
|
|
444
|
+
this.messagingSystem.call('ExecutionService:terminateAllSnaps');
|
|
445
|
+
snapIds.forEach(this.revokeAllSnapPermissions);
|
|
446
|
+
this.update((state) => {
|
|
447
|
+
state.snaps = {};
|
|
448
|
+
state.snapStates = {};
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Removes the given snap from state, and clears all associated handlers
|
|
453
|
+
* and listeners.
|
|
454
|
+
*
|
|
455
|
+
* @param snapId - The id of the Snap.
|
|
456
|
+
* @returns A promise that resolves once the snap has been removed.
|
|
457
|
+
*/
|
|
458
|
+
async removeSnap(snapId) {
|
|
459
|
+
return this.removeSnaps([snapId]);
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Stops the given snaps, removes them from state, and clears all associated
|
|
463
|
+
* permissions, handlers, and listeners.
|
|
464
|
+
*
|
|
465
|
+
* @param snapIds - The ids of the Snaps.
|
|
466
|
+
*/
|
|
467
|
+
async removeSnaps(snapIds) {
|
|
468
|
+
if (!Array.isArray(snapIds)) {
|
|
469
|
+
throw new Error('Expected array of snap ids.');
|
|
470
|
+
}
|
|
471
|
+
await Promise.all(snapIds.map(async (snapId) => {
|
|
472
|
+
const truncated = this.getTruncatedExpect(snapId);
|
|
473
|
+
// Disable the snap and revoke all of its permissions before deleting
|
|
474
|
+
// it. This ensures that the snap will not be restarted or otherwise
|
|
475
|
+
// affect the host environment while we are deleting it.
|
|
476
|
+
await this.disableSnap(snapId);
|
|
477
|
+
this.revokeAllSnapPermissions(snapId);
|
|
478
|
+
const permissionName = (0, snaps_utils_1.getSnapPermissionName)(snapId);
|
|
479
|
+
// Revoke all subjects access to the snap
|
|
480
|
+
this.messagingSystem.call('PermissionController:revokePermissionForAllSubjects', permissionName);
|
|
481
|
+
this.snapsRuntimeData.delete(snapId);
|
|
482
|
+
this.update((state) => {
|
|
483
|
+
delete state.snaps[snapId];
|
|
484
|
+
delete state.snapStates[snapId];
|
|
485
|
+
});
|
|
486
|
+
this.messagingSystem.publish(`SnapController:snapRemoved`, truncated);
|
|
487
|
+
}));
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Safely revokes all permissions granted to a Snap.
|
|
491
|
+
*
|
|
492
|
+
* @param snapId - The snap ID.
|
|
493
|
+
*/
|
|
494
|
+
async revokeAllSnapPermissions(snapId) {
|
|
495
|
+
if (await this.messagingSystem.call('PermissionController:hasPermissions', snapId)) {
|
|
496
|
+
this.messagingSystem.call('PermissionController:revokeAllPermissions', snapId);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Handles incrementing the activeReferences counter.
|
|
501
|
+
*
|
|
502
|
+
* @param snapId - The snap id of the snap that was referenced.
|
|
503
|
+
*/
|
|
504
|
+
incrementActiveReferences(snapId) {
|
|
505
|
+
const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
|
|
506
|
+
runtime.activeReferences += 1;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Handles decrement the activeReferences counter.
|
|
510
|
+
*
|
|
511
|
+
* @param snapId - The snap id of the snap that was referenced..
|
|
512
|
+
*/
|
|
513
|
+
decrementActiveReferences(snapId) {
|
|
514
|
+
const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
|
|
515
|
+
(0, utils_1.assert)(runtime.activeReferences > 0, 'SnapController reference management is in an invalid state.');
|
|
516
|
+
runtime.activeReferences -= 1;
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Gets all snaps in their truncated format.
|
|
520
|
+
*
|
|
521
|
+
* @returns All installed snaps in their truncated format.
|
|
522
|
+
*/
|
|
523
|
+
getAllSnaps() {
|
|
524
|
+
return Object.values(this.state.snaps).map(truncateSnap);
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Gets the serialized permitted snaps of the given origin, if any.
|
|
528
|
+
*
|
|
529
|
+
* @param origin - The origin whose permitted snaps to retrieve.
|
|
530
|
+
* @returns The serialized permitted snaps for the origin.
|
|
531
|
+
*/
|
|
532
|
+
async getPermittedSnaps(origin) {
|
|
533
|
+
var _a;
|
|
534
|
+
return Object.values((_a = (await this.messagingSystem.call('PermissionController:getPermissions', origin))) !== null && _a !== void 0 ? _a : {}).reduce((permittedSnaps, perm) => {
|
|
535
|
+
if (perm.parentCapability.startsWith(snaps_utils_1.SNAP_PREFIX)) {
|
|
536
|
+
const snapId = perm.parentCapability.replace(snaps_utils_1.SNAP_PREFIX_REGEX, '');
|
|
537
|
+
const snap = this.get(snapId);
|
|
538
|
+
const truncatedSnap = this.getTruncated(snapId);
|
|
539
|
+
if (truncatedSnap && (snap === null || snap === void 0 ? void 0 : snap.status) !== snaps_utils_1.SnapStatus.Installing) {
|
|
540
|
+
permittedSnaps[snapId] = truncatedSnap;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return permittedSnaps;
|
|
544
|
+
}, {});
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Installs the snaps requested by the given origin, returning the snap
|
|
548
|
+
* object if the origin is permitted to install it, and an authorization error
|
|
549
|
+
* otherwise.
|
|
550
|
+
*
|
|
551
|
+
* @param origin - The origin that requested to install the snaps.
|
|
552
|
+
* @param requestedSnaps - The snaps to install.
|
|
553
|
+
* @returns An object of snap ids and snap objects, or errors if a
|
|
554
|
+
* snap couldn't be installed.
|
|
555
|
+
*/
|
|
556
|
+
async installSnaps(origin, requestedSnaps) {
|
|
557
|
+
const result = {};
|
|
558
|
+
await Promise.all(Object.entries(requestedSnaps).map(async ([snapId, { version: rawVersion }]) => {
|
|
559
|
+
const version = (0, snaps_utils_1.resolveVersion)(rawVersion);
|
|
560
|
+
const permissionName = (0, snaps_utils_1.getSnapPermissionName)(snapId);
|
|
561
|
+
if (!(0, snaps_utils_1.isValidSnapVersionRange)(version)) {
|
|
562
|
+
result[snapId] = {
|
|
563
|
+
error: eth_rpc_errors_1.ethErrors.rpc.invalidParams(`The "version" field must be a valid SemVer version range if specified. Received: "${version}".`),
|
|
564
|
+
};
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
if (await this.messagingSystem.call('PermissionController:hasPermission', origin, permissionName)) {
|
|
568
|
+
// Attempt to install and run the snap, storing any errors that
|
|
569
|
+
// occur during the process.
|
|
570
|
+
result[snapId] = Object.assign({}, (await this.processRequestedSnap(origin, snapId, version)));
|
|
571
|
+
}
|
|
572
|
+
else {
|
|
573
|
+
// only allow the installation of permitted snaps
|
|
574
|
+
result[snapId] = {
|
|
575
|
+
error: eth_rpc_errors_1.ethErrors.provider.unauthorized(`Not authorized to install snap "${snapId}". Request the permission for the snap before attempting to install it.`),
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
}));
|
|
579
|
+
return result;
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Adds, authorizes, and runs the given snap with a snap provider.
|
|
583
|
+
* Results from this method should be efficiently serializable.
|
|
584
|
+
*
|
|
585
|
+
* @param origin - The origin requesting the snap.
|
|
586
|
+
* @param snapId - The id of the snap.
|
|
587
|
+
* @param versionRange - The semver range of the snap to install.
|
|
588
|
+
* @returns The resulting snap object, or an error if something went wrong.
|
|
589
|
+
*/
|
|
590
|
+
async processRequestedSnap(origin, snapId, versionRange) {
|
|
591
|
+
try {
|
|
592
|
+
(0, snaps_utils_1.validateSnapId)(snapId);
|
|
593
|
+
}
|
|
594
|
+
catch (err) {
|
|
595
|
+
return {
|
|
596
|
+
error: eth_rpc_errors_1.ethErrors.rpc.invalidParams(`"${snapId}" is not a valid snap id.`),
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
const existingSnap = this.getTruncated(snapId);
|
|
600
|
+
// For devX we always re-install local snaps.
|
|
601
|
+
if (existingSnap && (0, snaps_utils_1.getSnapPrefix)(snapId) !== snaps_utils_1.SnapIdPrefixes.local) {
|
|
602
|
+
if ((0, snaps_utils_1.satisfiesVersionRange)(existingSnap.version, versionRange)) {
|
|
603
|
+
return existingSnap;
|
|
604
|
+
}
|
|
605
|
+
if (__classPrivateFieldGet(this, _SnapController_featureFlags, "f").dappsCanUpdateSnaps === true) {
|
|
606
|
+
try {
|
|
607
|
+
const updateResult = await this.updateSnap(origin, snapId, versionRange);
|
|
608
|
+
if (updateResult === null) {
|
|
609
|
+
return {
|
|
610
|
+
error: eth_rpc_errors_1.ethErrors.rpc.invalidParams(`Snap "${snapId}@${existingSnap.version}" is already installed, couldn't update to a version inside requested "${versionRange}" range.`),
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
return updateResult;
|
|
614
|
+
}
|
|
615
|
+
catch (err) {
|
|
616
|
+
return { error: (0, eth_rpc_errors_1.serializeError)(err) };
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
else {
|
|
620
|
+
return {
|
|
621
|
+
error: eth_rpc_errors_1.ethErrors.rpc.invalidParams(`Version mismatch with already installed snap. ${snapId}@${existingSnap.version} doesn't satisfy requested version ${versionRange}`),
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
// Existing snaps must be stopped before overwriting
|
|
626
|
+
if (existingSnap && this.isRunning(snapId)) {
|
|
627
|
+
await this.stopSnap(snapId, snaps_utils_1.SnapStatusEvents.Stop);
|
|
628
|
+
}
|
|
629
|
+
try {
|
|
630
|
+
const { sourceCode } = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_add).call(this, {
|
|
631
|
+
origin,
|
|
632
|
+
id: snapId,
|
|
633
|
+
versionRange,
|
|
634
|
+
});
|
|
635
|
+
await this.authorize(origin, snapId);
|
|
636
|
+
await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_startSnap).call(this, {
|
|
637
|
+
snapId,
|
|
638
|
+
sourceCode,
|
|
639
|
+
});
|
|
640
|
+
const truncated = this.getTruncatedExpect(snapId);
|
|
641
|
+
this.messagingSystem.publish(`SnapController:snapInstalled`, truncated);
|
|
642
|
+
return truncated;
|
|
643
|
+
}
|
|
644
|
+
catch (err) {
|
|
645
|
+
console.error(`Error when adding snap.`, err);
|
|
646
|
+
if (this.has(snapId)) {
|
|
647
|
+
this.removeSnap(snapId);
|
|
648
|
+
}
|
|
649
|
+
return { error: (0, eth_rpc_errors_1.serializeError)(err) };
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Updates an installed snap. The flow is similar to
|
|
654
|
+
* {@link SnapController.installSnaps}. The user will be asked if they want
|
|
655
|
+
* to update, then approve any permission changes, and finally the snap will
|
|
656
|
+
* be restarted.
|
|
657
|
+
*
|
|
658
|
+
* The update will fail if the user rejects any prompt or if the new version
|
|
659
|
+
* of the snap is blocked.
|
|
660
|
+
*
|
|
661
|
+
* If the original version of the snap was blocked and the update succeeded,
|
|
662
|
+
* the snap will be unblocked and enabled before it is restarted.
|
|
663
|
+
*
|
|
664
|
+
* @param origin - The origin requesting the snap update.
|
|
665
|
+
* @param snapId - The id of the Snap to be updated.
|
|
666
|
+
* @param newVersionRange - A semver version range in which the maximum version will be chosen.
|
|
667
|
+
* @returns The snap metadata if updated, `null` otherwise.
|
|
668
|
+
*/
|
|
669
|
+
async updateSnap(origin, snapId, newVersionRange = snaps_utils_1.DEFAULT_REQUESTED_SNAP_VERSION) {
|
|
670
|
+
const snap = this.getExpect(snapId);
|
|
671
|
+
if (!(0, snaps_utils_1.isValidSnapVersionRange)(newVersionRange)) {
|
|
672
|
+
throw new Error(`Received invalid snap version range: "${newVersionRange}".`);
|
|
673
|
+
}
|
|
674
|
+
const newSnap = await this.fetchSnap(snapId, newVersionRange);
|
|
675
|
+
const newVersion = newSnap.manifest.version;
|
|
676
|
+
if (!(0, snaps_utils_1.gtVersion)(newVersion, snap.version)) {
|
|
677
|
+
console.warn(`Tried updating snap "${snapId}" within "${newVersionRange}" version range, but newer version "${snap.version}" is already installed`);
|
|
678
|
+
return null;
|
|
679
|
+
}
|
|
680
|
+
await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_assertIsUnblocked).call(this, snapId, {
|
|
681
|
+
version: newVersion,
|
|
682
|
+
shasum: newSnap.manifest.source.shasum,
|
|
683
|
+
});
|
|
684
|
+
const processedPermissions = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_processSnapPermissions).call(this, newSnap.manifest.initialPermissions);
|
|
685
|
+
const { newPermissions, unusedPermissions, approvedPermissions } = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_calculatePermissionsChange).call(this, snapId, processedPermissions);
|
|
686
|
+
const id = (0, nanoid_1.nanoid)();
|
|
687
|
+
const _a = (await this.messagingSystem.call('ApprovalController:addRequest', {
|
|
688
|
+
origin,
|
|
689
|
+
id,
|
|
690
|
+
type: exports.SNAP_APPROVAL_UPDATE,
|
|
691
|
+
requestData: {
|
|
692
|
+
// First two keys mirror installation params
|
|
693
|
+
metadata: { id, origin: snapId, dappOrigin: origin },
|
|
694
|
+
permissions: newPermissions,
|
|
695
|
+
snapId,
|
|
696
|
+
newVersion: newSnap.manifest.version,
|
|
697
|
+
newPermissions,
|
|
698
|
+
approvedPermissions,
|
|
699
|
+
unusedPermissions,
|
|
700
|
+
},
|
|
701
|
+
}, true)), { permissions: approvedNewPermissions } = _a, requestData = __rest(_a, ["permissions"]);
|
|
702
|
+
if (this.isRunning(snapId)) {
|
|
703
|
+
await this.stopSnap(snapId, snaps_utils_1.SnapStatusEvents.Stop);
|
|
704
|
+
}
|
|
705
|
+
__classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_transition).call(this, snapId, snaps_utils_1.SnapStatusEvents.Update);
|
|
706
|
+
__classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_set).call(this, {
|
|
707
|
+
origin,
|
|
708
|
+
id: snapId,
|
|
709
|
+
manifest: newSnap.manifest,
|
|
710
|
+
sourceCode: newSnap.sourceCode,
|
|
711
|
+
versionRange: newVersionRange,
|
|
712
|
+
});
|
|
713
|
+
const unusedPermissionsKeys = Object.keys(unusedPermissions);
|
|
714
|
+
if ((0, utils_1.isNonEmptyArray)(unusedPermissionsKeys)) {
|
|
715
|
+
await this.messagingSystem.call('PermissionController:revokePermissions', {
|
|
716
|
+
[snapId]: unusedPermissionsKeys,
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
if ((0, utils_1.isNonEmptyArray)(Object.keys(approvedNewPermissions))) {
|
|
720
|
+
await this.messagingSystem.call('PermissionController:grantPermissions', {
|
|
721
|
+
approvedPermissions: approvedNewPermissions,
|
|
722
|
+
subject: { origin: snapId },
|
|
723
|
+
requestData,
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_startSnap).call(this, { snapId, sourceCode: newSnap.sourceCode });
|
|
727
|
+
const truncatedSnap = this.getTruncatedExpect(snapId);
|
|
728
|
+
this.messagingSystem.publish('SnapController:snapUpdated', truncatedSnap, snap.version);
|
|
729
|
+
return truncatedSnap;
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Fetches the manifest and source code of a snap.
|
|
733
|
+
*
|
|
734
|
+
* This function is not hash private yet because of tests.
|
|
735
|
+
*
|
|
736
|
+
* @param snapId - The id of the Snap.
|
|
737
|
+
* @param versionRange - The SemVer version of the Snap to fetch.
|
|
738
|
+
* @returns A tuple of the Snap manifest object and the Snap source code.
|
|
739
|
+
*/
|
|
740
|
+
async fetchSnap(snapId, versionRange = snaps_utils_1.DEFAULT_REQUESTED_SNAP_VERSION) {
|
|
741
|
+
try {
|
|
742
|
+
const snapPrefix = (0, snaps_utils_1.getSnapPrefix)(snapId);
|
|
743
|
+
switch (snapPrefix) {
|
|
744
|
+
case snaps_utils_1.SnapIdPrefixes.local:
|
|
745
|
+
return __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_fetchLocalSnap).call(this, snapId.replace(snaps_utils_1.SnapIdPrefixes.local, ''));
|
|
746
|
+
case snaps_utils_1.SnapIdPrefixes.npm:
|
|
747
|
+
return __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_fetchNpmSnap).call(this, snapId.replace(snaps_utils_1.SnapIdPrefixes.npm, ''), versionRange);
|
|
748
|
+
/* istanbul ignore next */
|
|
749
|
+
default:
|
|
750
|
+
// This whill fail to compile if the above switch is not fully exhaustive
|
|
751
|
+
return (0, utils_1.assertExhaustive)(snapPrefix);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
catch (error) {
|
|
755
|
+
throw new Error(`Failed to fetch Snap "${snapId}": ${error.message}`);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Initiates a request for the given snap's initial permissions.
|
|
760
|
+
* Must be called in order. See processRequestedSnap.
|
|
761
|
+
*
|
|
762
|
+
* This function is not hash private yet because of tests.
|
|
763
|
+
*
|
|
764
|
+
* @param origin - The origin of the install request.
|
|
765
|
+
* @param snapId - The id of the Snap.
|
|
766
|
+
* @returns The snap's approvedPermissions.
|
|
767
|
+
*/
|
|
768
|
+
async authorize(origin, snapId) {
|
|
769
|
+
console.info(`Authorizing snap: ${snapId}`);
|
|
770
|
+
const snapsState = this.state.snaps;
|
|
771
|
+
const snap = snapsState[snapId];
|
|
772
|
+
const { initialPermissions } = snap;
|
|
773
|
+
try {
|
|
774
|
+
const processedPermissions = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_processSnapPermissions).call(this, initialPermissions);
|
|
775
|
+
const id = (0, nanoid_1.nanoid)();
|
|
776
|
+
const _a = (await this.messagingSystem.call('ApprovalController:addRequest', {
|
|
777
|
+
origin,
|
|
778
|
+
id,
|
|
779
|
+
type: exports.SNAP_APPROVAL_INSTALL,
|
|
780
|
+
requestData: {
|
|
781
|
+
// Mirror previous installation metadata
|
|
782
|
+
metadata: { id, origin: snapId, dappOrigin: origin },
|
|
783
|
+
permissions: processedPermissions,
|
|
784
|
+
snapId,
|
|
785
|
+
},
|
|
786
|
+
}, true)), { permissions: approvedPermissions } = _a, requestData = __rest(_a, ["permissions"]);
|
|
787
|
+
if ((0, utils_1.isNonEmptyArray)(Object.keys(approvedPermissions))) {
|
|
788
|
+
await this.messagingSystem.call('PermissionController:grantPermissions', {
|
|
789
|
+
approvedPermissions,
|
|
790
|
+
subject: { origin: snapId },
|
|
791
|
+
requestData,
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
finally {
|
|
796
|
+
const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
|
|
797
|
+
runtime.installPromise = null;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
destroy() {
|
|
801
|
+
super.destroy();
|
|
802
|
+
if (__classPrivateFieldGet(this, _SnapController_timeoutForLastRequestStatus, "f")) {
|
|
803
|
+
clearTimeout(__classPrivateFieldGet(this, _SnapController_timeoutForLastRequestStatus, "f"));
|
|
804
|
+
}
|
|
805
|
+
this.messagingSystem.unsubscribe('ExecutionService:unhandledError', this._onUnhandledSnapError);
|
|
806
|
+
this.messagingSystem.unsubscribe('ExecutionService:outboundRequest', this._onOutboundRequest);
|
|
807
|
+
this.messagingSystem.unsubscribe('ExecutionService:outboundResponse', this._onOutboundResponse);
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Passes a JSON-RPC request object to the RPC handler function of a snap.
|
|
811
|
+
*
|
|
812
|
+
* @param options - A bag of options.
|
|
813
|
+
* @param options.snapId - The ID of the recipient snap.
|
|
814
|
+
* @param options.origin - The origin of the RPC request.
|
|
815
|
+
* @param options.handler - The handler to trigger on the snap for the request.
|
|
816
|
+
* @param options.request - The JSON-RPC request object.
|
|
817
|
+
* @returns The result of the JSON-RPC request.
|
|
818
|
+
*/
|
|
819
|
+
async handleRequest({ snapId, origin, handler: handlerType, request, }) {
|
|
820
|
+
const handler = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRpcRequestHandler).call(this, snapId);
|
|
821
|
+
if (!handler) {
|
|
822
|
+
throw new Error(`Snap RPC message handler not found for snap "${snapId}".`);
|
|
823
|
+
}
|
|
824
|
+
return handler({ origin, handler: handlerType, request });
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
exports.SnapController = SnapController;
|
|
828
|
+
_SnapController_closeAllConnections = new WeakMap(), _SnapController_environmentEndowmentPermissions = new WeakMap(), _SnapController_featureFlags = new WeakMap(), _SnapController_fetchFunction = new WeakMap(), _SnapController_idleTimeCheckInterval = new WeakMap(), _SnapController_checkSnapBlockList = new WeakMap(), _SnapController_maxIdleTime = new WeakMap(), _SnapController_npmRegistryUrl = new WeakMap(), _SnapController_getAppKey = new WeakMap(), _SnapController_timeoutForLastRequestStatus = new WeakMap(), _SnapController_statusMachine = new WeakMap(), _SnapController_instances = new WeakSet(), _SnapController_initializeStateMachine = function _SnapController_initializeStateMachine() {
|
|
829
|
+
const disableGuard = ({ snapId }) => {
|
|
830
|
+
return this.getExpect(snapId).enabled;
|
|
831
|
+
};
|
|
832
|
+
const statusConfig = {
|
|
833
|
+
initial: snaps_utils_1.SnapStatus.Installing,
|
|
834
|
+
states: {
|
|
835
|
+
[snaps_utils_1.SnapStatus.Installing]: {
|
|
836
|
+
on: {
|
|
837
|
+
[snaps_utils_1.SnapStatusEvents.Start]: {
|
|
838
|
+
target: snaps_utils_1.SnapStatus.Running,
|
|
839
|
+
cond: disableGuard,
|
|
840
|
+
},
|
|
841
|
+
},
|
|
842
|
+
},
|
|
843
|
+
[snaps_utils_1.SnapStatus.Updating]: {
|
|
844
|
+
on: {
|
|
845
|
+
[snaps_utils_1.SnapStatusEvents.Start]: {
|
|
846
|
+
target: snaps_utils_1.SnapStatus.Running,
|
|
847
|
+
cond: disableGuard,
|
|
848
|
+
},
|
|
849
|
+
},
|
|
850
|
+
},
|
|
851
|
+
[snaps_utils_1.SnapStatus.Running]: {
|
|
852
|
+
on: {
|
|
853
|
+
[snaps_utils_1.SnapStatusEvents.Stop]: snaps_utils_1.SnapStatus.Stopped,
|
|
854
|
+
[snaps_utils_1.SnapStatusEvents.Crash]: snaps_utils_1.SnapStatus.Crashed,
|
|
855
|
+
},
|
|
856
|
+
},
|
|
857
|
+
[snaps_utils_1.SnapStatus.Stopped]: {
|
|
858
|
+
on: {
|
|
859
|
+
[snaps_utils_1.SnapStatusEvents.Start]: {
|
|
860
|
+
target: snaps_utils_1.SnapStatus.Running,
|
|
861
|
+
cond: disableGuard,
|
|
862
|
+
},
|
|
863
|
+
[snaps_utils_1.SnapStatusEvents.Update]: snaps_utils_1.SnapStatus.Updating,
|
|
864
|
+
},
|
|
865
|
+
},
|
|
866
|
+
[snaps_utils_1.SnapStatus.Crashed]: {
|
|
867
|
+
on: {
|
|
868
|
+
[snaps_utils_1.SnapStatusEvents.Start]: {
|
|
869
|
+
target: snaps_utils_1.SnapStatus.Running,
|
|
870
|
+
cond: disableGuard,
|
|
871
|
+
},
|
|
872
|
+
},
|
|
873
|
+
},
|
|
874
|
+
},
|
|
875
|
+
};
|
|
876
|
+
__classPrivateFieldSet(this, _SnapController_statusMachine, (0, fsm_1.createMachine)(statusConfig), "f");
|
|
877
|
+
(0, fsm_2.validateMachine)(__classPrivateFieldGet(this, _SnapController_statusMachine, "f"));
|
|
878
|
+
}, _SnapController_registerMessageHandlers = function _SnapController_registerMessageHandlers() {
|
|
879
|
+
this.messagingSystem.registerActionHandler(`${exports.controllerName}:clearSnapState`, (...args) => this.clearSnapState(...args));
|
|
880
|
+
this.messagingSystem.registerActionHandler(`${exports.controllerName}:get`, (...args) => this.get(...args));
|
|
881
|
+
this.messagingSystem.registerActionHandler(`${exports.controllerName}:getSnapState`, (...args) => this.getSnapState(...args));
|
|
882
|
+
this.messagingSystem.registerActionHandler(`${exports.controllerName}:handleRequest`, (...args) => this.handleRequest(...args));
|
|
883
|
+
this.messagingSystem.registerActionHandler(`${exports.controllerName}:has`, (...args) => this.has(...args));
|
|
884
|
+
this.messagingSystem.registerActionHandler(`${exports.controllerName}:updateBlockedSnaps`, () => this.updateBlockedSnaps());
|
|
885
|
+
this.messagingSystem.registerActionHandler(`${exports.controllerName}:updateSnapState`, (...args) => this.updateSnapState(...args));
|
|
886
|
+
this.messagingSystem.registerActionHandler(`${exports.controllerName}:enable`, (...args) => this.enableSnap(...args));
|
|
887
|
+
this.messagingSystem.registerActionHandler(`${exports.controllerName}:disable`, (...args) => this.disableSnap(...args));
|
|
888
|
+
this.messagingSystem.registerActionHandler(`${exports.controllerName}:remove`, (...args) => this.removeSnap(...args));
|
|
889
|
+
this.messagingSystem.registerActionHandler(`${exports.controllerName}:getPermitted`, (...args) => this.getPermittedSnaps(...args));
|
|
890
|
+
this.messagingSystem.registerActionHandler(`${exports.controllerName}:install`, (...args) => this.installSnaps(...args));
|
|
891
|
+
this.messagingSystem.registerActionHandler(`${exports.controllerName}:removeSnapError`, (...args) => this.removeSnapError(...args));
|
|
892
|
+
this.messagingSystem.registerActionHandler(`${exports.controllerName}:getAll`, (...args) => this.getAllSnaps(...args));
|
|
893
|
+
this.messagingSystem.registerActionHandler(`${exports.controllerName}:incrementActiveReferences`, (...args) => this.incrementActiveReferences(...args));
|
|
894
|
+
this.messagingSystem.registerActionHandler(`${exports.controllerName}:decrementActiveReferences`, (...args) => this.decrementActiveReferences(...args));
|
|
895
|
+
}, _SnapController_pollForLastRequestStatus = function _SnapController_pollForLastRequestStatus() {
|
|
896
|
+
__classPrivateFieldSet(this, _SnapController_timeoutForLastRequestStatus, setTimeout(async () => {
|
|
897
|
+
await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_stopSnapsLastRequestPastMax).call(this);
|
|
898
|
+
__classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_pollForLastRequestStatus).call(this);
|
|
899
|
+
}, __classPrivateFieldGet(this, _SnapController_idleTimeCheckInterval, "f")), "f");
|
|
900
|
+
}, _SnapController_blockSnap =
|
|
901
|
+
/**
|
|
902
|
+
* Blocks an installed snap and prevents it from being started again. Emits
|
|
903
|
+
* {@link SnapBlocked}. Does nothing if the snap is not installed.
|
|
904
|
+
*
|
|
905
|
+
* @param snapId - The snap to block.
|
|
906
|
+
* @param blockedSnapInfo - Information detailing why the snap is blocked.
|
|
907
|
+
*/
|
|
908
|
+
async function _SnapController_blockSnap(snapId, blockedSnapInfo) {
|
|
909
|
+
if (!this.has(snapId)) {
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
try {
|
|
913
|
+
this.update((state) => {
|
|
914
|
+
state.snaps[snapId].blocked = true;
|
|
915
|
+
state.snaps[snapId].blockInformation = blockedSnapInfo;
|
|
916
|
+
});
|
|
917
|
+
await this.disableSnap(snapId);
|
|
918
|
+
}
|
|
919
|
+
catch (error) {
|
|
920
|
+
console.error(`Encountered error when stopping blocked snap "${snapId}".`, error);
|
|
921
|
+
}
|
|
922
|
+
this.messagingSystem.publish(`${exports.controllerName}:snapBlocked`, snapId, blockedSnapInfo);
|
|
923
|
+
}, _SnapController_unblockSnap =
|
|
924
|
+
/**
|
|
925
|
+
* Unblocks a snap so that it can be enabled and started again. Emits
|
|
926
|
+
* {@link SnapUnblocked}. Does nothing if the snap is not installed or already
|
|
927
|
+
* unblocked.
|
|
928
|
+
*
|
|
929
|
+
* @param snapId - The id of the snap to unblock.
|
|
930
|
+
*/
|
|
931
|
+
async function _SnapController_unblockSnap(snapId) {
|
|
932
|
+
if (!this.has(snapId) || !this.state.snaps[snapId].blocked) {
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
this.update((state) => {
|
|
936
|
+
state.snaps[snapId].blocked = false;
|
|
937
|
+
delete state.snaps[snapId].blockInformation;
|
|
938
|
+
});
|
|
939
|
+
this.messagingSystem.publish(`${exports.controllerName}:snapUnblocked`, snapId);
|
|
940
|
+
}, _SnapController_assertIsUnblocked =
|
|
941
|
+
/**
|
|
942
|
+
* Asserts that a version of a snap is not blocked. Succeeds automatically
|
|
943
|
+
* if {@link SnapController._checkSnapBlockList} is undefined.
|
|
944
|
+
*
|
|
945
|
+
* @param snapId - The id of the snap to check.
|
|
946
|
+
* @param snapInfo - Snap information containing version and shasum.
|
|
947
|
+
*/
|
|
948
|
+
async function _SnapController_assertIsUnblocked(snapId, snapInfo) {
|
|
949
|
+
if (await this.isBlocked(snapId, snapInfo)) {
|
|
950
|
+
throw new Error(`Cannot install version "${snapInfo.version}" of snap "${snapId}": the version is blocked.`);
|
|
951
|
+
}
|
|
952
|
+
}, _SnapController_stopSnapsLastRequestPastMax = async function _SnapController_stopSnapsLastRequestPastMax() {
|
|
953
|
+
const entries = [...this.snapsRuntimeData.entries()];
|
|
954
|
+
return Promise.all(entries
|
|
955
|
+
.filter(([_snapId, runtime]) => runtime.activeReferences === 0 &&
|
|
956
|
+
runtime.pendingInboundRequests.length === 0 &&
|
|
957
|
+
// lastRequest should always be set here but TypeScript wants this check
|
|
958
|
+
runtime.lastRequest &&
|
|
959
|
+
__classPrivateFieldGet(this, _SnapController_maxIdleTime, "f") &&
|
|
960
|
+
(0, utils_1.timeSince)(runtime.lastRequest) > __classPrivateFieldGet(this, _SnapController_maxIdleTime, "f"))
|
|
961
|
+
.map(([snapId]) => this.stopSnap(snapId, snaps_utils_1.SnapStatusEvents.Stop)));
|
|
962
|
+
}, _SnapController_transition = function _SnapController_transition(snapId, event) {
|
|
963
|
+
const { interpreter } = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
|
|
964
|
+
interpreter.send(event);
|
|
965
|
+
this.update((state) => {
|
|
966
|
+
state.snaps[snapId].status = interpreter.state.value;
|
|
967
|
+
});
|
|
968
|
+
}, _SnapController_terminateSnap =
|
|
969
|
+
/**
|
|
970
|
+
* Terminates the specified snap and emits the `snapTerminated` event.
|
|
971
|
+
*
|
|
972
|
+
* @param snapId - The snap to terminate.
|
|
973
|
+
*/
|
|
974
|
+
async function _SnapController_terminateSnap(snapId) {
|
|
975
|
+
await this.messagingSystem.call('ExecutionService:terminateSnap', snapId);
|
|
976
|
+
this.messagingSystem.publish('SnapController:snapTerminated', this.getTruncatedExpect(snapId));
|
|
977
|
+
}, _SnapController_getEncryptionKey = async function _SnapController_getEncryptionKey(snapId) {
|
|
978
|
+
return __classPrivateFieldGet(this, _SnapController_getAppKey, "f").call(this, snapId, AppKeyType.stateEncryption);
|
|
979
|
+
}, _SnapController_encryptSnapState = async function _SnapController_encryptSnapState(snapId, state) {
|
|
980
|
+
const appKey = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getEncryptionKey).call(this, snapId);
|
|
981
|
+
return browser_passworder_1.default.encrypt(appKey, state);
|
|
982
|
+
}, _SnapController_decryptSnapState = async function _SnapController_decryptSnapState(snapId, encrypted) {
|
|
983
|
+
const appKey = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getEncryptionKey).call(this, snapId);
|
|
984
|
+
try {
|
|
985
|
+
return await browser_passworder_1.default.decrypt(appKey, encrypted);
|
|
986
|
+
}
|
|
987
|
+
catch (err) {
|
|
988
|
+
throw new Error('Failed to decrypt snap state, the state must be corrupted.');
|
|
989
|
+
}
|
|
990
|
+
}, _SnapController_add =
|
|
991
|
+
/**
|
|
992
|
+
* Returns a promise representing the complete installation of the requested snap.
|
|
993
|
+
* If the snap is already being installed, the previously pending promise will be returned.
|
|
994
|
+
*
|
|
995
|
+
* @param args - Object containing the snap id and either the URL of the snap's manifest,
|
|
996
|
+
* or the snap's manifest and source code. The object may also optionally contain a target
|
|
997
|
+
* version.
|
|
998
|
+
* @returns The resulting snap object.
|
|
999
|
+
*/
|
|
1000
|
+
async function _SnapController_add(args) {
|
|
1001
|
+
const { id: snapId } = args;
|
|
1002
|
+
(0, snaps_utils_1.validateSnapId)(snapId);
|
|
1003
|
+
if (!args ||
|
|
1004
|
+
!('origin' in args) ||
|
|
1005
|
+
!('id' in args) ||
|
|
1006
|
+
(!('manifest' in args) && 'sourceCode' in args) ||
|
|
1007
|
+
('manifest' in args && !('sourceCode' in args))) {
|
|
1008
|
+
throw new Error(`Invalid add snap args for snap "${snapId}".`);
|
|
1009
|
+
}
|
|
1010
|
+
__classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_setupRuntime).call(this, snapId, { sourceCode: null, state: null });
|
|
1011
|
+
const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
|
|
1012
|
+
if (!runtime.installPromise) {
|
|
1013
|
+
console.info(`Adding snap: ${snapId}`);
|
|
1014
|
+
// If fetching and setting the snap succeeds, this property will be set
|
|
1015
|
+
// to null in the authorize() method.
|
|
1016
|
+
runtime.installPromise = (async () => {
|
|
1017
|
+
if ('manifest' in args && 'sourceCode' in args) {
|
|
1018
|
+
return __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_set).call(this, Object.assign(Object.assign({}, args), { id: snapId }));
|
|
1019
|
+
}
|
|
1020
|
+
const fetchedSnap = await this.fetchSnap(snapId, args.versionRange);
|
|
1021
|
+
await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_assertIsUnblocked).call(this, snapId, {
|
|
1022
|
+
version: fetchedSnap.manifest.version,
|
|
1023
|
+
shasum: fetchedSnap.manifest.source.shasum,
|
|
1024
|
+
});
|
|
1025
|
+
return __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_set).call(this, Object.assign(Object.assign(Object.assign({}, args), fetchedSnap), { id: snapId }));
|
|
1026
|
+
})();
|
|
1027
|
+
}
|
|
1028
|
+
try {
|
|
1029
|
+
return await runtime.installPromise;
|
|
1030
|
+
}
|
|
1031
|
+
catch (error) {
|
|
1032
|
+
// Reset promise so users can retry installation in case the problem is
|
|
1033
|
+
// temporary.
|
|
1034
|
+
runtime.installPromise = null;
|
|
1035
|
+
throw error;
|
|
1036
|
+
}
|
|
1037
|
+
}, _SnapController_startSnap = async function _SnapController_startSnap(snapData) {
|
|
1038
|
+
const { snapId } = snapData;
|
|
1039
|
+
if (this.isRunning(snapId)) {
|
|
1040
|
+
throw new Error(`Snap "${snapId}" is already started.`);
|
|
1041
|
+
}
|
|
1042
|
+
try {
|
|
1043
|
+
const result = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_executeWithTimeout).call(this, snapId, this.messagingSystem.call('ExecutionService:executeSnap', Object.assign(Object.assign({}, snapData), { endowments: await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getEndowments).call(this, snapId) })));
|
|
1044
|
+
__classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_transition).call(this, snapId, snaps_utils_1.SnapStatusEvents.Start);
|
|
1045
|
+
return result;
|
|
1046
|
+
}
|
|
1047
|
+
catch (err) {
|
|
1048
|
+
await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_terminateSnap).call(this, snapId);
|
|
1049
|
+
throw err;
|
|
1050
|
+
}
|
|
1051
|
+
}, _SnapController_getEndowments =
|
|
1052
|
+
/**
|
|
1053
|
+
* Gets the names of all endowments that will be added to the Snap's
|
|
1054
|
+
* Compartment when it executes. These should be the names of global
|
|
1055
|
+
* JavaScript APIs accessible in the root realm of the execution environment.
|
|
1056
|
+
*
|
|
1057
|
+
* Throws an error if the endowment getter for a permission returns a truthy
|
|
1058
|
+
* value that is not an array of strings.
|
|
1059
|
+
*
|
|
1060
|
+
* @param snapId - The id of the snap whose SES endowments to get.
|
|
1061
|
+
* @returns An array of the names of the endowments.
|
|
1062
|
+
*/
|
|
1063
|
+
async function _SnapController_getEndowments(snapId) {
|
|
1064
|
+
let allEndowments = [];
|
|
1065
|
+
for (const permissionName of __classPrivateFieldGet(this, _SnapController_environmentEndowmentPermissions, "f")) {
|
|
1066
|
+
if (await this.messagingSystem.call('PermissionController:hasPermission', snapId, permissionName)) {
|
|
1067
|
+
const endowments = await this.messagingSystem.call('PermissionController:getEndowments', snapId, permissionName);
|
|
1068
|
+
if (endowments) {
|
|
1069
|
+
// We don't have any guarantees about the type of the endowments
|
|
1070
|
+
// value, so we have to guard at runtime.
|
|
1071
|
+
if (!Array.isArray(endowments) ||
|
|
1072
|
+
endowments.some((value) => typeof value !== 'string')) {
|
|
1073
|
+
throw new Error('Expected an array of string endowment names.');
|
|
1074
|
+
}
|
|
1075
|
+
allEndowments = allEndowments.concat(endowments);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
const dedupedEndowments = [
|
|
1080
|
+
...new Set([...snaps_utils_1.DEFAULT_ENDOWMENTS, ...allEndowments]),
|
|
1081
|
+
];
|
|
1082
|
+
if (dedupedEndowments.length <
|
|
1083
|
+
snaps_utils_1.DEFAULT_ENDOWMENTS.length + allEndowments.length) {
|
|
1084
|
+
console.error('Duplicate endowments found. Default endowments should not be requested.', allEndowments);
|
|
1085
|
+
}
|
|
1086
|
+
return dedupedEndowments;
|
|
1087
|
+
}, _SnapController_set = function _SnapController_set(args) {
|
|
1088
|
+
var _a;
|
|
1089
|
+
const { id: snapId, origin, manifest, sourceCode, svgIcon, versionRange = snaps_utils_1.DEFAULT_REQUESTED_SNAP_VERSION, } = args;
|
|
1090
|
+
(0, snaps_utils_1.assertIsSnapManifest)(manifest);
|
|
1091
|
+
const { version } = manifest;
|
|
1092
|
+
if (!(0, snaps_utils_1.satisfiesVersionRange)(version, versionRange)) {
|
|
1093
|
+
throw new Error(`Version mismatch. Manifest for "${snapId}" specifies version "${version}" which doesn't satisfy requested version range "${versionRange}"`);
|
|
1094
|
+
}
|
|
1095
|
+
if (typeof sourceCode !== 'string' || sourceCode.length === 0) {
|
|
1096
|
+
throw new Error(`Invalid source code for snap "${snapId}".`);
|
|
1097
|
+
}
|
|
1098
|
+
const initialPermissions = manifest === null || manifest === void 0 ? void 0 : manifest.initialPermissions;
|
|
1099
|
+
if (!initialPermissions ||
|
|
1100
|
+
typeof initialPermissions !== 'object' ||
|
|
1101
|
+
Array.isArray(initialPermissions)) {
|
|
1102
|
+
throw new Error(`Invalid initial permissions for snap "${snapId}".`);
|
|
1103
|
+
}
|
|
1104
|
+
const snapsState = this.state.snaps;
|
|
1105
|
+
const existingSnap = snapsState[snapId];
|
|
1106
|
+
const previousVersionHistory = (_a = existingSnap === null || existingSnap === void 0 ? void 0 : existingSnap.versionHistory) !== null && _a !== void 0 ? _a : [];
|
|
1107
|
+
const versionHistory = [
|
|
1108
|
+
...previousVersionHistory,
|
|
1109
|
+
{
|
|
1110
|
+
version,
|
|
1111
|
+
date: Date.now(),
|
|
1112
|
+
origin,
|
|
1113
|
+
},
|
|
1114
|
+
];
|
|
1115
|
+
const snap = Object.assign(Object.assign({}, existingSnap), {
|
|
1116
|
+
// Note that the snap will be unblocked and enabled, regardless of its
|
|
1117
|
+
// previous state.
|
|
1118
|
+
blocked: false, enabled: true,
|
|
1119
|
+
// So we can easily correlate the snap with its permission
|
|
1120
|
+
permissionName: (0, snaps_utils_1.getSnapPermissionName)(snapId), id: snapId, initialPermissions,
|
|
1121
|
+
manifest, status: __classPrivateFieldGet(this, _SnapController_statusMachine, "f").config.initial, version,
|
|
1122
|
+
versionHistory });
|
|
1123
|
+
// If the snap was blocked, it isn't any longer
|
|
1124
|
+
delete snap.blockInformation;
|
|
1125
|
+
// store the snap back in state
|
|
1126
|
+
this.update((state) => {
|
|
1127
|
+
state.snaps[snapId] = snap;
|
|
1128
|
+
});
|
|
1129
|
+
const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
|
|
1130
|
+
runtime.sourceCode = sourceCode;
|
|
1131
|
+
this.messagingSystem.publish(`SnapController:snapAdded`, snap, svgIcon);
|
|
1132
|
+
return Object.assign(Object.assign({}, snap), { sourceCode });
|
|
1133
|
+
}, _SnapController_fetchNpmSnap = async function _SnapController_fetchNpmSnap(packageName, versionRange) {
|
|
1134
|
+
if (!(0, snaps_utils_1.isValidSnapVersionRange)(versionRange)) {
|
|
1135
|
+
throw new Error(`Received invalid Snap version range: "${versionRange}".`);
|
|
1136
|
+
}
|
|
1137
|
+
const { manifest, sourceCode, svgIcon } = await (0, utils_3.fetchNpmSnap)(packageName, versionRange, __classPrivateFieldGet(this, _SnapController_npmRegistryUrl, "f"), __classPrivateFieldGet(this, _SnapController_fetchFunction, "f"));
|
|
1138
|
+
return { manifest, sourceCode, svgIcon };
|
|
1139
|
+
}, _SnapController_fetchLocalSnap =
|
|
1140
|
+
/**
|
|
1141
|
+
* Fetches the manifest and source code of a local snap.
|
|
1142
|
+
*
|
|
1143
|
+
* @param localhostUrl - The localhost URL to download from.
|
|
1144
|
+
* @returns The validated manifest and the source code.
|
|
1145
|
+
*/
|
|
1146
|
+
async function _SnapController_fetchLocalSnap(localhostUrl) {
|
|
1147
|
+
// Local snaps are mostly used for development purposes. Fetches were cached in the browser and were not requested
|
|
1148
|
+
// afterwards which lead to confusing development where old versions of snaps were installed.
|
|
1149
|
+
// Thus we disable caching
|
|
1150
|
+
const fetchOptions = { cache: 'no-cache' };
|
|
1151
|
+
const manifestUrl = new URL(snaps_utils_1.NpmSnapFileNames.Manifest, localhostUrl);
|
|
1152
|
+
if (!snaps_utils_1.LOCALHOST_HOSTNAMES.has(manifestUrl.hostname)) {
|
|
1153
|
+
throw new Error(`Invalid URL: Locally hosted Snaps must be hosted on localhost. Received URL: "${manifestUrl.toString()}"`);
|
|
1154
|
+
}
|
|
1155
|
+
const manifest = await (await __classPrivateFieldGet(this, _SnapController_fetchFunction, "f").call(this, manifestUrl.toString(), fetchOptions)).json();
|
|
1156
|
+
(0, snaps_utils_1.assertIsSnapManifest)(manifest);
|
|
1157
|
+
const { source: { location: { npm: { filePath, iconPath }, }, }, } = manifest;
|
|
1158
|
+
const [sourceCode, svgIcon] = await Promise.all([
|
|
1159
|
+
(await __classPrivateFieldGet(this, _SnapController_fetchFunction, "f").call(this, new URL(filePath, localhostUrl).toString(), fetchOptions)).text(),
|
|
1160
|
+
iconPath
|
|
1161
|
+
? (await __classPrivateFieldGet(this, _SnapController_fetchFunction, "f").call(this, new URL(iconPath, localhostUrl).toString(), fetchOptions)).text()
|
|
1162
|
+
: undefined,
|
|
1163
|
+
]);
|
|
1164
|
+
(0, snaps_utils_1.validateSnapShasum)(manifest, sourceCode);
|
|
1165
|
+
return { manifest, sourceCode, svgIcon };
|
|
1166
|
+
}, _SnapController_processSnapPermissions = function _SnapController_processSnapPermissions(initialPermissions) {
|
|
1167
|
+
return (0, snaps_utils_1.fromEntries)(Object.entries(initialPermissions).map(([initialPermission, value]) => {
|
|
1168
|
+
if ((0, utils_1.hasProperty)(rpc_methods_1.caveatMappers, initialPermission)) {
|
|
1169
|
+
return [initialPermission, rpc_methods_1.caveatMappers[initialPermission](value)];
|
|
1170
|
+
}
|
|
1171
|
+
else if ((0, utils_1.hasProperty)(endowments_1.endowmentCaveatMappers, initialPermission)) {
|
|
1172
|
+
return [
|
|
1173
|
+
initialPermission,
|
|
1174
|
+
endowments_1.endowmentCaveatMappers[initialPermission](value),
|
|
1175
|
+
];
|
|
1176
|
+
}
|
|
1177
|
+
(0, utils_1.assert)(Object.keys(value).length === 0);
|
|
1178
|
+
return [initialPermission, {}];
|
|
1179
|
+
}));
|
|
1180
|
+
}, _SnapController_getRpcRequestHandler =
|
|
1181
|
+
/**
|
|
1182
|
+
* Gets the RPC message handler for the given snap.
|
|
1183
|
+
*
|
|
1184
|
+
* @param snapId - The id of the Snap whose message handler to get.
|
|
1185
|
+
* @returns The RPC handler for the given snap.
|
|
1186
|
+
*/
|
|
1187
|
+
async function _SnapController_getRpcRequestHandler(snapId) {
|
|
1188
|
+
const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
|
|
1189
|
+
const existingHandler = runtime.rpcHandler;
|
|
1190
|
+
if (existingHandler) {
|
|
1191
|
+
return existingHandler;
|
|
1192
|
+
}
|
|
1193
|
+
const requestQueue = new RequestQueue_1.RequestQueue(5);
|
|
1194
|
+
// We need to set up this promise map to map snapIds to their respective startPromises,
|
|
1195
|
+
// because otherwise we would lose context on the correct startPromise.
|
|
1196
|
+
const startPromises = new Map();
|
|
1197
|
+
const rpcHandler = async ({ origin, handler: handlerType, request, }) => {
|
|
1198
|
+
if (this.state.snaps[snapId].enabled === false) {
|
|
1199
|
+
throw new Error(`Snap "${snapId}" is disabled.`);
|
|
1200
|
+
}
|
|
1201
|
+
if (this.state.snaps[snapId].status === snaps_utils_1.SnapStatus.Installing) {
|
|
1202
|
+
throw new Error(`Snap "${snapId}" is currently being installed. Please try again later.`);
|
|
1203
|
+
}
|
|
1204
|
+
if (this.isRunning(snapId) === false) {
|
|
1205
|
+
let localStartPromise = startPromises.get(snapId);
|
|
1206
|
+
if (!localStartPromise) {
|
|
1207
|
+
localStartPromise = this.startSnap(snapId);
|
|
1208
|
+
startPromises.set(snapId, localStartPromise);
|
|
1209
|
+
}
|
|
1210
|
+
else if (requestQueue.get(origin) >= requestQueue.maxQueueSize) {
|
|
1211
|
+
throw new Error('Exceeds maximum number of requests waiting to be resolved, please try again.');
|
|
1212
|
+
}
|
|
1213
|
+
requestQueue.increment(origin);
|
|
1214
|
+
try {
|
|
1215
|
+
await localStartPromise;
|
|
1216
|
+
}
|
|
1217
|
+
finally {
|
|
1218
|
+
requestQueue.decrement(origin);
|
|
1219
|
+
// Only delete startPromise for a snap if its value hasn't changed
|
|
1220
|
+
if (startPromises.get(snapId) === localStartPromise) {
|
|
1221
|
+
startPromises.delete(snapId);
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
let _request = request;
|
|
1226
|
+
if (!(0, utils_1.hasProperty)(request, 'jsonrpc')) {
|
|
1227
|
+
_request = Object.assign(Object.assign({}, request), { jsonrpc: '2.0' });
|
|
1228
|
+
}
|
|
1229
|
+
else if (request.jsonrpc !== '2.0') {
|
|
1230
|
+
throw eth_rpc_errors_1.ethErrors.rpc.invalidRequest({
|
|
1231
|
+
message: 'Invalid "jsonrpc" property. Must be "2.0" if provided.',
|
|
1232
|
+
data: request.jsonrpc,
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1235
|
+
const timer = new Timer_1.Timer(this.maxRequestTime);
|
|
1236
|
+
__classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_recordSnapRpcRequestStart).call(this, snapId, request.id, timer);
|
|
1237
|
+
const handleRpcRequestPromise = this.messagingSystem.call('ExecutionService:handleRpcRequest', snapId, { origin, handler: handlerType, request: _request });
|
|
1238
|
+
// This will either get the result or reject due to the timeout.
|
|
1239
|
+
try {
|
|
1240
|
+
const result = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_executeWithTimeout).call(this, snapId, handleRpcRequestPromise, timer);
|
|
1241
|
+
__classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_recordSnapRpcRequestFinish).call(this, snapId, request.id);
|
|
1242
|
+
return result;
|
|
1243
|
+
}
|
|
1244
|
+
catch (err) {
|
|
1245
|
+
await this.stopSnap(snapId, snaps_utils_1.SnapStatusEvents.Crash);
|
|
1246
|
+
throw err;
|
|
1247
|
+
}
|
|
1248
|
+
};
|
|
1249
|
+
runtime.rpcHandler = rpcHandler;
|
|
1250
|
+
return rpcHandler;
|
|
1251
|
+
}, _SnapController_executeWithTimeout =
|
|
1252
|
+
/**
|
|
1253
|
+
* Awaits the specified promise and rejects if the promise doesn't resolve
|
|
1254
|
+
* before the timeout.
|
|
1255
|
+
*
|
|
1256
|
+
* @param snapId - The snap id.
|
|
1257
|
+
* @param promise - The promise to await.
|
|
1258
|
+
* @param timer - An optional timer object to control the timeout.
|
|
1259
|
+
* @returns The result of the promise or rejects if the promise times out.
|
|
1260
|
+
* @template PromiseValue - The value of the Promise.
|
|
1261
|
+
*/
|
|
1262
|
+
async function _SnapController_executeWithTimeout(snapId, promise, timer) {
|
|
1263
|
+
const isLongRunning = await this.messagingSystem.call('PermissionController:hasPermission', snapId, endowments_1.SnapEndowments.LongRunning);
|
|
1264
|
+
// Long running snaps have timeouts disabled
|
|
1265
|
+
if (isLongRunning) {
|
|
1266
|
+
return promise;
|
|
1267
|
+
}
|
|
1268
|
+
const result = await (0, utils_2.withTimeout)(promise, timer !== null && timer !== void 0 ? timer : this.maxRequestTime);
|
|
1269
|
+
if (result === utils_2.hasTimedOut) {
|
|
1270
|
+
throw new Error('The request timed out.');
|
|
1271
|
+
}
|
|
1272
|
+
return result;
|
|
1273
|
+
}, _SnapController_recordSnapRpcRequestStart = function _SnapController_recordSnapRpcRequestStart(snapId, requestId, timer) {
|
|
1274
|
+
const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
|
|
1275
|
+
runtime.pendingInboundRequests.push({ requestId, timer });
|
|
1276
|
+
runtime.lastRequest = null;
|
|
1277
|
+
}, _SnapController_recordSnapRpcRequestFinish = function _SnapController_recordSnapRpcRequestFinish(snapId, requestId) {
|
|
1278
|
+
const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
|
|
1279
|
+
runtime.pendingInboundRequests = runtime.pendingInboundRequests.filter((r) => r.requestId !== requestId);
|
|
1280
|
+
if (runtime.pendingInboundRequests.length === 0) {
|
|
1281
|
+
runtime.lastRequest = Date.now();
|
|
1282
|
+
}
|
|
1283
|
+
}, _SnapController_getRuntime = function _SnapController_getRuntime(snapId) {
|
|
1284
|
+
return this.snapsRuntimeData.get(snapId);
|
|
1285
|
+
}, _SnapController_getRuntimeExpect = function _SnapController_getRuntimeExpect(snapId) {
|
|
1286
|
+
const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntime).call(this, snapId);
|
|
1287
|
+
(0, utils_1.assert)(runtime !== undefined, new Error(`Snap "${snapId}" runtime data not found`));
|
|
1288
|
+
return runtime;
|
|
1289
|
+
}, _SnapController_setupRuntime = function _SnapController_setupRuntime(snapId, data) {
|
|
1290
|
+
var _a;
|
|
1291
|
+
if (this.snapsRuntimeData.has(snapId)) {
|
|
1292
|
+
return;
|
|
1293
|
+
}
|
|
1294
|
+
const snap = this.get(snapId);
|
|
1295
|
+
const interpreter = (0, fsm_1.interpret)(__classPrivateFieldGet(this, _SnapController_statusMachine, "f"));
|
|
1296
|
+
interpreter.start({
|
|
1297
|
+
context: { snapId },
|
|
1298
|
+
value: (_a = snap === null || snap === void 0 ? void 0 : snap.status) !== null && _a !== void 0 ? _a : __classPrivateFieldGet(this, _SnapController_statusMachine, "f").config.initial,
|
|
1299
|
+
});
|
|
1300
|
+
(0, fsm_2.forceStrict)(interpreter);
|
|
1301
|
+
this.snapsRuntimeData.set(snapId, Object.assign({ lastRequest: null, rpcHandler: null, installPromise: null, activeReferences: 0, pendingInboundRequests: [], pendingOutboundRequests: 0, interpreter }, data));
|
|
1302
|
+
}, _SnapController_calculatePermissionsChange = async function _SnapController_calculatePermissionsChange(snapId, desiredPermissionsSet) {
|
|
1303
|
+
var _a;
|
|
1304
|
+
const oldPermissions = (_a = (await this.messagingSystem.call('PermissionController:getPermissions', snapId))) !== null && _a !== void 0 ? _a : {};
|
|
1305
|
+
const newPermissions = (0, utils_2.setDiff)(desiredPermissionsSet, oldPermissions);
|
|
1306
|
+
// TODO(ritave): The assumption that these are unused only holds so long as we do not
|
|
1307
|
+
// permit dynamic permission requests.
|
|
1308
|
+
const unusedPermissions = (0, utils_2.setDiff)(oldPermissions, desiredPermissionsSet);
|
|
1309
|
+
// It's a Set Intersection of oldPermissions and desiredPermissionsSet
|
|
1310
|
+
// oldPermissions ∖ (oldPermissions ∖ desiredPermissionsSet) ⟺ oldPermissions ∩ desiredPermissionsSet
|
|
1311
|
+
const approvedPermissions = (0, utils_2.setDiff)(oldPermissions, unusedPermissions);
|
|
1312
|
+
return { newPermissions, unusedPermissions, approvedPermissions };
|
|
1313
|
+
};
|
|
1314
|
+
//# sourceMappingURL=SnapController.js.map
|