Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Registrar 3188 #3299

Merged
merged 7 commits into from
Jun 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion packages/governance/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,20 @@
"@agoric/eventual-send": "^0.13.17",
"@agoric/marshal": "^0.4.14",
"@agoric/nat": "^4.1.0",
"@agoric/notifier": "^0.3.16",
"@agoric/promise-kit": "^0.2.16",
"@agoric/same-structure": "^0.1.15",
"@agoric/store": "^0.4.17",
"@agoric/zoe": "^0.17.0"
},
"devDependencies": {
"@agoric/install-ses": "^0.5.16",
"@agoric/babel-standalone": "^7.14.3",
"@agoric/bundle-source": "^1.3.9",
"@agoric/swingset-vat": "^0.18.0",
"ava": "^3.12.1",
"esm": "agoric-labs/esm#Agoric-built"
"esm": "agoric-labs/esm#Agoric-built",
"ses": "^0.13.4"
},
"files": [
"src/",
Expand Down
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
47 changes: 31 additions & 16 deletions packages/governance/src/binaryBallotCounter.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { makeStore } from '@agoric/store';
import { makePromiseKit } from '@agoric/promise-kit';
import { Far } from '@agoric/marshal';

import { E } from '@agoric/eventual-send';
import { ChoiceMethod, buildBallot } from './ballotBuilder';
import { scheduleClose } from './closingRule';

Expand All @@ -29,6 +30,7 @@ const makeBinaryBallotCounter = (
threshold,
tieOutcome = undefined,
closingRule,
instance,
) => {
assert(
positions.length === 2,
Expand All @@ -44,7 +46,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 All @@ -56,18 +64,20 @@ const makeBinaryBallotCounter = (
const tallyPromise = makePromiseKit();
const allBallots = makeStore('seat');

const recordBallot = (seat, filledBallot, shares = 1n) => {
assert(
filledBallot.question === question,
X`Ballot not for this question ${filledBallot.question} should have been ${question}`,
);
assert(
positions.includes(filledBallot.chosen[0]),
X`The ballot's choice is not a legal position: ${filledBallot.chosen[0]}.`,
);
allBallots.has(seat)
? allBallots.set(seat, makeWeightedBallot(filledBallot, shares))
: allBallots.init(seat, makeWeightedBallot(filledBallot, shares));
const recordBallot = (seat, filledBallotP, shares = 1n) => {
return E.when(filledBallotP, filledBallot => {
assert(
filledBallot.question === question,
X`Ballot not for this question ${filledBallot.question} should have been ${question}`,
);
assert(
positions.includes(filledBallot.chosen[0]),
X`The ballot's choice is not a legal position: ${filledBallot.chosen[0]}.`,
);
allBallots.has(seat)
? allBallots.set(seat, makeWeightedBallot(filledBallot, shares))
: allBallots.init(seat, makeWeightedBallot(filledBallot, shares));
});
};

const countVotes = () => {
Expand Down Expand Up @@ -144,8 +154,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 +183,17 @@ const start = zcf => {
quorumThreshold,
tieOutcome,
closingRule,
zcf.getInstance(),
);

scheduleClose(closingRule, closeFacet.closeVoting);

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

harden(start);
Expand Down
92 changes: 92 additions & 0 deletions packages/governance/src/committeeRegistrar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// @ts-check

import { Far } from '@agoric/marshal';
import { makeNotifierKit } from '@agoric/notifier';
import { E } from '@agoric/eventual-send';
import { makeStore } from '@agoric/store';
import { allComparable } from '@agoric/same-structure';

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

const getOpenQuestions = async () => {
const isOpenPQuestions = allQuestions.keys().map(key => {
const { publicFacet } = allQuestions.get(key);
return [E(publicFacet).isOpen(), key];
});
const isOpenQuestions = await allComparable(harden(isOpenPQuestions));
return isOpenQuestions
.filter(([open, _key]) => open)
.map(([_open, key]) => key);
};

const makeCommitteeVoterInvitation = index => {
const handler = voterSeat => {
return Far(`voter${index}`, {
castBallot: ballotp => {
E.when(ballotp, ballot => {
const { voter } = allQuestions.get(ballot.question);
return E(voter).submitVote(voterSeat, ballot);
});
},
castBallotFor: (question, positions) => {
const { publicFacet: counter, voter } = allQuestions.get(question);
const ballotTemplate = E(counter).getBallotTemplate();
const ballot = E(ballotTemplate).choose(positions);
return E(voter).submitVote(voterSeat, ballot);
},
});
};

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

const { committeeName, committeeSize } = zcf.getTerms();
for (let i = 0; i < committeeSize; i += 1) {
invitations[i] = makeCommitteeVoterInvitation(i);
}

/** @type {AddQuestion} */
const addQuestion = async (voteCounter, questionDetailsShort) => {
const questionDetails = {
...questionDetailsShort,
registrar: zcf.getInstance(),
};
// facets of the ballot counter. Suppress creatorInvitation and adminFacet.
const { creatorFacet, publicFacet, instance } = await E(
zcf.getZoeService(),
).startInstance(voteCounter, {}, questionDetails);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to take it or leave it:

Suggested change
).startInstance(voteCounter, {}, questionDetails);
).startInstance(voteCounter, undefined, questionDetails);

{} should work now that we don't treat empty objects as presences, but I think undefined is clearer

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer the curly braces. It's easier for me to recognize those as empty terms.

const facets = { voter: E(creatorFacet).getVoterFacet(), publicFacet };

updater.updateState(questionDetails.question);
allQuestions.init(questionDetails.question, facets);
return { creatorFacet, publicFacet, instance };
};

const creatorFacet = Far('adminFacet', {
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(),
});

return { publicFacet, creatorFacet };
};

harden(start);
export { start };
38 changes: 37 additions & 1 deletion packages/governance/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,29 @@
* @returns {ParamManagerFull}
*/

/**
* @typedef {Object} QuestionTermsShort
* BallotDetails as provided to the Registrar
* @property {ChoiceMethod} method
* @property {string} question
* @property {string[]} positions
* @property {number} maxChoices
* @property {ClosingRule} closingRule
*/

/**
* @typedef {Object} QuestionTerms
* BallotDetails after the Registrar adds its Instance
* @property {ChoiceMethod} method
* @property {string} question
* @property {string[]} positions
* @property {number} maxChoices
* @property {ClosingRule} closingRule
* @property {Instance} registrar
*/
/**
* @typedef {Object} BallotDetails
* BallotDetails after the Registrar adds its Instance
* @property {ChoiceMethod} method
* @property {string} question
* @property {string[]} positions
Expand Down Expand Up @@ -75,6 +96,7 @@
* @param {string} question
* @param {string[]} positions
* @param {number} maxChoices
* @param {Instance} instance - ballotCounter instance
* @returns {Ballot}
*/

Expand Down Expand Up @@ -126,7 +148,7 @@
/**
* @callback SubmitVote
* @param {Handle<'Voter'>} seat
* @param {CompletedBallot} filledBallot
* @param {ERef<CompletedBallot>} filledBallot
* @param {bigint=} weight
*/

Expand All @@ -146,3 +168,17 @@
* @param {ClosingRule} closingRule
* @param {() => void} closeVoting
*/

/**
* @typedef {Object} AddQuestionReturn
* @property {BallotCounterPublicFacet} publicFacet
* @property {BallotCounterCreatorFacet} creatorFacet
* @property {Instance} instance
*/

/**
* @callback AddQuestion
* @param {Installation} voteCounter
* @param {QuestionTermsShort} questionDetailsShort
* @returns {Promise<AddQuestionReturn>}
*/
Loading