Skip to content

Commit

Permalink
feat: publish vote results from the voteCounter (#6204)
Browse files Browse the repository at this point in the history
* feat: publish vote results from the voteCounter

@turadg,   Is there a straightforward change to the test that would
allow us to verify the published results?

* test: test the published outcomes

* chore: discriminate fail outcome

* chore: drop extra declaration of OutcomeRecord

Co-authored-by: Turadg Aleahmad <turadg@agoric.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Sep 15, 2022
1 parent e304af3 commit 7645df0
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 13 deletions.
32 changes: 29 additions & 3 deletions packages/governance/src/binaryVoteCounter.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { Far } from '@endo/marshal';
import { makePromiseKit } from '@endo/promise-kit';
import { makeHeapFarInstance, keyEQ, makeStore } from '@agoric/store';
import { E } from '@endo/eventual-send';

import {
buildUnrankedQuestion,
Expand Down Expand Up @@ -53,7 +54,12 @@ const validateBinaryQuestionSpec = questionSpec => {
// independently. The standard Zoe start function is at the bottom of this file.

/** @type {BuildVoteCounter} */
const makeBinaryVoteCounter = (questionSpec, threshold, instance) => {
const makeBinaryVoteCounter = (
questionSpec,
threshold,
instance,
publisher,
) => {
validateBinaryQuestionSpec(questionSpec);

const question = buildUnrankedQuestion(questionSpec, instance);
Expand Down Expand Up @@ -113,6 +119,13 @@ const makeBinaryVoteCounter = (questionSpec, threshold, instance) => {

if (!makeQuorumCounter(threshold).check(stats)) {
outcomePromise.reject('No quorum');
/** @type {OutcomeRecord} */
const voteOutcome = {
question: details.questionHandle,
outcome: 'fail',
reason: 'No quorum',
};
E(publisher).publish(voteOutcome);
return;
}

Expand All @@ -123,6 +136,17 @@ const makeBinaryVoteCounter = (questionSpec, threshold, instance) => {
} else {
outcomePromise.resolve(questionSpec.tieOutcome);
}

// XXX if we should distinguish ties, publish should be called in if above
E.when(outcomePromise.promise, position => {
/** @type {OutcomeRecord} */
const voteOutcome = {
question: details.questionHandle,
position,
outcome: 'win',
};
return E(publisher).publish(voteOutcome);
});
};

const closeFacet = makeHeapFarInstance(
Expand Down Expand Up @@ -196,9 +220,10 @@ const makeBinaryVoteCounter = (questionSpec, threshold, instance) => {
// instance in the publicFacet before returning public and creator facets.

/**
* @type {ContractStartFn<VoteCounterPublicFacet, VoteCounterCreatorFacet, {questionSpec: QuestionSpec, quorumThreshold: bigint}>}
* @param {ZCF<{questionSpec: QuestionSpec, quorumThreshold: bigint}>} zcf
* @param {{outcomePublisher: Publisher<OutcomeRecord>}} outcomePublisher
*/
const start = zcf => {
const start = (zcf, { outcomePublisher }) => {
// There are a variety of ways of counting quorums. The parameters must be
// visible in the terms. We're doing a simple threshold here. If we wanted to
// discount abstentions, we could refactor to provide the quorumCounter as a
Expand All @@ -210,6 +235,7 @@ const start = zcf => {
questionSpec,
quorumThreshold,
zcf.getInstance(),
outcomePublisher,
);

scheduleClose(questionSpec.closingRule, () => closeFacet.closeVoting());
Expand Down
19 changes: 15 additions & 4 deletions packages/governance/src/committee.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ const start = (zcf, privateArgs) => {
const allQuestions = makeStore('Question');
assert(privateArgs?.storageNode, 'Missing storageNode');
assert(privateArgs?.marshaller, 'Missing marshaller');
const questionNode = E(privateArgs.storageNode).makeChildNode(
'latestQuestion',
);
/** @type {StoredPublishKit<QuestionDetails>} */
const { subscriber: questionsSubscriber, publisher: questionsPublisher } =
makeStoredPublishKit(
E(privateArgs.storageNode).makeChildNode('latestQuestion'),
privateArgs.marshaller,
);
makeStoredPublishKit(questionNode, privateArgs.marshaller);

const makeCommitteeVoterInvitation = index => {
/** @type {OfferHandler} */
Expand Down Expand Up @@ -158,13 +158,24 @@ const start = (zcf, privateArgs) => {
}
};

const outcomeNode = E(privateArgs.storageNode).makeChildNode(
'latestOutcome',
);

/** @type {StoredPublishKit<OutcomeRecord>} */
const { publisher: outcomePublisher } = makeStoredPublishKit(
outcomeNode,
privateArgs.marshaller,
);

return startCounter(
zcf,
questionSpec,
quorumThreshold(questionSpec.quorumRule),
voteCounter,
allQuestions,
questionsPublisher,
outcomePublisher,
);
},
getVoterInvitations() {
Expand Down
7 changes: 4 additions & 3 deletions packages/governance/src/electorateTools.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ const startCounter = async (
quorumThreshold,
voteCounter,
questionStore,
publisher,
questionsPublisher,
outcomePublisher,
) => {
const voteCounterTerms = {
questionSpec,
Expand All @@ -26,10 +27,10 @@ const startCounter = async (
/** @type {{ creatorFacet: VoteCounterCreatorFacet, publicFacet: VoteCounterPublicFacet, instance: Instance }} */
const { creatorFacet, publicFacet, instance } = await E(
zcf.getZoeService(),
).startInstance(voteCounter, {}, voteCounterTerms);
).startInstance(voteCounter, {}, voteCounterTerms, { outcomePublisher });
const details = await E(publicFacet).getDetails();
const { deadline } = questionSpec.closingRule;
publisher.publish(details);
questionsPublisher.publish(details);
const questionHandle = details.questionHandle;

const voteCounterFacets = { voteCap: creatorFacet, publicFacet, deadline };
Expand Down
4 changes: 3 additions & 1 deletion packages/governance/src/internalTypes.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @ts-check
/**
* @typedef {object} QuestionRecord
* @property {ERef<VoteCounterCreatorFacet>} voteCap
Expand All @@ -12,6 +13,7 @@
* @param {unknown} quorumThreshold
* @param {ERef<Installation>} voteCounter
* @param {Store<Handle<'Question'>, QuestionRecord>} questionStore
* @param {Publisher<unknown>} publisher
* @param {Publisher<unknown>} questionPublisher
* @param {Publisher<OutcomeRecord>} outcomePublisher
* @returns {AddQuestionReturn}
*/
8 changes: 8 additions & 0 deletions packages/governance/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@
* OfferFilterPosition | NoChangeOfferFilterPosition | InvokeApiPosition } Position
*/

/**
* @typedef {{ question: Handle<'Question'> } & (
* { outcome: 'win', position: Position } |
* { outcome: 'fail', reason: 'No quorum' }
* )} OutcomeRecord
*/

/**
* Specification when requesting creation of a Question
*
Expand Down Expand Up @@ -213,6 +220,7 @@
* @param {bigint} threshold - questionSpec includes quorumRule; the electorate
* converts that to a number that the counter can enforce.
* @param {Instance} instance
* @param {ERef<Publisher<OutcomeRecord>>} publisher
* @returns {VoteCounterFacets}
*/

Expand Down
Loading

0 comments on commit 7645df0

Please sign in to comment.