Skip to content

Commit

Permalink
refactor: more legibility
Browse files Browse the repository at this point in the history
make ballotCounter instance accessible from ballot
add more to terms of ballotCounter (closureRule)
add more to public facet of Registrar
one registrar per contract/vat: details in terms
use zcf.getInstance() to supply instances

In tests
  voters look up their own positions
  voters cast ballots on notification
  • Loading branch information
Chris-Hibbert committed Jun 20, 2021
1 parent 832b398 commit 259aa6d
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 127 deletions.
13 changes: 11 additions & 2 deletions packages/governance/src/ballotBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const buildEqualWeightBallot = (
question,
positions,
maxChoices = 0,
instance,
) => {
const choose = chosenPositions => {
assert(
Expand All @@ -46,21 +47,29 @@ const buildEqualWeightBallot = (
question,
positions,
maxChoices,
instance,
});

return {
getBallotCounter: () => instance,
getDetails,
choose,
};
};

/** @type {BuildBallot} */
const buildBallot = (method, question, positions, maxChoices = 0) => {
const buildBallot = (method, question, positions, maxChoices = 0, instance) => {
assert.typeof(question, 'string');

switch (method) {
case ChoiceMethod.CHOOSE_N:
return buildEqualWeightBallot(method, question, positions, maxChoices);
return buildEqualWeightBallot(
method,
question,
positions,
maxChoices,
instance,
);
case ChoiceMethod.ORDER:
case ChoiceMethod.WEIGHT:
throw Error(`choice method ${ChoiceMethod.WEIGHT} is unimplemented`);
Expand Down
20 changes: 16 additions & 4 deletions packages/governance/src/binaryBallotCounter.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const makeBinaryBallotCounter = (
threshold,
tieOutcome = undefined,
closureRule,
instance,
) => {
assert(
positions.length === 2,
Expand All @@ -44,7 +45,13 @@ const makeBinaryBallotCounter = (
);
}

const template = buildBallot(ChoiceMethod.CHOOSE_N, question, positions, 1);
const template = buildBallot(
ChoiceMethod.CHOOSE_N,
question,
positions,
1,
instance,
);
const ballotDetails = template.getDetails();

assert(
Expand Down Expand Up @@ -144,8 +151,7 @@ const makeBinaryBallotCounter = (
getVoterFacet: () => voterFacet,
});

/** @type {BallotCounterPublicFacet} */
const publicFacet = Far('publicFacet', {
const publicFacet = Far('preliminaryPublicFacet', {
...sharedFacet,
getOutcome: () => outcomePromise.promise,
getStats: () => tallyPromise.promise,
Expand Down Expand Up @@ -174,11 +180,17 @@ const start = zcf => {
quorumThreshold,
tieOutcome,
closureRule,
zcf.getInstance(),
);

scheduleClose(closureRule, closeFacet.closeVoting);

return { publicFacet, creatorFacet };
/** @type {BallotCounterPublicFacet} */
const publicFacetWithGetInstance = Far('publicFacet', {
...publicFacet,
getInstance: zcf.getInstance,
});
return { publicFacet: publicFacetWithGetInstance, creatorFacet };
};

harden(start);
Expand Down
85 changes: 48 additions & 37 deletions packages/governance/src/committeeRegistrar.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,62 +5,73 @@ import { makeNotifierKit } from '@agoric/notifier';
import { E } from '@agoric/eventual-send';
import { makeStore } from '@agoric/store';

// Each CommitteeRegistrar represents a particular set of voters. The number of
// voters is visible in the terms.
const start = zcf => {
const allQuestions = makeStore('Question');
const { notifier, updater } = makeNotifierKit();
const invitations = [];

const getOpenQuestions = () =>
allQuestions.keys().filter(key => {
const { pubicFacet } = allQuestions.get(key);
return E(pubicFacet).isOpen();
});

const makeCommitteeVoterInvitation = index => {
const handler = voterSeat => {
return Far(`voter${index}`, {
castBallot: ballot =>
E(allQuestions.get(ballot.question)).submitVote(voterSeat, ballot),
castBallot: ballot => {
const { creatorFacet } = allQuestions.get(ballot.question);
const voterFacet = E(creatorFacet).getVoterFacet();
return E(voterFacet).submitVote(voterSeat, ballot);
},
});
};

return zcf.makeInvitation(handler, `Voter${index}`);
};

const createRegistrar = (name, count) => {
const { notifier, updater } = makeNotifierKit();

const invitations = [];
for (let i = 0; i < count; i += 1) {
invitations[i] = makeCommitteeVoterInvitation(i);
}

/**
* @param {Installation} voteCounter
* @param {BallotDetails} questionDetails
*/
const addQuestion = async (voteCounter, questionDetails) => {
const { creatorFacet, publicFacet, instance } = await E(
zcf.getZoeService(),
).startInstance(voteCounter, {}, questionDetails);
const { committeeName, committeeSize } = zcf.getTerms();
for (let i = 0; i < committeeSize; i += 1) {
invitations[i] = makeCommitteeVoterInvitation(i);
}

const { clock, deadline } = questionDetails;
const wake = async _timestamp => {
E(creatorFacet).closeVoting();
};
E(clock).setWakeup(deadline, Far('waker', { wake }));

updater.updateState(questionDetails.question);
allQuestions.init(
questionDetails.question,
E(creatorFacet).getVoterFacet(),
);
return { creatorFacet, publicFacet, instance };
/**
* @param {Installation} voteCounter
* @param {BallotDetailsShort} questionDetailsShort
*/
const addQuestion = async (voteCounter, questionDetailsShort) => {
const questionDetails = {
...questionDetailsShort,
registrar: zcf.getInstance(),
};
const { creatorFacet, publicFacet, instance } = await E(
zcf.getZoeService(),
).startInstance(voteCounter, {}, questionDetails);
const facets = { creatorFacet, publicFacet, instance };

return Far(`CommitteeRegistrar: ${name}`, {
addQuestion,
getVoterInvitations: () => invitations,
getQuestionNotifier: () => notifier,
});
updater.updateState(questionDetails.question);
allQuestions.init(questionDetails.question, facets);
return facets;
};

const creatorFacet = Far('adminFacet', {
createRegistrar,
addQuestion,
getVoterInvitations: () => invitations,
getQuestionNotifier: () => notifier,
});

const publicFacet = Far('publicFacet', {
getQuestionNotifier: () => notifier,
getOpenQuestions,
getName: () => committeeName,
getInstance: zcf.getInstance,
getDetails: name =>
E(E(allQuestions.get(name).publicFacet).getBallotTemplate()).getDetails(),
getBallot: name =>
E(allQuestions.get(name).publicFacet).getBallotTemplate(),
});
const publicFacet = Far('publicFacet', {});

return { publicFacet, creatorFacet };
};
Expand Down
14 changes: 14 additions & 0 deletions packages/governance/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,25 @@
* @returns {ParamManagerFull}
*/

/**
* @typedef {Object} BallotDetailsShort
* BallotDetails as provided to the Registrar
* @property {ChoiceMethod} method
* @property {string} question
* @property {string[]} positions
* @property {bigint} maxChoices
* @property {ClosureRule} closureRule
*/

/**
* @typedef {Object} BallotDetails
* BallotDetails after the Registrar adds its Instance
* @property {ChoiceMethod} method
* @property {string} question
* @property {string[]} positions
* @property {bigint} maxChoices
* @property {ClosureRule} closureRule
* @property {Instance} registrar
*/

/**
Expand Down Expand Up @@ -75,6 +88,7 @@
* @param {string} question
* @param {string[]} positions
* @param {number} maxChoices
* @param {Instance} instance - ballotCounter instance
* @returns {Ballot}
*/

Expand Down
93 changes: 28 additions & 65 deletions packages/governance/test/swingsetTests/committeeBinary/bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,86 +4,62 @@ import { E } from '@agoric/eventual-send';
import { Far } from '@agoric/marshal';
import buildManualTimer from '@agoric/zoe/tools/manualTimer';

const makeVats = async (log, vats, zoe, installations) => {
// Setup Voters
const makeVoterVat = async (log, vats, zoe) => {
const voterCreator = E(vats.voter).build(zoe);

const { creatorFacet, instance: registrarInstance } = await E(
zoe,
).startInstance(installations.committeeRegistrar);

const result = { voterCreator, creatorFacet, registrarInstance };

log(`=> voter and registrar vats are set up`);
return harden(result);
};

const getVoter = (zoe, voterCreator, invitation, name) => {
return E(zoe)
.offer(invitation)
.then(seat => {
const facet = E(seat).getOfferResult();
return E(voterCreator).createVoter(name, facet);
});
log(`=> voter vat is set up`);
return voterCreator;
};

async function committeeBinaryStart(
zoe,
committeeCreatorFacet,
voterCreator,
timer,
log,
installations,
registrarInstance,
) {
const registrar = E(committeeCreatorFacet).createRegistrar('aCommittee', 5);
const registrarTerms = { committeeName: 'TheCommittee', committeeSize: 5 };
const { creatorFacet: registrarFacet, instance: registrarInstance } = await E(
zoe,
).startInstance(installations.committeeRegistrar, {}, registrarTerms);

const details = {
clock: timer,
deadline: 3n,
const ballotDetails = {
question: 'Choose',
positions: ['Eeny', 'Meeny'],
quorumThreshold: 3n,
tieOutcome: undefined,
closureRule: {
timer,
deadline: 3n,
},
};
const { instance } = await E(registrar).addQuestion(
const { instance: ballotInstance } = await E(registrarFacet).addQuestion(
installations.binaryBallotCounter,
details,
ballotDetails,
);

const invitations = await E(registrar).getVoterInvitations();

const invitations = await E(registrarFacet).getVoterInvitations();
const details2 = await E(zoe).getInvitationDetails(invitations[2]);

log(
`invitaiton details check: ${details2.instance === registrarInstance} ${
`invitation details check: ${details2.instance === registrarInstance} ${
details2.description
}}`,
}`,
);

const aliceP = getVoter(zoe, voterCreator, invitations[0], 'Alice');
const bobP = getVoter(zoe, voterCreator, invitations[1], 'Bob');
const carolP = getVoter(zoe, voterCreator, invitations[2], 'Carol');
const daveP = getVoter(zoe, voterCreator, invitations[3], 'Dave');
const emmaP = getVoter(zoe, voterCreator, invitations[4], 'Emma');

const publicFacet = E(zoe).getPublicFacet(instance);
const ballotTemplate = E(publicFacet).getBallotTemplate();
const [ballot0, ballot1] = await Promise.all([
E(ballotTemplate).choose([details.positions[0]]),
E(ballotTemplate).choose([details.positions[1]]),
]);
const aliceP = E(voterCreator).createVoter('Alice', invitations[0], 'Eeny');
E(voterCreator).createVoter('Bob', invitations[1], 'Meeny');
E(voterCreator).createVoter('Carol', invitations[2], 'Eeny');
E(voterCreator).createVoter('Dave', invitations[3], 'Eeny');
await E(voterCreator).createVoter('Emma', invitations[4], 'Meeny');

E(aliceP).voteBallot('Choose', ballot0);
E(bobP).voteBallot('Choose', ballot1);
E(carolP).voteBallot('Choose', ballot0);
E(daveP).voteBallot('Choose', ballot0);
await E(emmaP).voteBallot('Choose', ballot1);
await E(aliceP).verifyBallot(instance);
// At least one voter should verify that everything is on the up-and-up
await E(aliceP).verifyBallot('Choose');

E(timer).tick();
E(timer).tick();
await E(timer).tick();

const publicFacet = E(zoe).getPublicFacet(ballotInstance);
await E(publicFacet)
.getOutcome()
.then(outcome => log(`vote outcome: ${outcome}`))
Expand All @@ -105,25 +81,12 @@ const makeBootstrap = (argv, cb, vatPowers) => async (vats, devices) => {

const installations = { committeeRegistrar, binaryBallotCounter };

const { voterCreator, creatorFacet, registrarInstance } = await makeVats(
log,
vats,
zoe,
installations,
);
const voterCreator = await makeVoterVat(log, vats, zoe);

const [testName] = argv;
switch (testName) {
case 'committeeBinaryStart':
committeeBinaryStart(
zoe,
creatorFacet,
voterCreator,
timer,
log,
installations,
registrarInstance,
);
committeeBinaryStart(zoe, voterCreator, timer, log, installations);
break;
default:
log(`didn't find test: ${argv}`);
Expand Down
Loading

0 comments on commit 259aa6d

Please sign in to comment.