Skip to content

Commit 3b060ec

Browse files
perf: Reduce JSON validation during state updates
1 parent 0c693f0 commit 3b060ec

File tree

5 files changed

+107
-59
lines changed

5 files changed

+107
-59
lines changed

packages/snaps-rpc-methods/src/permitted/setState.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,14 @@ describe('snap_setState', () => {
3434
const updateSnapState = jest.fn().mockReturnValue(null);
3535
const getUnlockPromise = jest.fn().mockResolvedValue(undefined);
3636
const hasPermission = jest.fn().mockReturnValue(true);
37+
const getSnap = jest.fn().mockReturnValue({ preinstalled: false });
3738

3839
const hooks = {
3940
getSnapState,
4041
updateSnapState,
4142
getUnlockPromise,
4243
hasPermission,
44+
getSnap,
4345
};
4446

4547
const engine = new JsonRpcEngine();
@@ -87,12 +89,14 @@ describe('snap_setState', () => {
8789
const updateSnapState = jest.fn().mockReturnValue(null);
8890
const getUnlockPromise = jest.fn().mockResolvedValue(undefined);
8991
const hasPermission = jest.fn().mockReturnValue(true);
92+
const getSnap = jest.fn().mockReturnValue({ preinstalled: false });
9093

9194
const hooks = {
9295
getSnapState,
9396
updateSnapState,
9497
getUnlockPromise,
9598
hasPermission,
99+
getSnap,
96100
};
97101

98102
const engine = new JsonRpcEngine();
@@ -141,12 +145,14 @@ describe('snap_setState', () => {
141145
const updateSnapState = jest.fn().mockReturnValue(null);
142146
const getUnlockPromise = jest.fn().mockResolvedValue(undefined);
143147
const hasPermission = jest.fn().mockReturnValue(true);
148+
const getSnap = jest.fn().mockReturnValue({ preinstalled: false });
144149

145150
const hooks = {
146151
getSnapState,
147152
updateSnapState,
148153
getUnlockPromise,
149154
hasPermission,
155+
getSnap,
150156
};
151157

152158
const engine = new JsonRpcEngine();
@@ -200,12 +206,14 @@ describe('snap_setState', () => {
200206
const updateSnapState = jest.fn().mockReturnValue(null);
201207
const getUnlockPromise = jest.fn().mockResolvedValue(undefined);
202208
const hasPermission = jest.fn().mockReturnValue(false);
209+
const getSnap = jest.fn().mockReturnValue({ preinstalled: false });
203210

204211
const hooks = {
205212
getSnapState,
206213
updateSnapState,
207214
getUnlockPromise,
208215
hasPermission,
216+
getSnap,
209217
};
210218

211219
const engine = new JsonRpcEngine();
@@ -252,12 +260,14 @@ describe('snap_setState', () => {
252260
const updateSnapState = jest.fn().mockReturnValue(null);
253261
const getUnlockPromise = jest.fn().mockResolvedValue(undefined);
254262
const hasPermission = jest.fn().mockReturnValue(true);
263+
const getSnap = jest.fn().mockReturnValue({ preinstalled: false });
255264

256265
const hooks = {
257266
getSnapState,
258267
updateSnapState,
259268
getUnlockPromise,
260269
hasPermission,
270+
getSnap,
261271
};
262272

263273
const engine = new JsonRpcEngine();
@@ -303,12 +313,14 @@ describe('snap_setState', () => {
303313
const updateSnapState = jest.fn().mockReturnValue(null);
304314
const getUnlockPromise = jest.fn().mockResolvedValue(undefined);
305315
const hasPermission = jest.fn().mockReturnValue(true);
316+
const getSnap = jest.fn().mockReturnValue({ preinstalled: false });
306317

307318
const hooks = {
308319
getSnapState,
309320
updateSnapState,
310321
getUnlockPromise,
311322
hasPermission,
323+
getSnap,
312324
};
313325

314326
const engine = new JsonRpcEngine();
@@ -358,12 +370,14 @@ describe('snap_setState', () => {
358370
const updateSnapState = jest.fn().mockReturnValue(null);
359371
const getUnlockPromise = jest.fn().mockResolvedValue(undefined);
360372
const hasPermission = jest.fn().mockReturnValue(true);
373+
const getSnap = jest.fn().mockReturnValue({ preinstalled: false });
361374

362375
const hooks = {
363376
getSnapState,
364377
updateSnapState,
365378
getUnlockPromise,
366379
hasPermission,
380+
getSnap,
367381
};
368382

369383
const engine = new JsonRpcEngine();
@@ -408,12 +422,14 @@ describe('snap_setState', () => {
408422
const updateSnapState = jest.fn().mockReturnValue(null);
409423
const getUnlockPromise = jest.fn().mockResolvedValue(undefined);
410424
const hasPermission = jest.fn().mockReturnValue(true);
425+
const getSnap = jest.fn().mockReturnValue({ preinstalled: false });
411426

412427
const hooks = {
413428
getSnapState,
414429
updateSnapState,
415430
getUnlockPromise,
416431
hasPermission,
432+
getSnap,
417433
};
418434

419435
const engine = new JsonRpcEngine();
@@ -461,12 +477,14 @@ describe('snap_setState', () => {
461477
const updateSnapState = jest.fn().mockReturnValue(null);
462478
const getUnlockPromise = jest.fn().mockResolvedValue(undefined);
463479
const hasPermission = jest.fn().mockReturnValue(true);
480+
const getSnap = jest.fn().mockReturnValue({ preinstalled: false });
464481

465482
const hooks = {
466483
getSnapState,
467484
updateSnapState,
468485
getUnlockPromise,
469486
hasPermission,
487+
getSnap,
470488
};
471489

472490
const engine = new JsonRpcEngine();

packages/snaps-rpc-methods/src/permitted/setState.ts

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ import type { PermittedHandlerExport } from '@metamask/permission-controller';
33
import { providerErrors, rpcErrors } from '@metamask/rpc-errors';
44
import type { SetStateParams, SetStateResult } from '@metamask/snaps-sdk';
55
import type { JsonObject } from '@metamask/snaps-sdk/jsx';
6-
import { type InferMatching } from '@metamask/snaps-utils';
6+
import {
7+
getJsonSizeUnsafe,
8+
type InferMatching,
9+
type Snap,
10+
} from '@metamask/snaps-utils';
711
import {
812
boolean,
913
create,
@@ -16,13 +20,7 @@ import type {
1620
Json,
1721
JsonRpcRequest,
1822
} from '@metamask/utils';
19-
import {
20-
getJsonSize,
21-
hasProperty,
22-
isObject,
23-
assert,
24-
JsonStruct,
25-
} from '@metamask/utils';
23+
import { hasProperty, isObject, assert, JsonStruct } from '@metamask/utils';
2624

2725
import {
2826
manageStateBuilder,
@@ -36,6 +34,7 @@ const hookNames: MethodHooksObject<SetStateHooks> = {
3634
getSnapState: true,
3735
getUnlockPromise: true,
3836
updateSnapState: true,
37+
getSnap: true,
3938
};
4039

4140
/**
@@ -85,6 +84,13 @@ export type SetStateHooks = {
8584
newState: Record<string, Json>,
8685
encrypted: boolean,
8786
) => Promise<void>;
87+
88+
/**
89+
* Get Snap metadata.
90+
*
91+
* @param snapId - The ID of a Snap.
92+
*/
93+
getSnap: (snapId: string) => Snap | undefined;
8894
};
8995

9096
const SetStateParametersStruct = objectStruct({
@@ -112,6 +118,7 @@ export type SetStateParameters = InferMatching<
112118
* @param hooks.getSnapState - Get the state of the requesting Snap.
113119
* @param hooks.getUnlockPromise - Wait for the extension to be unlocked.
114120
* @param hooks.updateSnapState - Update the state of the requesting Snap.
121+
* @param hooks.getSnap - The hook function to get Snap metadata.
115122
* @returns Nothing.
116123
*/
117124
async function setStateImplementation(
@@ -124,6 +131,7 @@ async function setStateImplementation(
124131
getSnapState,
125132
getUnlockPromise,
126133
updateSnapState,
134+
getSnap,
127135
}: SetStateHooks,
128136
): Promise<void> {
129137
const { params } = request;
@@ -150,13 +158,20 @@ async function setStateImplementation(
150158

151159
const newState = await getNewState(key, value, encrypted, getSnapState);
152160

153-
const size = getJsonSize(newState);
154-
if (size > STORAGE_SIZE_LIMIT) {
155-
throw rpcErrors.invalidParams({
156-
message: `Invalid params: The new state must not exceed ${
157-
STORAGE_SIZE_LIMIT / 1_000_000
158-
} MB in size.`,
159-
});
161+
const snap = getSnap(
162+
(request as JsonRpcRequest<SetStateParams> & { origin: string }).origin,
163+
);
164+
165+
if (!snap?.preinstalled) {
166+
// We know that the state is valid JSON as per previous validation.
167+
const size = getJsonSizeUnsafe(newState, true);
168+
if (size > STORAGE_SIZE_LIMIT) {
169+
throw rpcErrors.invalidParams({
170+
message: `Invalid params: The new state must not exceed ${
171+
STORAGE_SIZE_LIMIT / 1_000_000
172+
} MB in size.`,
173+
});
174+
}
160175
}
161176

162177
await updateSnapState(newState, encrypted);

0 commit comments

Comments
 (0)