Skip to content

Commit

Permalink
feat(cosmic-swingset): add board, use board in mailboxAdmin (#1167)
Browse files Browse the repository at this point in the history
* feat: add board, use board in mailboxAdmin
  • Loading branch information
katelynsills authored Jun 9, 2020
1 parent 9cb3050 commit 1381ba6
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 30 deletions.
4 changes: 3 additions & 1 deletion packages/cosmic-swingset/lib/ag-solo/vats/bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,14 @@ export default function setup(syscall, state, helpers) {
// Create singleton instances.
const sharingService = await E(vats.sharing).getSharingService();
const registry = await E(vats.registrar).getSharedRegistrar();
const board = await E(vats.board).getBoard();
const chainTimerService = await E(vats.timer).createTimerService(
timerDevice,
);

const zoe = await E(vats.zoe).getZoe();
const contractHost = await E(vats.host).makeHost();
const mailboxAdmin = await E(vats.mailbox).getMailboxAdmin();
const mailboxAdmin = await E(vats.mailbox).startup(board);

// Make the other demo mints
const issuerNames = ['moola', 'simolean'];
Expand Down Expand Up @@ -119,6 +120,7 @@ export default function setup(syscall, state, helpers) {
ibcport,
registrar: registry,
registry,
board,
zoe,
mailboxAdmin,
});
Expand Down
38 changes: 38 additions & 0 deletions packages/cosmic-swingset/lib/ag-solo/vats/lib-board.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import harden from '@agoric/harden';
import { generateSparseInts } from '@agoric/sparse-ints';
import { assert, details } from '@agoric/assert';
import makeStore from '@agoric/store';

function makeBoard(seed = 0) {
const idToVal = makeStore();
const valToId = makeStore();
const sparseInts = generateSparseInts(seed);

const board = harden({
// Add if not already present
getId: value => {
if (!valToId.has(value)) {
// Retry until we have a unique id.
let id;
do {
id = sparseInts.next().value.toString();
} while (idToVal.has(id));

valToId.init(value, id);
idToVal.init(id, value);
}
return valToId.get(value);
},
getValue: id => {
assert.equal(typeof id, 'string', details`id must be string ${id}`);
assert(idToVal.has(id), details`board does not have id: ${id}`);
return idToVal.get(id);
},
has: valToId.has,
ids: () => harden([...idToVal.keys()]),
});

return board;
}

export { makeBoard };
16 changes: 16 additions & 0 deletions packages/cosmic-swingset/lib/ag-solo/vats/vat-board.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import harden from '@agoric/harden';
import { makeBoard } from './lib-board';

function build(_E, _log) {
const board = makeBoard();
return harden({ getBoard: () => board });
}

export default function setup(syscall, state, helpers) {
return helpers.makeLiveSlots(
syscall,
state,
E => build(E, helpers.log),
helpers.vatID,
);
}
55 changes: 27 additions & 28 deletions packages/cosmic-swingset/lib/ag-solo/vats/vat-mailbox.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,39 @@
import harden from '@agoric/harden';
import { makeRegistrar } from '@agoric/registrar';
import { E } from '@agoric/eventual-send';
import { assert } from '@agoric/assert';

function build(_log) {
const mailboxRegistry = makeRegistrar();
let mailboxAdmin;

const mailboxAdmin = harden({
makeMailbox: (mailboxPrefix, purse, action = 'deposit') => {
assert.typeof(action, 'string');
const mailbox = harden({
receivePayment: payment => {
switch (action) {
case 'deposit': {
return E(purse).deposit(payment);
const startup = board => {
mailboxAdmin = harden({
makeMailbox: (purse, action = 'deposit') => {
assert.typeof(action, 'string');
const mailbox = harden({
receivePayment: payment => {
switch (action) {
case 'deposit': {
return E(purse).deposit(payment);
}
default: {
throw new Error(`action ${action} is not implemented`);
}
}
default: {
throw new Error(`action ${action} is not implemented`);
}
}
},
});
const key = mailboxRegistry.register(mailboxPrefix, mailbox);
return key;
},
sendPayment: (mailboxKey, payment) => {
const mailbox = mailboxRegistry.get(mailboxKey);
return mailbox.receivePayment(payment);
},
// We deliberately do not expose the entire registry so that the
// only way someone can send a payment using a mailbox is if they
// have obtained the key. This is intended only to cut down on
// spam. The key should not be assumed to be unforgeable or unguessable.
});
},
});
return E(board).getId(mailbox);
},
sendPayment: async (mailboxId, payment) => {
return E(board)
.getValue(mailboxId)
.then(mailbox => mailbox.receivePayment(payment));
},
});
return mailboxAdmin;
};

return harden({
startup,
getMailboxAdmin: () => mailboxAdmin,
});
}
Expand Down
71 changes: 70 additions & 1 deletion packages/cosmic-swingset/test/test-home.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ test('setup', async t => {
// Now come the tests that use `home`...
// =========================================

test('registry', async t => {
test('home.registry', async t => {
try {
const { registry } = E.G(home);
const regVal = await E(registry).get('foolobr_19191');
Expand All @@ -38,6 +38,75 @@ test('registry', async t => {
}
});

test('home.board', async t => {
try {
const { board } = E.G(home);
t.rejects(
() => E(board).getValue('0000000000'),
`getting a value for a fake id throws`,
);

const myValue = {};
const myId = await E(board).getId(myValue);
t.equals(typeof myId, 'string', `board key is string`);

const valueInBoard = await E(board).getValue(myId);
t.deepEquals(valueInBoard, myValue, `board contains myValue`);

const myId2 = await E(board).getId(myValue);
t.equals(myId2, myId, `board gives the same id for the same value`);
} catch (e) {
t.isNot(e, e, 'unexpected exception');
} finally {
t.end();
}
});

test('home.mailboxAdmin', async t => {
try {
const { mailboxAdmin, wallet } = E.G(home);
await E(wallet).makeEmptyPurse('moola', 'externalPurse');

const purses = await E(wallet).getPurses();
const purseMap = new Map(purses);
const sourcePurse = purseMap.get('Fun budget');
const externalPurse = purseMap.get('externalPurse');

const issuers = await E(wallet).getIssuers();
const issuersMap = new Map(issuers);
const moolaIssuer = issuersMap.get('moola');
const amountMath = await E(moolaIssuer).getAmountMath();

t.deepEquals(
await E(sourcePurse).getCurrentAmount(),
await E(amountMath).make(1900),
`balance starts at 1900`,
);
t.deepEquals(
await E(externalPurse).getCurrentAmount(),
await E(amountMath).make(0),
`balance for external purse starts at 0`,
);

const moola50 = await E(amountMath).make(50);
const payment = await E(sourcePurse).withdraw(moola50);
const mailboxId = await E(mailboxAdmin).makeMailbox(externalPurse);

// Someone else can send a payment given the mailboxId
const result = await E(mailboxAdmin).sendPayment(mailboxId, payment);
t.deepEquals(result, moola50, `result is 50 moola`);
t.deepEquals(
await E(externalPurse).getCurrentAmount(),
moola50,
`balance for external purse is 50 moola`,
);
} catch (e) {
t.isNot(e, e, 'unexpected exception');
} finally {
t.end();
}
});

// =========================================
// This runs after all the tests.
test('teardown', async t => {
Expand Down
32 changes: 32 additions & 0 deletions packages/cosmic-swingset/test/unitTests/test-lib-board.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { test } from 'tape-promise/tape';
import harden from '@agoric/harden';

import { makeBoard } from '../../lib/ag-solo/vats/lib-board';

test('makeBoard', async t => {
try {
const board = makeBoard();

const obj1 = harden({});
const obj2 = harden({});

t.deepEquals(board.ids(), [], `board is empty to start`);

const idObj1 = board.getId(obj1);
t.deepEquals(board.ids(), [idObj1], `board has one id`);

const idObj2 = board.getId(obj2);
t.deepEquals(board.ids().length, 2, `board has two ids`);

t.deepEquals(board.getValue(idObj1), obj1, `id matches value obj1`);
t.deepEquals(board.getValue(idObj2), obj2, `id matches value obj2`);

t.deepEquals(board.getId(obj1), idObj1, `value matches id obj1`);
t.deepEquals(board.getId(obj2), idObj2, `value matches id obj2`);
} catch (e) {
t.isNot(e, e, 'unexpected exception');
} finally {
t.end();
}
});

0 comments on commit 1381ba6

Please sign in to comment.