From 926e938c76dce81fb9aaa726eb798630b8cf705b Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Wed, 26 Apr 2023 17:17:00 -0700 Subject: [PATCH] feat(ertp,kernel): use pattern-based compression --- packages/ERTP/src/paymentLedger.js | 1 + .../src/collectionManager.js | 21 +++++++---- .../src/virtualObjectManager.js | 35 +++++++++++++------ 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/packages/ERTP/src/paymentLedger.js b/packages/ERTP/src/paymentLedger.js index 681ddd2eb20..11c444456ce 100644 --- a/packages/ERTP/src/paymentLedger.js +++ b/packages/ERTP/src/paymentLedger.js @@ -37,6 +37,7 @@ const amountShapeFromElementShape = (brand, assetKind, elementShape) => { if (elementShape === undefined) { valueShape = M.arrayOf(M.key()); } else { + // M.and compresses only according to its last conjunct valueShape = M.arrayOf(M.and(M.key(), elementShape)); } break; diff --git a/packages/swingset-liveslots/src/collectionManager.js b/packages/swingset-liveslots/src/collectionManager.js index 178514988f8..57989dfff0c 100644 --- a/packages/swingset-liveslots/src/collectionManager.js +++ b/packages/swingset-liveslots/src/collectionManager.js @@ -17,6 +17,8 @@ import { getRankCover, getCopyMapEntries, getCopySetKeys, + mustCompress, + mustDecompress, } from '@endo/patterns'; import { isCopyMap, isCopySet } from '@agoric/store'; import { makeBaseRef, parseVatSlot } from './parseVatSlots.js'; @@ -274,19 +276,24 @@ export function makeCollectionManager( const serializeValue = value => { const { valueShape, label } = getSchema(); - if (valueShape !== undefined) { - mustMatch(value, valueShape, makeInvalidValueTypeMsg(label)); + if (valueShape === undefined) { + return serialize(value); } - return serialize(value); + return serialize( + mustCompress(value, valueShape, makeInvalidValueTypeMsg(label)), + ); }; const unserializeValue = data => { const { valueShape, label } = getSchema(); - const value = unserialize(data); - if (valueShape !== undefined) { - mustMatch(value, valueShape, makeInvalidValueTypeMsg(label)); + if (valueShape === undefined) { + return unserialize(data); } - return value; + return mustDecompress( + unserialize(data), + valueShape, + makeInvalidValueTypeMsg(label), + ); }; function prefix(dbEntryKey) { diff --git a/packages/swingset-liveslots/src/virtualObjectManager.js b/packages/swingset-liveslots/src/virtualObjectManager.js index 3a349f483cc..0170b192146 100644 --- a/packages/swingset-liveslots/src/virtualObjectManager.js +++ b/packages/swingset-liveslots/src/virtualObjectManager.js @@ -8,7 +8,7 @@ import { quote as q, bare as b, } from '@endo/errors'; -import { assertPattern, mustMatch } from '@agoric/store'; +import { assertPattern, mustCompress, mustDecompress } from '@endo/patterns'; import { defendPrototype, defendPrototypeKit } from '@endo/exo/tools.js'; import { Far, passStyleOf } from '@endo/marshal'; import { Nat } from '@endo/nat'; @@ -821,8 +821,12 @@ export const makeVirtualObjectManager = ( /** @type {(prop: string) => void} */ let checkStateProperty = _prop => {}; - /** @type {(value: any, prop: string) => void} */ - let checkStatePropertyValue = (_value, _prop) => {}; + let serializeStatePropertyValue = (value, _prop, _label) => { + return serialize(value); + }; + let unserializeStatePropertyValue = (data, _prop, _label) => { + return harden(unserialize(data)); + }; if (stateShape) { checkStateProperty = prop => { hasOwn(stateShape, prop) || @@ -830,9 +834,17 @@ export const makeVirtualObjectManager = ( ownKeys(stateShape), )}`; }; - checkStatePropertyValue = (value, prop) => { + serializeStatePropertyValue = (value, prop, label) => { checkStateProperty(prop); - mustMatch(value, stateShape[prop]); + return serialize(mustCompress(value, stateShape[prop], label)); + }; + unserializeStatePropertyValue = (data, prop, label) => { + checkStateProperty(prop); + return mustDecompress( + harden(unserialize(data)), + stateShape[prop], + label, + ); }; } @@ -866,16 +878,18 @@ export const makeVirtualObjectManager = ( assert(record !== undefined); const { valueMap, capdatas } = record; if (!valueMap.has(prop)) { - const value = harden(unserialize(capdatas[prop])); - checkStatePropertyValue(value, prop); + const value = unserializeStatePropertyValue( + capdatas[prop], + prop, + prop, + ); valueMap.set(prop, value); } return valueMap.get(prop); }, set(value) { const baseRef = getBaseRef(this); - checkStatePropertyValue(value, prop); - const capdata = serialize(value); + const capdata = serializeStatePropertyValue(value, prop, prop); assertAcceptableSyscallCapdataSize([capdata]); if (isDurable) { insistDurableCapdata(vrm, prop, capdata, true); @@ -1066,8 +1080,7 @@ export const makeVirtualObjectManager = ( const valueMap = new Map(); for (const prop of getOwnPropertyNames(initialData)) { const value = initialData[prop]; - checkStatePropertyValue(value, prop); - const valueCD = serialize(value); + const valueCD = serializeStatePropertyValue(value, prop, prop); // TODO: we're only checking the size of one property at a // time, but the real constraint is the vatstoreSet of the // aggregate record. We should apply this check to the full