Skip to content

Commit 7e458fe

Browse files
committed
fix: pattern based compression on split reform
1 parent ab70895 commit 7e458fe

23 files changed

+975
-171
lines changed

packages/ERTP/src/paymentLedger.js

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const amountShapeFromElementShape = (brand, assetKind, elementShape) => {
3434
if (elementShape === undefined) {
3535
valueShape = M.arrayOf(M.key());
3636
} else {
37+
// M.and compresses only according to its last conjunct
3738
valueShape = M.arrayOf(M.and(M.key(), elementShape));
3839
}
3940
break;
@@ -139,6 +140,7 @@ export const vivifyPaymentLedger = (
139140
const paymentLedger = provideDurableWeakMapStore(
140141
issuerBaggage,
141142
'paymentLedger',
143+
{ valueShape: amountShape },
142144
);
143145

144146
/**

packages/ERTP/src/purse.js

+9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { M } from '@agoric/store';
12
import { vivifyFarClassKit, makeScalarBigSetStore } from '@agoric/vat-data';
23
import { AmountMath } from './amountMath.js';
34
import { makeTransientNotifierKit } from './transientNotifier.js';
@@ -12,6 +13,8 @@ export const vivifyPurseKind = (
1213
PurseIKit,
1314
purseMethods,
1415
) => {
16+
const amountShape = brand.getAmountShape();
17+
1518
// Note: Virtual for high cardinality, but *not* durable, and so
1619
// broken across an upgrade.
1720
const { provideNotifier, update: updateBalance } = makeTransientNotifierKit();
@@ -111,6 +114,12 @@ export const vivifyPurseKind = (
111114
},
112115
},
113116
},
117+
{
118+
stateShape: {
119+
currentBalance: amountShape,
120+
recoverySet: M.remotable('recoverySet'),
121+
},
122+
},
114123
);
115124
return () => makePurseKit().purse;
116125
};

packages/ERTP/test/unitTests/test-inputValidation.js

+7-8
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ test('makeIssuerKit bad displayInfo.decimalPlaces', async t => {
3333
harden({ decimalPlaces: 'hello' }),
3434
),
3535
{
36-
message:
37-
'displayInfo: optional: decimalPlaces: "hello" - Must be >= -100',
36+
message: 'displayInfo: decimalPlaces?: "hello" - Must be >= -100',
3837
},
3938
);
4039

@@ -62,15 +61,15 @@ test('makeIssuerKit bad displayInfo.decimalPlaces', async t => {
6261
() =>
6362
makeIssuerKit('myTokens', AssetKind.NAT, harden({ decimalPlaces: 101 })),
6463
{
65-
message: 'displayInfo: optional: decimalPlaces: 101 - Must be <= 100',
64+
message: 'displayInfo: decimalPlaces?: 101 - Must be <= 100',
6665
},
6766
);
6867

6968
t.throws(
7069
() =>
7170
makeIssuerKit('myTokens', AssetKind.NAT, harden({ decimalPlaces: -101 })),
7271
{
73-
message: 'displayInfo: optional: decimalPlaces: -101 - Must be >= -100',
72+
message: 'displayInfo: decimalPlaces?: -101 - Must be >= -100',
7473
},
7574
);
7675
});
@@ -88,7 +87,7 @@ test('makeIssuerKit bad displayInfo.assetKind', async t => {
8887
),
8988
{
9089
message:
91-
'displayInfo: optional: assetKind: "something" - Must match one of ["nat","set","copySet","copyBag"]',
90+
'displayInfo: assetKind?: "something" - Must match one of ["nat","set","copySet","copyBag"]',
9291
},
9392
);
9493
});
@@ -105,7 +104,7 @@ test('makeIssuerKit bad displayInfo.whatever', async t => {
105104
}),
106105
),
107106
{
108-
message: 'displayInfo: rest: {"whatever":"something"} - Must be: {}',
107+
message: 'displayInfo: ...rest: {"whatever":"something"} - Must be: {}',
109108
},
110109
);
111110
});
@@ -142,7 +141,7 @@ test('brand.isMyIssuer bad issuer', async t => {
142141
// @ts-expect-error Intentional wrong type for testing
143142
t.throwsAsync(() => brand.isMyIssuer('not an issuer'), {
144143
message:
145-
'In "isMyIssuer" method of (myTokens brand): args: [0]: string "not an issuer" - Must be a remotable (Issuer)',
144+
'In "isMyIssuer" method of (myTokens brand): arg 0: string "not an issuer" - Must be a remotable (Issuer)',
146145
});
147146
const fakeIssuer = /** @type {Issuer} */ (
148147
/** @type {unknown} */ (Far('myTokens issuer', {}))
@@ -195,7 +194,7 @@ test('issuer.combine bad payments array', async t => {
195194
// @ts-expect-error Intentional wrong type for testing
196195
await t.throwsAsync(() => E(issuer).combine(notAnArray2), {
197196
message:
198-
'In "combine" method of (fungible issuer): args: [0]: remotable "[Alleged: notAnArray2]" - Must be a copyArray',
197+
'In "combine" method of (fungible issuer): arg 0: remotable "[Alleged: notAnArray2]" - Must be a copyArray',
199198
});
200199
});
201200

packages/ERTP/test/unitTests/test-issuerObj.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ test('bad display info', t => {
4444
const displayInfo = harden({ somethingUnexpected: 3 });
4545
// @ts-expect-error deliberate invalid arguments for testing
4646
t.throws(() => makeIssuerKit('fungible', AssetKind.NAT, displayInfo), {
47-
message: 'displayInfo: rest: {"somethingUnexpected":3} - Must be: {}',
47+
message: 'displayInfo: ...rest: {"somethingUnexpected":3} - Must be: {}',
4848
});
4949
});
5050

@@ -200,7 +200,7 @@ test('purse.deposit promise', async t => {
200200
() => E(purse).deposit(exclusivePaymentP, fungible25),
201201
{
202202
message:
203-
'In "deposit" method of (fungible Purse purse): args: [0]: promise "[Promise]" - Must be a remotable (Payment)',
203+
'In "deposit" method of (fungible Purse purse): arg 0: promise "[Promise]" - Must be a remotable (Payment)',
204204
},
205205
'failed to reject a promise for a payment',
206206
);
@@ -335,7 +335,7 @@ test('issuer.split bad amount', async t => {
335335
_ => E(issuer).split(payment, AmountMath.make(otherBrand, 10n)),
336336
{
337337
message:
338-
'In "split" method of (fungible issuer): args: [1]: brand: "[Alleged: other fungible brand]" - Must be: "[Alleged: fungible brand]"',
338+
'In "split" method of (fungible issuer): arg 1: brand: "[Alleged: other fungible brand]" - Must be: "[Alleged: fungible brand]"',
339339
},
340340
'throws for bad amount',
341341
);

packages/ERTP/test/unitTests/test-mintObj.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ test('mint.mintPayment set w strings AssetKind', async t => {
4646
const badAmount = AmountMath.make(brand, harden([['badElement']]));
4747
t.throws(() => mint.mintPayment(badAmount), {
4848
message:
49-
'In "mintPayment" method of (items mint): args: [0]: value: [0]: copyArray ["badElement"] - Must be a string',
49+
'In "mintPayment" method of (items mint): arg 0: value: [0]: copyArray ["badElement"] - Must be a string',
5050
});
5151
});
5252

packages/SwingSet/src/liveslots/collectionManager.js

+30-22
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
assertKeyPattern,
66
assertPattern,
77
matches,
8+
fit,
89
compareRank,
910
M,
1011
zeroPad,
@@ -13,6 +14,8 @@ import {
1314
isEncodedRemotable,
1415
makeCopySet,
1516
makeCopyMap,
17+
mustCompress,
18+
decompress,
1619
} from '@agoric/store';
1720
import { Far, passStyleOf } from '@endo/marshal';
1821
import { decodeToJustin } from '@endo/marshal/src/marshal-justin.js';
@@ -213,7 +216,7 @@ export function makeCollectionManager(
213216
return storeKindInfo[kindName].kindID;
214217
}
215218

216-
// Not that it's only used for this purpose, what should it be called?
219+
// Now that it's only used for this purpose, what should it be called?
217220
// TODO Should we be using the new encodeBigInt scheme instead, anyway?
218221
const BIGINT_TAG_LEN = 10;
219222

@@ -257,6 +260,23 @@ export function makeCollectionManager(
257260
const dbKeyPrefix = `vc.${collectionID}.`;
258261
let currentGenerationNumber = 0;
259262

263+
const keyLabel = `invalid key type for collection ${q(label)}`;
264+
const valueLabel = `invalid value type for collection ${q(label)}`;
265+
266+
const serializeValue = value => {
267+
if (valueShape === undefined) {
268+
return serialize(value);
269+
}
270+
return serialize(mustCompress(value, valueShape, valueLabel));
271+
};
272+
273+
const unserializeValue = data => {
274+
if (valueShape === undefined) {
275+
return unserialize(data);
276+
}
277+
return decompress(unserialize(data), valueShape);
278+
};
279+
260280
function prefix(dbEntryKey) {
261281
return `${dbKeyPrefix}${dbEntryKey}`;
262282
}
@@ -331,11 +351,10 @@ export function makeCollectionManager(
331351
}
332352

333353
function get(key) {
334-
matches(key, keyShape) ||
335-
assert.fail(X`invalid key type for collection ${q(label)}`);
354+
fit(key, keyShape, keyLabel);
336355
const result = syscall.vatstoreGet(keyToDBKey(key));
337356
if (result) {
338-
return unserialize(JSON.parse(result));
357+
return unserializeValue(JSON.parse(result));
339358
}
340359
assert.fail(X`key ${key} not found in collection ${q(label)}`);
341360
}
@@ -351,16 +370,11 @@ export function makeCollectionManager(
351370
}
352371

353372
function init(key, value) {
354-
matches(key, keyShape) ||
355-
assert.fail(X`invalid key type for collection ${q(label)}`);
373+
fit(key, keyShape, keyLabel);
356374
!has(key) ||
357375
assert.fail(X`key ${key} already registered in collection ${q(label)}`);
358-
if (valueShape) {
359-
matches(value, valueShape) ||
360-
assert.fail(X`invalid value type for collection ${q(label)}`);
361-
}
376+
const serializedValue = serializeValue(value);
362377
currentGenerationNumber += 1;
363-
const serializedValue = serialize(value);
364378
assertAcceptableSyscallCapdataSize([serializedValue]);
365379
if (durable) {
366380
serializedValue.slots.forEach((vref, slotIndex) => {
@@ -388,13 +402,8 @@ export function makeCollectionManager(
388402
}
389403

390404
function set(key, value) {
391-
matches(key, keyShape) ||
392-
assert.fail(X`invalid key type for collection ${q(label)}`);
393-
if (valueShape) {
394-
matches(value, valueShape) ||
395-
assert.fail(X`invalid value type for collection ${q(label)}`);
396-
}
397-
const after = serialize(harden(value));
405+
fit(key, keyShape, keyLabel);
406+
const after = serializeValue(harden(value));
398407
assertAcceptableSyscallCapdataSize([after]);
399408
if (durable) {
400409
after.slots.forEach((vref, i) => {
@@ -412,8 +421,7 @@ export function makeCollectionManager(
412421
}
413422

414423
function deleteInternal(key) {
415-
matches(key, keyShape) ||
416-
assert.fail(X`invalid key type for collection ${q(label)}`);
424+
fit(key, keyShape, keyLabel);
417425
const dbKey = keyToDBKey(key);
418426
const rawValue = syscall.vatstoreGet(dbKey);
419427
assert(rawValue, X`key ${key} not found in collection ${q(label)}`);
@@ -472,7 +480,7 @@ export function makeCollectionManager(
472480
if (dbKey < end) {
473481
priorDBKey = dbKey;
474482
if (ignoreKeys) {
475-
const value = unserialize(JSON.parse(dbValue));
483+
const value = unserializeValue(JSON.parse(dbValue));
476484
if (matches(value, valuePatt)) {
477485
yield [undefined, value];
478486
}
@@ -484,7 +492,7 @@ export function makeCollectionManager(
484492
} else {
485493
const key = dbKeyToKey(dbKey);
486494
if (matches(key, keyPatt)) {
487-
const value = unserialize(JSON.parse(dbValue));
495+
const value = unserializeValue(JSON.parse(dbValue));
488496
if (matches(value, valuePatt)) {
489497
yield [key, value];
490498
}

0 commit comments

Comments
 (0)