Skip to content

Commit

Permalink
refactor: turn committee into a heapFarClass
Browse files Browse the repository at this point in the history
Add incomplete definition of QuestionDetailsShape
  • Loading branch information
Chris-Hibbert committed Sep 3, 2022
1 parent 9c60883 commit ea40db9
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 68 deletions.
136 changes: 85 additions & 51 deletions packages/governance/src/committee.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @ts-check

import { makeStoredPublishKit } from '@agoric/notifier';
import { makeStore } from '@agoric/store';
import { defineHeapFarClassKit, initEmpty, makeStore } from '@agoric/store';
import { natSafeMath } from '@agoric/zoe/src/contractSupport/index.js';
import { E } from '@endo/eventual-send';
import { Far } from '@endo/marshal';
Expand All @@ -14,9 +14,7 @@ import {
startCounter,
} from './electorateTools.js';
import { QuorumRule } from './question.js';
import { defineHeapFarClass } from '../../store/src/patterns/interface-tools';
import { CommitteeAdminI, CommitteeIKit } from './typeGuards';
import { initEmpty } from '../../store/src';
import { CommitteeIKit } from './typeGuards.js';

const { ceilDivide } = natSafeMath;

Expand Down Expand Up @@ -93,58 +91,94 @@ const start = (zcf, privateArgs) => {
[...Array(committeeSize).keys()].map(makeCommitteeVoterInvitation),
);

/** @type {AddQuestion} */
const addQuestion = async (voteCounter, questionSpec) => {
const quorumThreshold = quorumRule => {
switch (quorumRule) {
case QuorumRule.MAJORITY:
return ceilDivide(committeeSize, 2);
case QuorumRule.ALL:
return committeeSize;
case QuorumRule.NO_QUORUM:
return 0;
default:
throw Error(`${quorumRule} is not a recognized quorum rule`);
}
};

return startCounter(
zcf,
questionSpec,
quorumThreshold(questionSpec.quorumRule),
voteCounter,
allQuestions,
questionsPublisher,
);
};

/** @type {CommitteeElectoratePublic} */
const publicFacet = {
getQuestionSubscriber: () => questionsSubscriber,
getOpenQuestions: () => getOpenQuestions(allQuestions),
getName: () => committeeName,
getInstance: zcf.getInstance,
getQuestion: handleP => getQuestion(handleP, allQuestions),
};

// /** @ type {CommitteeElectorateCreatorFacet} */
const adminFacet = {
getPoserInvitation: () => getPoserInvitation(zcf, addQuestion),
addQuestion,
getVoterInvitations: () => invitations,
getQuestionSubscriber: () => questionsSubscriber,
getPublicFacet: () => publicFacet,
};

const makeCommitteeFacets = defineHeapFarClass(
// TODO(CTH) a Kit!
// const makeCommitteeFacets = defineHeapFarClassKit(
const makeCommitteeFacets = defineHeapFarClassKit(
'Committee Facets',
CommitteeIKit,
initEmpty,
{ adminFacet, publicFacet },
{
creatorFacet: {
getPoserInvitation() {
return getPoserInvitation(zcf, async (voteCounter, questionSpec) => {
const quorumThreshold = quorumRule => {
switch (quorumRule) {
case QuorumRule.MAJORITY:
return ceilDivide(committeeSize, 2);
case QuorumRule.ALL:
return committeeSize;
case QuorumRule.NO_QUORUM:
return 0;
default:
throw Error(`${quorumRule} is not a recognized quorum rule`);
}
};

return startCounter(
zcf,
questionSpec,
quorumThreshold(questionSpec.quorumRule),
voteCounter,
allQuestions,
questionsPublisher,
);
});
},
/** @type {AddQuestion} */
async addQuestion(voteCounter, questionSpec) {
const quorumThreshold = quorumRule => {
switch (quorumRule) {
case QuorumRule.MAJORITY:
return ceilDivide(committeeSize, 2);
case QuorumRule.ALL:
return committeeSize;
case QuorumRule.NO_QUORUM:
return 0;
default:
throw Error(`${quorumRule} is not a recognized quorum rule`);
}
};

return startCounter(
zcf,
questionSpec,
quorumThreshold(questionSpec.quorumRule),
voteCounter,
allQuestions,
questionsPublisher,
);
},
getVoterInvitations() {
return invitations;
},
getQuestionSubscriber() {
return questionsSubscriber;
},

getPublicFacet() {
return this.facets.publicFacet;
},
},

publicFacet: {
getQuestionSubscriber() {
return questionsSubscriber;
},
getOpenQuestions() {
return getOpenQuestions(allQuestions);
},
getName() {
return committeeName;
},
getInstance() {
return zcf.getInstance();
},
getQuestion(handleP) {
return getQuestion(handleP, allQuestions);
},
},
},
);

// @ts-expect-error How to type farClasses?
return makeCommitteeFacets();
};

Expand Down
4 changes: 2 additions & 2 deletions packages/governance/src/contractGovernance/governParam.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
ElectionType,
QuorumRule,
} from '../question.js';
import { ParamChangesQuestionSpecShape } from '../typeGuards.js';
import { ParamChangesQuestionDetailsShape } from '../typeGuards.js';

const { details: X } = assert;

Expand Down Expand Up @@ -43,7 +43,7 @@ const makeParamChangePositions = changes => {
* @param {QuestionSpec<ParamChangeIssue<unknown>>} questionSpec
*/
const assertBallotConcernsParam = (paramSpec, questionSpec) => {
fit(questionSpec, ParamChangesQuestionSpecShape);
fit(questionSpec, ParamChangesQuestionDetailsShape);

const { parameterName, paramPath } = paramSpec;
const { issue } = questionSpec;
Expand Down
4 changes: 2 additions & 2 deletions packages/governance/src/contractGovernor.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from './contractGovernance/governParam.js';
import { setupApiGovernance } from './contractGovernance/governApi.js';
import { setupFilterGovernance } from './contractGovernance/governFilter.js';
import { ParamChangesQuestionSpecShape } from './typeGuards.js';
import { ParamChangesQuestionDetailsShape } from './typeGuards.js';

const { details: X } = assert;

Expand All @@ -28,7 +28,7 @@ const validateQuestionDetails = async (zoe, electorate, details) => {
counterInstance,
issue: { contract: governedInstance },
} = details;
fit(details, ParamChangesQuestionSpecShape);
fit(details, ParamChangesQuestionDetailsShape);

const governorInstance = await E.get(E(zoe).getTerms(governedInstance))
.electionManager;
Expand Down
37 changes: 25 additions & 12 deletions packages/governance/src/typeGuards.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,18 @@ export const ParamChangesQuestionSpecShape = harden({
quorumRule: 'majority',
tieOutcome: NoParamChangesPositionShape,
});
export const ParamChangesQuestionDetailsShape = harden({
method: 'unranked',
issue: ParamChangesIssueShape,
positions: ParamChangesPositionsShape,
electionType: 'param_change',
maxChoices: M.eq(1),
closingRule: ClosingRuleShape,
quorumRule: 'majority',
tieOutcome: NoParamChangesPositionShape,
questionHandle: makeHandleShape('Question'),
counterInstance: InstanceShape,
});

const ApiInvocationSpecShape = harden({
apiMethodName: M.string(),
Expand Down Expand Up @@ -154,30 +166,31 @@ export const PositionShape = M.or(
export const QuestionHandleShape = makeHandleShape('question');

// TODO(hibbert): add details; move to a more appropriate location
export const SubscriberShape = M.remotable();
export const InvitationShape = M.remotable();
export const SubscriberShape = M.remotable('Subscriber');
export const InvitationShape = M.remotable('Invitation');

// TODO(cth) M.or(<list of questions details subtypes> )
export const QuestionDetailsShape = M.or(ParamChangesQuestionDetailsShape);

export const CommitteePublicI = M.interface('Committee PublicFacet', {
getQuestionSubscriber: M.call().returns(SubscriberShape),
getOpenQuestions: M.call().returns(M.arrayOf(QuestionSpecShape)),
getOpenQuestions: M.call().returns(M.promise()),
getName: M.call().returns(M.string()),
getInstance: M.call().returns(InstanceShape),
getQuestion: M.call(QuestionHandleShape).returns(QuestionSpecShape),
getQuestion: M.call(QuestionHandleShape).returns(M.promise()),
});

export const CommitteeAdminI = M.interface('Committee AdminFacet', {
getPoserInvitation: M.call().returns(InvitationShape),
addQuestion: M.call(InstanceShape, QuestionSpecShape).return(InstanceShape),
getVoterInvitations: M.call().returns(InvitationShape),
getPoserInvitation: M.call().returns(M.promise()),
addQuestion: M.call(InstanceShape, QuestionSpecShape).returns(M.promise()),
getVoterInvitations: M.call().returns(M.arrayOf(M.promise())),
getQuestionSubscriber: M.call().returns(SubscriberShape),
getPublicFacet: M.call().returns(CommitteePublicI),
});

export const CommitteeIKit = harden({ CommitteePublicI, CommitteeAdminI });

export const QuestionDetailsShape = M.and(QuestionSpecShape, {
questionHandle: M.remotable(),
counterInstance: InstanceShape,
export const CommitteeIKit = harden({
publicFacet: CommitteePublicI,
creatorFacet: CommitteeAdminI,
});

export const QuestionStatsShape = harden({
Expand Down
7 changes: 6 additions & 1 deletion packages/store/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,12 @@ export {
matches,
fit,
} from './patterns/patternMatchers.js';
export { defendPrototype, initEmpty } from './patterns/interface-tools.js';
export {
defendPrototype,
initEmpty,
defineHeapFarClass,
defineHeapFarClassKit,
} from './patterns/interface-tools.js';
export { compareRank, isRankSorted, sortByRank } from './patterns/rankOrder.js';
export {
makeDecodePassable,
Expand Down

0 comments on commit ea40db9

Please sign in to comment.