Skip to content

Commit

Permalink
Canonical marshal was easy! (#2223)
Browse files Browse the repository at this point in the history
* fix: canonical marshal was easy!
  • Loading branch information
erights authored Jan 20, 2021
1 parent 648b297 commit 0ef8ef1
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 53 deletions.
6 changes: 3 additions & 3 deletions packages/SwingSet/test/test-exomessages.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,14 @@ async function testFailure(t) {
failureHappened = true;
t.is(
e.message,
'kernel panic kp40.policy panic: rejection {"body":"{\\"@qclass\\":\\"error\\",\\"name\\":\\"Error\\",\\"message\\":\\"gratuitous error\\",\\"errorId\\":\\"error:liveSlots:v1#1\\"}","slots":[]}',
'kernel panic kp40.policy panic: rejection {"body":"{\\"@qclass\\":\\"error\\",\\"errorId\\":\\"error:liveSlots:v1#1\\",\\"message\\":\\"gratuitous error\\",\\"name\\":\\"Error\\"}","slots":[]}',
);
}
t.truthy(failureHappened);
t.is(controller.kpStatus(controller.bootstrapResult), 'rejected');
t.deepEqual(controller.kpResolution(controller.bootstrapResult), {
body:
'{"@qclass":"error","name":"Error","message":"gratuitous error","errorId":"error:liveSlots:v1#1"}',
'{"@qclass":"error","errorId":"error:liveSlots:v1#1","message":"gratuitous error","name":"Error"}',
slots: [],
});
}
Expand Down Expand Up @@ -123,7 +123,7 @@ test('extra message rejects', async t => {
t,
'reject',
'rejected',
'{"@qclass":"error","name":"Error","message":"gratuitous error","errorId":"error:liveSlots:v1#1"}',
'{"@qclass":"error","errorId":"error:liveSlots:v1#1","message":"gratuitous error","name":"Error"}',
[],
);
});
4 changes: 2 additions & 2 deletions packages/SwingSet/test/vat-admin/test-innerVat.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ test('VatAdmin get vat stats', async t => {
await c.run();
t.deepEqual(c.dump().log, [
'starting stats test',
'{"objectCount":0,"promiseCount":0,"deviceCount":0,"transcriptCount":0}',
'{"deviceCount":0,"objectCount":0,"promiseCount":0,"transcriptCount":0}',
'4',
'{"objectCount":0,"promiseCount":2,"deviceCount":0,"transcriptCount":2}',
'{"deviceCount":0,"objectCount":0,"promiseCount":2,"transcriptCount":2}',
]);
await c.run();
});
6 changes: 3 additions & 3 deletions packages/dapp-svelte-wallet/api/test/test-lib-dehydrate.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,13 +157,13 @@ test('makeDehydrator', async t => {
dehydrate(proposal),
{
body:
'{"want":{"Asset1":{"brand":{"@qclass":"slot","iface":"Alleged: mock brand","index":0},"value":60},"Asset2":{"brand":{"@qclass":"slot","iface":"Alleged: mock brand","index":1},"value":{"instanceHandle":{"@qclass":"slot","index":2}}}},"give":{"Price":{"brand":{"@qclass":"slot","iface":"Alleged: mock brand","index":3},"value":3}},"exit":{"afterDeadline":{"timer":{"@qclass":"slot","index":4},"deadline":55}}}',
'{"exit":{"afterDeadline":{"deadline":55,"timer":{"@qclass":"slot","index":0}}},"give":{"Price":{"brand":{"@qclass":"slot","iface":"Alleged: mock brand","index":1},"value":3}},"want":{"Asset1":{"brand":{"@qclass":"slot","iface":"Alleged: mock brand","index":2},"value":60},"Asset2":{"brand":{"@qclass":"slot","iface":"Alleged: mock brand","index":3},"value":{"instanceHandle":{"@qclass":"slot","index":4}}}}}',
slots: [
{ kind: 'unnamed', petname: 'unnamed-1' },
{ kind: 'brand', petname: 'simolean' },
{ kind: 'brand', petname: 'moola' },
{ kind: 'brand', petname: 'zoeInvite' },
{ kind: 'instanceHandle', petname: 'automaticRefund' },
{ kind: 'brand', petname: 'simolean' },
{ kind: 'unnamed', petname: 'unnamed-1' },
],
},
`dehydrated proposal`,
Expand Down
22 changes: 11 additions & 11 deletions packages/dapp-svelte-wallet/api/test/test-lib-wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ test('lib-wallet dapp suggests issuer, instance, installation petnames', async t
],
currentAmountSlots: {
body:
'{"brand":{"@qclass":"slot","iface":"Alleged: Zoe Invitation brand","index":0},"value":[{"description":"getRefund","handle":{"@qclass":"slot","index":1},"instance":{"@qclass":"slot","iface":"Alleged: InstanceHandle","index":2},"installation":{"@qclass":"slot","iface":"Alleged: Installation","index":3}}]}',
'{"brand":{"@qclass":"slot","iface":"Alleged: Zoe Invitation brand","index":0},"value":[{"description":"getRefund","handle":{"@qclass":"slot","index":1},"installation":{"@qclass":"slot","iface":"Alleged: Installation","index":2},"instance":{"@qclass":"slot","iface":"Alleged: InstanceHandle","index":3}}]}',
slots: [
{ kind: 'brand', petname: 'zoe invite' },
{ kind: 'unnamed', petname: 'unnamed-4' },
Expand All @@ -344,8 +344,8 @@ test('lib-wallet dapp suggests issuer, instance, installation petnames', async t
{
description: 'getRefund',
handle: { kind: 'unnamed', petname: 'unnamed-4' },
instance: { kind: 'unnamed', petname: 'unnamed-2' },
installation: { kind: 'unnamed', petname: 'unnamed-3' },
installation: { kind: 'unnamed', petname: 'unnamed-2' },
instance: { kind: 'unnamed', petname: 'unnamed-3' },
},
],
},
Expand Down Expand Up @@ -412,12 +412,12 @@ test('lib-wallet dapp suggests issuer, instance, installation petnames', async t
],
currentAmountSlots: {
body:
'{"brand":{"@qclass":"slot","iface":"Alleged: Zoe Invitation brand","index":0},"value":[{"description":"getRefund","handle":{"@qclass":"slot","index":1},"instance":{"@qclass":"slot","iface":"Alleged: InstanceHandle","index":2},"installation":{"@qclass":"slot","iface":"Alleged: Installation","index":3}}]}',
'{"brand":{"@qclass":"slot","iface":"Alleged: Zoe Invitation brand","index":0},"value":[{"description":"getRefund","handle":{"@qclass":"slot","index":1},"installation":{"@qclass":"slot","iface":"Alleged: Installation","index":2},"instance":{"@qclass":"slot","iface":"Alleged: InstanceHandle","index":3}}]}',
slots: [
{ kind: 'brand', petname: 'zoe invite' },
{ kind: 'unnamed', petname: 'unnamed-4' },
{ kind: 'instance', petname: 'automaticRefund' },
{ kind: 'installation', petname: 'automaticRefund' },
{ kind: 'instance', petname: 'automaticRefund' },
],
},
currentAmount: {
Expand Down Expand Up @@ -517,12 +517,12 @@ test('lib-wallet dapp suggests issuer, instance, installation petnames', async t
],
currentAmountSlots: {
body:
'{"brand":{"@qclass":"slot","iface":"Alleged: Zoe Invitation brand","index":0},"value":[{"description":"getRefund","handle":{"@qclass":"slot","index":1},"instance":{"@qclass":"slot","iface":"Alleged: InstanceHandle","index":2},"installation":{"@qclass":"slot","iface":"Alleged: Installation","index":3}}]}',
'{"brand":{"@qclass":"slot","iface":"Alleged: Zoe Invitation brand","index":0},"value":[{"description":"getRefund","handle":{"@qclass":"slot","index":1},"installation":{"@qclass":"slot","iface":"Alleged: Installation","index":2},"instance":{"@qclass":"slot","iface":"Alleged: InstanceHandle","index":3}}]}',
slots: [
{ kind: 'brand', petname: 'zoe invite' },
{ kind: 'unnamed', petname: 'unnamed-4' },
{ kind: 'instance', petname: 'automaticRefund' },
{ kind: 'installation', petname: 'automaticRefund2' },
{ kind: 'instance', petname: 'automaticRefund' },
],
},
currentAmount: {
Expand Down Expand Up @@ -728,8 +728,8 @@ test('lib-wallet offer methods', async t => {
},
requestContext: { dappOrigin: 'unknown' },
status: 'accept',
instancePetname: 'unnamed-2',
installationPetname: 'unnamed-3',
instancePetname: 'unnamed-3',
installationPetname: 'unnamed-2',
proposalForDisplay: {
give: {
Contribution: {
Expand Down Expand Up @@ -760,8 +760,8 @@ test('lib-wallet offer methods', async t => {
},
requestContext: { dappOrigin: 'unknown' },
status: 'decline',
instancePetname: 'unnamed-2',
installationPetname: 'unnamed-3',
instancePetname: 'unnamed-3',
installationPetname: 'unnamed-2',
proposalForDisplay: {
give: {
Contribution: {
Expand Down
64 changes: 44 additions & 20 deletions packages/marshal/src/marshal.js
Original file line number Diff line number Diff line change
Expand Up @@ -542,10 +542,36 @@ export function makeMarshal(
});
}

function makeReplacer(slots, slotMap) {
/**
* @template Slot
* @type {Serialize<Slot>}
*/
const serialize = root => {
const slots = [];
// maps val (promise or remotable) to index of slots[]
const slotMap = new Map();
const ibidTable = makeReplacerIbidTable();

return function replacer(_, val) {
/**
* Just consists of data that rounds trips to plain data.
*
* @typedef {any} PlainJSONData
*/

/**
* Must encode `val` into plain JSON data *canonically*, such that
* `sameStructure(v1, v2)` implies
* `JSON.stringify(encode(v1)) === JSON.stringify(encode(v2))`
* For each record, we only accept sortable property names
* (no anonymous symbols) and on the encoded form. The sort
* order of these names must be the same as their enumeration
* order, so a `JSON.stringify` of the encoded form agrees with
* a canonical-json stringify of the encoded form.
*
* @param {Passable} val
* @returns {PlainJSONData}
*/
const encode = val => {
// First we handle all primitives. Some can be represented directly as
// JSON, and some must be encoded as [QCLASS] composites.
const passStyle = passStyleOf(val);
Expand Down Expand Up @@ -605,12 +631,17 @@ export function makeMarshal(
ibidTable.add(val);

switch (passStyle) {
case 'copyRecord':
case 'copyRecord': {
// Currently copyRecord allows only string keys so this will
// work. If we allow sortable symbol keys, this will need to
// become more interesting.
const names = Reflect.ownKeys(val).sort();
return Object.fromEntries(
names.map(name => [name, encode(val[name])]),
);
}
case 'copyArray': {
// console.log(`canPassByCopy: ${val}`);
// Purposely in-band for readability, but creates need for
// Hilbert hotel.
return val;
return val.map(encode);
}
case 'copyError': {
// We deliberately do not share the stack, but it would
Expand All @@ -631,9 +662,9 @@ export function makeMarshal(
console.log('Temporary logging of sent error', val);
return harden({
[QCLASS]: 'error',
name: `${val.name}`,
message: `${val.message}`,
errorId,
message: `${val.message}`,
name: `${val.name}`,
});
}
case REMOTE_STYLE: {
Expand All @@ -652,21 +683,14 @@ export function makeMarshal(
}
}
};
}

/**
* @template Slot
* @type {Serialize<Slot>}
*/
function serialize(val) {
const slots = [];
const slotMap = new Map(); // maps val (promise or remotable) to
// index of slots[]
const encoded = encode(root);

return harden({
body: JSON.stringify(val, makeReplacer(slots, slotMap)),
body: JSON.stringify(encoded),
slots,
});
}
};

function makeFullRevive(slots, cyclePolicy) {
// ibid table is shared across recursive calls to fullRevive.
Expand Down
10 changes: 5 additions & 5 deletions packages/marshal/test/test-marshal.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ test('serialize static data', t => {
}
t.deepEqual(ser(emptyem), {
body:
'{"@qclass":"error","name":"Error","message":"","errorId":"error:anon-marshal#1"}',
'{"@qclass":"error","errorId":"error:anon-marshal#1","message":"","name":"Error"}',
slots: [],
});

Expand All @@ -84,7 +84,7 @@ test('serialize static data', t => {
}
t.deepEqual(ser(em), {
body:
'{"@qclass":"error","name":"ReferenceError","message":"msg","errorId":"error:anon-marshal#2"}',
'{"@qclass":"error","errorId":"error:anon-marshal#2","message":"msg","name":"ReferenceError"}',
slots: [],
});

Expand Down Expand Up @@ -123,17 +123,17 @@ test('unserialize static data', t => {
}

const em1 = uns(
'{"@qclass":"error","name":"ReferenceError","message":"msg"}',
'{"@qclass":"error","message":"msg","name":"ReferenceError"}',
);
t.truthy(em1 instanceof ReferenceError);
t.is(em1.message, 'msg');
t.truthy(Object.isFrozen(em1));

const em2 = uns('{"@qclass":"error","name":"TypeError","message":"msg2"}');
const em2 = uns('{"@qclass":"error","message":"msg2","name":"TypeError"}');
t.truthy(em2 instanceof TypeError);
t.is(em2.message, 'msg2');

const em3 = uns('{"@qclass":"error","name":"Unknown","message":"msg3"}');
const em3 = uns('{"@qclass":"error","message":"msg3","name":"Unknown"}');
t.truthy(em3 instanceof Error);
t.is(em3.message, 'msg3');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ test.skip('run contractHost Demo --mint', async t => {
const contractTrivialGolden = [
'starting trivialContractTest',
'Does source match? true',
'foo balance {"brand":{},"value":[{"installation":{},"terms":"foo terms","seatIdentity":{},"seatDesc":"foo"}]}',
'foo balance {"brand":{},"value":[{"installation":{},"seatDesc":"foo","seatIdentity":{},"terms":"foo terms"}]}',
'++ eightP resolved to 8 (should be 8)',
'++ DONE',
];
Expand Down
16 changes: 8 additions & 8 deletions packages/zoe/test/swingsetTests/zoe/test-zoe.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,21 +212,21 @@ test.serial('zoe - simpleExchange - valid inputs', async t => {
const expectedSimpleExchangeNotificationLog = [
'=> alice, bob, carol and dave are set up',
'{"buys":[],"sells":[]}',
'{"buys":[],"sells":[{"want":{"Price":{"brand":{},"value":4}},"give":{"Asset":{"brand":{},"value":3}}}]}',
'{"buys":[],"sells":[{"give":{"Asset":{"brand":{},"value":3}},"want":{"Price":{"brand":{},"value":4}}}]}',
'Order Added',
'{"buys":[],"sells":[]}',
'Order Added',
'bobMoolaPurse: balance {"brand":{},"value":0}',
'bobSimoleanPurse: balance {"brand":{},"value":20}',
'{"buys":[{"want":{"Asset":{"brand":{},"value":8}},"give":{"Price":{"brand":{},"value":2}}}],"sells":[]}',
'{"buys":[{"give":{"Price":{"brand":{},"value":2}},"want":{"Asset":{"brand":{},"value":8}}}],"sells":[]}',
'Order Added',
'bobMoolaPurse: balance {"brand":{},"value":3}',
'bobSimoleanPurse: balance {"brand":{},"value":18}',
'{"buys":[{"want":{"Asset":{"brand":{},"value":8}},"give":{"Price":{"brand":{},"value":2}}},{"want":{"Asset":{"brand":{},"value":20}},"give":{"Price":{"brand":{},"value":13}}}],"sells":[]}',
'{"buys":[{"give":{"Price":{"brand":{},"value":2}},"want":{"Asset":{"brand":{},"value":8}}},{"give":{"Price":{"brand":{},"value":13}},"want":{"Asset":{"brand":{},"value":20}}}],"sells":[]}',
'Order Added',
'bobMoolaPurse: balance {"brand":{},"value":3}',
'bobSimoleanPurse: balance {"brand":{},"value":5}',
'{"buys":[{"want":{"Asset":{"brand":{},"value":8}},"give":{"Price":{"brand":{},"value":2}}},{"want":{"Asset":{"brand":{},"value":20}},"give":{"Price":{"brand":{},"value":13}}},{"want":{"Asset":{"brand":{},"value":5}},"give":{"Price":{"brand":{},"value":2}}}],"sells":[]}',
'{"buys":[{"give":{"Price":{"brand":{},"value":2}},"want":{"Asset":{"brand":{},"value":8}}},{"give":{"Price":{"brand":{},"value":13}},"want":{"Asset":{"brand":{},"value":20}}},{"give":{"Price":{"brand":{},"value":2}},"want":{"Asset":{"brand":{},"value":5}}}],"sells":[]}',
'Order Added',
'bobMoolaPurse: balance {"brand":{},"value":3}',
'bobSimoleanPurse: balance {"brand":{},"value":3}',
Expand Down Expand Up @@ -254,7 +254,7 @@ const expectedAutoswapOkLog = [
'bobSimoleanPurse: balance {"brand":{},"value":5}',
'simoleans required {"brand":{},"value":5}',
'Liquidity successfully removed.',
'poolAmounts{"Liquidity":{"brand":{},"value":10},"Central":{"brand":{},"value":0},"Secondary":{"brand":{},"value":0}}',
'poolAmounts{"Central":{"brand":{},"value":0},"Liquidity":{"brand":{},"value":10},"Secondary":{"brand":{},"value":0}}',
'aliceMoolaPurse: balance {"brand":{},"value":8}',
'aliceSimoleanPurse: balance {"brand":{},"value":7}',
'aliceLiquidityTokenPurse: balance {"brand":{},"value":0}',
Expand All @@ -270,9 +270,9 @@ test.serial('zoe - autoswap - valid inputs', async t => {

const expectedSellTicketsOkLog = [
'=> alice, bob, carol and dave are set up',
'availableTickets: {"brand":{},"value":[{"show":"Steven Universe, the Opera","start":"Wed, March 25th 2020 at 8pm","number":1},{"show":"Steven Universe, the Opera","start":"Wed, March 25th 2020 at 8pm","number":2},{"show":"Steven Universe, the Opera","start":"Wed, March 25th 2020 at 8pm","number":3}]}',
'boughtTicketAmount: {"brand":{},"value":[{"show":"Steven Universe, the Opera","start":"Wed, March 25th 2020 at 8pm","number":1}]}',
'after ticket1 purchased: {"brand":{},"value":[{"show":"Steven Universe, the Opera","start":"Wed, March 25th 2020 at 8pm","number":2},{"show":"Steven Universe, the Opera","start":"Wed, March 25th 2020 at 8pm","number":3}]}',
'availableTickets: {"brand":{},"value":[{"number":1,"show":"Steven Universe, the Opera","start":"Wed, March 25th 2020 at 8pm"},{"number":2,"show":"Steven Universe, the Opera","start":"Wed, March 25th 2020 at 8pm"},{"number":3,"show":"Steven Universe, the Opera","start":"Wed, March 25th 2020 at 8pm"}]}',
'boughtTicketAmount: {"brand":{},"value":[{"number":1,"show":"Steven Universe, the Opera","start":"Wed, March 25th 2020 at 8pm"}]}',
'after ticket1 purchased: {"brand":{},"value":[{"number":2,"show":"Steven Universe, the Opera","start":"Wed, March 25th 2020 at 8pm"},{"number":3,"show":"Steven Universe, the Opera","start":"Wed, March 25th 2020 at 8pm"}]}',
'alice earned: {"brand":{},"value":22}',
];
test.serial('zoe - sellTickets - valid inputs', async t => {
Expand Down

0 comments on commit 0ef8ef1

Please sign in to comment.