From 67a1340310a59fb386b8e0be13725e5afd2592d9 Mon Sep 17 00:00:00 2001 From: Chris Hibbert Date: Wed, 14 Sep 2022 16:08:38 -0700 Subject: [PATCH 1/4] 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? --- packages/governance/src/binaryVoteCounter.js | 17 +++++++++++++++-- packages/governance/src/committee.js | 19 +++++++++++++++---- packages/governance/src/electorateTools.js | 7 ++++--- packages/governance/src/internalTypes.js | 1 + packages/governance/src/types.js | 1 + .../test/unitTests/test-ballotCount.js | 18 ++++++++++++++++++ 6 files changed, 54 insertions(+), 9 deletions(-) diff --git a/packages/governance/src/binaryVoteCounter.js b/packages/governance/src/binaryVoteCounter.js index 797addcda4a..50ac4e4e3de 100644 --- a/packages/governance/src/binaryVoteCounter.js +++ b/packages/governance/src/binaryVoteCounter.js @@ -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, @@ -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); @@ -123,6 +129,12 @@ 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, outcome => { + const voteOutcome = { question: details.questionHandle, outcome }; + return E(publisher).publish(voteOutcome); + }); }; const closeFacet = makeHeapFarInstance( @@ -198,7 +210,7 @@ const makeBinaryVoteCounter = (questionSpec, threshold, instance) => { /** * @type {ContractStartFn} */ -const start = zcf => { +const start = (zcf, { outcomesPublisher }) => { // 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 @@ -210,6 +222,7 @@ const start = zcf => { questionSpec, quorumThreshold, zcf.getInstance(), + outcomesPublisher, ); scheduleClose(questionSpec.closingRule, () => closeFacet.closeVoting()); diff --git a/packages/governance/src/committee.js b/packages/governance/src/committee.js index 19a89a22a47..bd50b267cff 100644 --- a/packages/governance/src/committee.js +++ b/packages/governance/src/committee.js @@ -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} */ const { subscriber: questionsSubscriber, publisher: questionsPublisher } = - makeStoredPublishKit( - E(privateArgs.storageNode).makeChildNode('latestQuestion'), - privateArgs.marshaller, - ); + makeStoredPublishKit(questionNode, privateArgs.marshaller); const makeCommitteeVoterInvitation = index => { /** @type {OfferHandler} */ @@ -158,6 +158,16 @@ const start = (zcf, privateArgs) => { } }; + const outcomeNode = E(privateArgs.storageNode).makeChildNode( + 'latestOutcome', + ); + + /** @type {StoredPublishKit<{question: Handle<'Question'>,outcome: Position}>} */ + const { publisher: outcomesPublisher } = makeStoredPublishKit( + outcomeNode, + privateArgs.marshaller, + ); + return startCounter( zcf, questionSpec, @@ -165,6 +175,7 @@ const start = (zcf, privateArgs) => { voteCounter, allQuestions, questionsPublisher, + outcomesPublisher, ); }, getVoterInvitations() { diff --git a/packages/governance/src/electorateTools.js b/packages/governance/src/electorateTools.js index 6b7e4cfd49e..82f2c93b8a8 100644 --- a/packages/governance/src/electorateTools.js +++ b/packages/governance/src/electorateTools.js @@ -14,7 +14,8 @@ const startCounter = async ( quorumThreshold, voteCounter, questionStore, - publisher, + questionsPublisher, + outcomesPublisher, ) => { const voteCounterTerms = { questionSpec, @@ -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, { outcomesPublisher }); 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 }; diff --git a/packages/governance/src/internalTypes.js b/packages/governance/src/internalTypes.js index 875e0ffbfb1..5f33bd6e4b8 100644 --- a/packages/governance/src/internalTypes.js +++ b/packages/governance/src/internalTypes.js @@ -13,5 +13,6 @@ * @param {ERef} voteCounter * @param {Store, QuestionRecord>} questionStore * @param {Publisher} publisher + * @param {Publisher<{question: Handle<'Question'>,outcome: Position}>} storageNode` * @returns {AddQuestionReturn} */ diff --git a/packages/governance/src/types.js b/packages/governance/src/types.js index 3a9bc2b4225..c4053580ada 100644 --- a/packages/governance/src/types.js +++ b/packages/governance/src/types.js @@ -213,6 +213,7 @@ * @param {bigint} threshold - questionSpec includes quorumRule; the electorate * converts that to a number that the counter can enforce. * @param {Instance} instance + * @param {ERef,outcome: Position}>>} publisher * @returns {VoteCounterFacets} */ diff --git a/packages/governance/test/unitTests/test-ballotCount.js b/packages/governance/test/unitTests/test-ballotCount.js index 1c67475a2ba..757f046472e 100644 --- a/packages/governance/test/unitTests/test-ballotCount.js +++ b/packages/governance/test/unitTests/test-ballotCount.js @@ -6,6 +6,8 @@ import { E } from '@endo/eventual-send'; import buildManualTimer from '@agoric/zoe/tools/manualTimer.js'; import { makeHandle } from '@agoric/zoe/src/makeHandle.js'; import { Far } from '@endo/marshal'; +import { makeStoredPublishKit } from '@agoric/notifier'; +import { makeFakeMarshaller } from '@agoric/notifier/tools/testSupports.js'; import { makeBinaryVoteCounter, @@ -15,6 +17,7 @@ import { coerceQuestionSpec, makeParamChangePositions, } from '../../src/index.js'; +import { makeMockChainStorageRoot } from '../../../vats/tools/storage-test-utils.js'; const SIMPLE_ISSUE = harden({ text: 'Fish or cut bait?' }); const FISH = harden({ text: 'Fish' }); @@ -40,6 +43,11 @@ const FAKE_CLOSING_RULE = { const FAKE_COUNTER_INSTANCE = makeHandle('Instance'); +function makePublisherFromFakes() { + return makeStoredPublishKit(makeMockChainStorageRoot(), makeFakeMarshaller()) + .publisher; +} + test('binary question', async t => { const questionSpec = coerceQuestionSpec({ method: ChoiceMethod.UNRANKED, @@ -55,6 +63,7 @@ test('binary question', async t => { questionSpec, 0n, FAKE_COUNTER_INSTANCE, + makePublisherFromFakes(), ); const aliceTemplate = publicFacet.getQuestion(); const aliceSeat = makeHandle('Voter'); @@ -83,6 +92,7 @@ test('binary spoiled', async t => { questionSpec, 0n, FAKE_COUNTER_INSTANCE, + makePublisherFromFakes(), ); const aliceTemplate = publicFacet.getQuestion(); const aliceSeat = makeHandle('Voter'); @@ -114,6 +124,7 @@ test('binary tied', async t => { questionSpec, 2n, FAKE_COUNTER_INSTANCE, + makePublisherFromFakes(), ); const aliceTemplate = publicFacet.getQuestion(); const aliceSeat = makeHandle('Voter'); @@ -142,6 +153,7 @@ test('binary bad vote', async t => { questionSpec, 0n, FAKE_COUNTER_INSTANCE, + makePublisherFromFakes(), ); const aliceSeat = makeHandle('Voter'); @@ -165,6 +177,7 @@ test('binary no votes', async t => { questionSpec, 0n, FAKE_COUNTER_INSTANCE, + makePublisherFromFakes(), ); closeFacet.closeVoting(); @@ -187,6 +200,7 @@ test('binary varying share weights', async t => { questionSpec, 1n, FAKE_COUNTER_INSTANCE, + makePublisherFromFakes(), ); const aceSeat = makeHandle('Voter'); const austinSeat = makeHandle('Voter'); @@ -219,6 +233,7 @@ test('binary contested', async t => { questionSpec, 3n, FAKE_COUNTER_INSTANCE, + makePublisherFromFakes(), ); const template = publicFacet.getQuestion(); const aliceSeat = makeHandle('Voter'); @@ -250,6 +265,7 @@ test('binary revote', async t => { questionSpec, 5n, FAKE_COUNTER_INSTANCE, + makePublisherFromFakes(), ); const template = publicFacet.getQuestion(); const aliceSeat = makeHandle('Voter'); @@ -282,6 +298,7 @@ test('binary question too many', async t => { questionSpec, 1n, FAKE_COUNTER_INSTANCE, + makePublisherFromFakes(), ); const aliceTemplate = publicFacet.getQuestion(); const aliceSeat = makeHandle('Voter'); @@ -310,6 +327,7 @@ test('binary no quorum', async t => { questionSpec, 2n, FAKE_COUNTER_INSTANCE, + makePublisherFromFakes(), ); const aliceTemplate = publicFacet.getQuestion(); const aliceSeat = makeHandle('Voter'); From ef87a2f57a9a196005f32d0e4a3ce358c2f2cdcf Mon Sep 17 00:00:00 2001 From: Chris Hibbert Date: Wed, 14 Sep 2022 17:35:36 -0700 Subject: [PATCH 2/4] test: test the published outcomes --- packages/governance/src/binaryVoteCounter.js | 6 + .../test/unitTests/test-ballotCount.js | 103 +++++++++++++++--- 2 files changed, 94 insertions(+), 15 deletions(-) diff --git a/packages/governance/src/binaryVoteCounter.js b/packages/governance/src/binaryVoteCounter.js index 50ac4e4e3de..8b63bf1e8c2 100644 --- a/packages/governance/src/binaryVoteCounter.js +++ b/packages/governance/src/binaryVoteCounter.js @@ -119,6 +119,12 @@ const makeBinaryVoteCounter = ( if (!makeQuorumCounter(threshold).check(stats)) { outcomePromise.reject('No quorum'); + const voteOutcome = { + question: details.questionHandle, + outcome: 'No quorum', + }; + // @ts-expect-error Expand type to allow non-position? + E(publisher).publish(voteOutcome); return; } diff --git a/packages/governance/test/unitTests/test-ballotCount.js b/packages/governance/test/unitTests/test-ballotCount.js index 757f046472e..470e5419636 100644 --- a/packages/governance/test/unitTests/test-ballotCount.js +++ b/packages/governance/test/unitTests/test-ballotCount.js @@ -7,7 +7,10 @@ import buildManualTimer from '@agoric/zoe/tools/manualTimer.js'; import { makeHandle } from '@agoric/zoe/src/makeHandle.js'; import { Far } from '@endo/marshal'; import { makeStoredPublishKit } from '@agoric/notifier'; -import { makeFakeMarshaller } from '@agoric/notifier/tools/testSupports.js'; +import { + eventLoopIteration, + makeFakeMarshaller, +} from '@agoric/notifier/tools/testSupports.js'; import { makeBinaryVoteCounter, @@ -44,8 +47,9 @@ const FAKE_CLOSING_RULE = { const FAKE_COUNTER_INSTANCE = makeHandle('Instance'); function makePublisherFromFakes() { - return makeStoredPublishKit(makeMockChainStorageRoot(), makeFakeMarshaller()) - .publisher; + const storageRoot = makeMockChainStorageRoot(); + const publishKit = makeStoredPublishKit(storageRoot, makeFakeMarshaller()); + return { publisher: publishKit.publisher, storageRoot }; } test('binary question', async t => { @@ -59,11 +63,12 @@ test('binary question', async t => { quorumRule: QuorumRule.NO_QUORUM, tieOutcome: BAIT, }); + const { publisher, storageRoot } = makePublisherFromFakes(); const { publicFacet, creatorFacet, closeFacet } = makeBinaryVoteCounter( questionSpec, 0n, FAKE_COUNTER_INSTANCE, - makePublisherFromFakes(), + publisher, ); const aliceTemplate = publicFacet.getQuestion(); const aliceSeat = makeHandle('Voter'); @@ -75,6 +80,11 @@ test('binary question', async t => { closeFacet.closeVoting(); const outcome = await E(publicFacet).getOutcome(); t.deepEqual(outcome, FISH); + + await eventLoopIteration(); + t.like(storageRoot.getBody('mockChainStorageRoot'), { + outcome: FISH, + }); }); test('binary spoiled', async t => { @@ -88,11 +98,13 @@ test('binary spoiled', async t => { quorumRule: QuorumRule.NO_QUORUM, tieOutcome: BAIT, }); + + const { publisher } = makePublisherFromFakes(); const { publicFacet, creatorFacet } = makeBinaryVoteCounter( questionSpec, 0n, FAKE_COUNTER_INSTANCE, - makePublisherFromFakes(), + publisher, ); const aliceTemplate = publicFacet.getQuestion(); const aliceSeat = makeHandle('Voter'); @@ -120,11 +132,13 @@ test('binary tied', async t => { quorumRule: QuorumRule.MAJORITY, tieOutcome: negative, }); + + const { publisher, storageRoot } = makePublisherFromFakes(); const { publicFacet, creatorFacet, closeFacet } = makeBinaryVoteCounter( questionSpec, 2n, FAKE_COUNTER_INSTANCE, - makePublisherFromFakes(), + publisher, ); const aliceTemplate = publicFacet.getQuestion(); const aliceSeat = makeHandle('Voter'); @@ -136,6 +150,11 @@ test('binary tied', async t => { closeFacet.closeVoting(); const outcome = await E(publicFacet).getOutcome(); t.deepEqual(outcome, negative); + + await eventLoopIteration(); + t.like(storageRoot.getBody('mockChainStorageRoot'), { + outcome: negative, + }); }); test('binary bad vote', async t => { @@ -149,17 +168,29 @@ test('binary bad vote', async t => { quorumRule: QuorumRule.MAJORITY, tieOutcome: negative, }); - const { creatorFacet } = makeBinaryVoteCounter( + const { publisher, storageRoot } = makePublisherFromFakes(); + const { creatorFacet, publicFacet, closeFacet } = makeBinaryVoteCounter( questionSpec, 0n, FAKE_COUNTER_INSTANCE, - makePublisherFromFakes(), + publisher, ); const aliceSeat = makeHandle('Voter'); await t.throwsAsync(() => E(creatorFacet).submitVote(aliceSeat, [BAIT]), { message: `The specified choice is not a legal position: {"text":"Cut Bait"}.`, }); + + closeFacet.closeVoting(); + const outcome = await E(publicFacet) + .getOutcome() + .catch(e => t.is(e, 'No quorum')); + t.deepEqual(outcome, negative); + + await eventLoopIteration(); + t.like(storageRoot.getBody('mockChainStorageRoot'), { + outcome: negative, + }); }); test('binary no votes', async t => { @@ -173,16 +204,22 @@ test('binary no votes', async t => { quorumRule: QuorumRule.MAJORITY, tieOutcome: negative, }); + const { publisher, storageRoot } = makePublisherFromFakes(); const { publicFacet, closeFacet } = makeBinaryVoteCounter( questionSpec, 0n, FAKE_COUNTER_INSTANCE, - makePublisherFromFakes(), + publisher, ); closeFacet.closeVoting(); const outcome = await E(publicFacet).getOutcome(); t.deepEqual(outcome, negative); + + await eventLoopIteration(); + t.like(storageRoot.getBody('mockChainStorageRoot'), { + outcome: negative, + }); }); test('binary varying share weights', async t => { @@ -196,11 +233,12 @@ test('binary varying share weights', async t => { quorumRule: QuorumRule.NO_QUORUM, tieOutcome: BAIT, }); + const { publisher, storageRoot } = makePublisherFromFakes(); const { publicFacet, creatorFacet, closeFacet } = makeBinaryVoteCounter( questionSpec, 1n, FAKE_COUNTER_INSTANCE, - makePublisherFromFakes(), + publisher, ); const aceSeat = makeHandle('Voter'); const austinSeat = makeHandle('Voter'); @@ -215,6 +253,11 @@ test('binary varying share weights', async t => { closeFacet.closeVoting(); const outcome = await E(publicFacet).getOutcome(); t.deepEqual(outcome, FISH); + + await eventLoopIteration(); + t.like(storageRoot.getBody('mockChainStorageRoot'), { + outcome: FISH, + }); }); test('binary contested', async t => { @@ -229,11 +272,12 @@ test('binary contested', async t => { quorumRule: QuorumRule.MAJORITY, tieOutcome: negative, }); + const { publisher, storageRoot } = makePublisherFromFakes(); const { publicFacet, creatorFacet, closeFacet } = makeBinaryVoteCounter( questionSpec, 3n, FAKE_COUNTER_INSTANCE, - makePublisherFromFakes(), + publisher, ); const template = publicFacet.getQuestion(); const aliceSeat = makeHandle('Voter'); @@ -248,6 +292,11 @@ test('binary contested', async t => { const outcome = await E(publicFacet).getOutcome(); t.deepEqual(outcome, negative); + + await eventLoopIteration(); + t.like(storageRoot.getBody('mockChainStorageRoot'), { + outcome: negative, + }); }); test('binary revote', async t => { @@ -261,11 +310,12 @@ test('binary revote', async t => { quorumRule: QuorumRule.MAJORITY, tieOutcome: negative, }); + const { publisher, storageRoot } = makePublisherFromFakes(); const { publicFacet, creatorFacet, closeFacet } = makeBinaryVoteCounter( questionSpec, 5n, FAKE_COUNTER_INSTANCE, - makePublisherFromFakes(), + publisher, ); const template = publicFacet.getQuestion(); const aliceSeat = makeHandle('Voter'); @@ -281,6 +331,11 @@ test('binary revote', async t => { const outcome = await E(publicFacet).getOutcome(); t.deepEqual(outcome, positive); + + await eventLoopIteration(); + t.like(storageRoot.getBody('mockChainStorageRoot'), { + outcome: positive, + }); }); test('binary question too many', async t => { @@ -294,11 +349,12 @@ test('binary question too many', async t => { quorumRule: QuorumRule.NO_QUORUM, tieOutcome: BAIT, }); - const { publicFacet, creatorFacet } = makeBinaryVoteCounter( + const { publisher, storageRoot } = makePublisherFromFakes(); + const { publicFacet, creatorFacet, closeFacet } = makeBinaryVoteCounter( questionSpec, 1n, FAKE_COUNTER_INSTANCE, - makePublisherFromFakes(), + publisher, ); const aliceTemplate = publicFacet.getQuestion(); const aliceSeat = makeHandle('Voter'); @@ -310,6 +366,17 @@ test('binary question too many', async t => { message: 'only 1 position allowed', }, ); + + closeFacet.closeVoting(); + const outcome = await E(publicFacet) + .getOutcome() + .catch(e => t.is(e, 'No quorum')); + t.deepEqual(outcome, true); + + await eventLoopIteration(); + t.like(storageRoot.getBody('mockChainStorageRoot'), { + outcome: 'No quorum', + }); }); test('binary no quorum', async t => { @@ -323,11 +390,12 @@ test('binary no quorum', async t => { quorumRule: QuorumRule.NO_QUORUM, tieOutcome: BAIT, }); + const { publisher, storageRoot } = makePublisherFromFakes(); const { publicFacet, creatorFacet, closeFacet } = makeBinaryVoteCounter( questionSpec, 2n, FAKE_COUNTER_INSTANCE, - makePublisherFromFakes(), + publisher, ); const aliceTemplate = publicFacet.getQuestion(); const aliceSeat = makeHandle('Voter'); @@ -339,6 +407,11 @@ test('binary no quorum', async t => { .getOutcome() .then(o => t.fail(`expected to reject, not ${o}`)) .catch(e => t.deepEqual(e, 'No quorum')); + + await eventLoopIteration(); + t.like(storageRoot.getBody('mockChainStorageRoot'), { + outcome: 'No quorum', + }); }); test('binary too many positions', async t => { From e93c2796582e5c9664f8914edffce4d95a953acf Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Thu, 15 Sep 2022 06:53:37 -0700 Subject: [PATCH 3/4] chore: discriminate fail outcome --- packages/governance/src/binaryVoteCounter.js | 21 ++++++++++----- packages/governance/src/committee.js | 10 ++++--- packages/governance/src/electorateTools.js | 4 +-- packages/governance/src/internalTypes.js | 5 ++-- packages/governance/src/types.js | 9 ++++++- .../test/unitTests/test-ballotCount.js | 27 ++++++++++++------- 6 files changed, 52 insertions(+), 24 deletions(-) diff --git a/packages/governance/src/binaryVoteCounter.js b/packages/governance/src/binaryVoteCounter.js index 8b63bf1e8c2..f809874f9ef 100644 --- a/packages/governance/src/binaryVoteCounter.js +++ b/packages/governance/src/binaryVoteCounter.js @@ -119,11 +119,12 @@ const makeBinaryVoteCounter = ( if (!makeQuorumCounter(threshold).check(stats)) { outcomePromise.reject('No quorum'); + /** @type {OutcomeRecord} */ const voteOutcome = { question: details.questionHandle, - outcome: 'No quorum', + outcome: 'fail', + reason: 'No quorum', }; - // @ts-expect-error Expand type to allow non-position? E(publisher).publish(voteOutcome); return; } @@ -137,8 +138,13 @@ const makeBinaryVoteCounter = ( } // XXX if we should distinguish ties, publish should be called in if above - E.when(outcomePromise.promise, outcome => { - const voteOutcome = { question: details.questionHandle, outcome }; + E.when(outcomePromise.promise, position => { + /** @type {OutcomeRecord} */ + const voteOutcome = { + question: details.questionHandle, + position, + outcome: 'win', + }; return E(publisher).publish(voteOutcome); }); }; @@ -214,9 +220,10 @@ const makeBinaryVoteCounter = ( // instance in the publicFacet before returning public and creator facets. /** - * @type {ContractStartFn} + * @param {ZCF<{questionSpec: QuestionSpec, quorumThreshold: bigint}>} zcf + * @param {{outcomePublisher: Publisher}} outcomePublisher */ -const start = (zcf, { outcomesPublisher }) => { +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 @@ -228,7 +235,7 @@ const start = (zcf, { outcomesPublisher }) => { questionSpec, quorumThreshold, zcf.getInstance(), - outcomesPublisher, + outcomePublisher, ); scheduleClose(questionSpec.closingRule, () => closeFacet.closeVoting()); diff --git a/packages/governance/src/committee.js b/packages/governance/src/committee.js index bd50b267cff..da6cb0fd85e 100644 --- a/packages/governance/src/committee.js +++ b/packages/governance/src/committee.js @@ -22,6 +22,10 @@ import { const { ceilDivide } = natSafeMath; +/** + * @typedef {{ question: Handle<'Question'>, outcome: Position }} OutcomeRecord + */ + /** * Each Committee (an Electorate) represents a particular set of voters. The * number of voters is visible in the terms. @@ -162,8 +166,8 @@ const start = (zcf, privateArgs) => { 'latestOutcome', ); - /** @type {StoredPublishKit<{question: Handle<'Question'>,outcome: Position}>} */ - const { publisher: outcomesPublisher } = makeStoredPublishKit( + /** @type {StoredPublishKit} */ + const { publisher: outcomePublisher } = makeStoredPublishKit( outcomeNode, privateArgs.marshaller, ); @@ -175,7 +179,7 @@ const start = (zcf, privateArgs) => { voteCounter, allQuestions, questionsPublisher, - outcomesPublisher, + outcomePublisher, ); }, getVoterInvitations() { diff --git a/packages/governance/src/electorateTools.js b/packages/governance/src/electorateTools.js index 82f2c93b8a8..6ac020a3711 100644 --- a/packages/governance/src/electorateTools.js +++ b/packages/governance/src/electorateTools.js @@ -15,7 +15,7 @@ const startCounter = async ( voteCounter, questionStore, questionsPublisher, - outcomesPublisher, + outcomePublisher, ) => { const voteCounterTerms = { questionSpec, @@ -27,7 +27,7 @@ const startCounter = async ( /** @type {{ creatorFacet: VoteCounterCreatorFacet, publicFacet: VoteCounterPublicFacet, instance: Instance }} */ const { creatorFacet, publicFacet, instance } = await E( zcf.getZoeService(), - ).startInstance(voteCounter, {}, voteCounterTerms, { outcomesPublisher }); + ).startInstance(voteCounter, {}, voteCounterTerms, { outcomePublisher }); const details = await E(publicFacet).getDetails(); const { deadline } = questionSpec.closingRule; questionsPublisher.publish(details); diff --git a/packages/governance/src/internalTypes.js b/packages/governance/src/internalTypes.js index 5f33bd6e4b8..08f5ae3e4ae 100644 --- a/packages/governance/src/internalTypes.js +++ b/packages/governance/src/internalTypes.js @@ -1,3 +1,4 @@ +// @ts-check /** * @typedef {object} QuestionRecord * @property {ERef} voteCap @@ -12,7 +13,7 @@ * @param {unknown} quorumThreshold * @param {ERef} voteCounter * @param {Store, QuestionRecord>} questionStore - * @param {Publisher} publisher - * @param {Publisher<{question: Handle<'Question'>,outcome: Position}>} storageNode` + * @param {Publisher} questionPublisher + * @param {Publisher} outcomePublisher * @returns {AddQuestionReturn} */ diff --git a/packages/governance/src/types.js b/packages/governance/src/types.js index c4053580ada..cef771f4f41 100644 --- a/packages/governance/src/types.js +++ b/packages/governance/src/types.js @@ -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 * @@ -213,7 +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,outcome: Position}>>} publisher + * @param {ERef>} publisher * @returns {VoteCounterFacets} */ diff --git a/packages/governance/test/unitTests/test-ballotCount.js b/packages/governance/test/unitTests/test-ballotCount.js index 470e5419636..2278104198d 100644 --- a/packages/governance/test/unitTests/test-ballotCount.js +++ b/packages/governance/test/unitTests/test-ballotCount.js @@ -83,7 +83,8 @@ test('binary question', async t => { await eventLoopIteration(); t.like(storageRoot.getBody('mockChainStorageRoot'), { - outcome: FISH, + outcome: 'win', + position: FISH, }); }); @@ -153,7 +154,8 @@ test('binary tied', async t => { await eventLoopIteration(); t.like(storageRoot.getBody('mockChainStorageRoot'), { - outcome: negative, + outcome: 'win', + position: negative, }); }); @@ -189,7 +191,8 @@ test('binary bad vote', async t => { await eventLoopIteration(); t.like(storageRoot.getBody('mockChainStorageRoot'), { - outcome: negative, + outcome: 'win', + position: negative, }); }); @@ -218,7 +221,8 @@ test('binary no votes', async t => { await eventLoopIteration(); t.like(storageRoot.getBody('mockChainStorageRoot'), { - outcome: negative, + outcome: 'win', + position: negative, }); }); @@ -256,7 +260,8 @@ test('binary varying share weights', async t => { await eventLoopIteration(); t.like(storageRoot.getBody('mockChainStorageRoot'), { - outcome: FISH, + outcome: 'win', + position: FISH, }); }); @@ -295,7 +300,8 @@ test('binary contested', async t => { await eventLoopIteration(); t.like(storageRoot.getBody('mockChainStorageRoot'), { - outcome: negative, + outcome: 'win', + position: negative, }); }); @@ -334,7 +340,8 @@ test('binary revote', async t => { await eventLoopIteration(); t.like(storageRoot.getBody('mockChainStorageRoot'), { - outcome: positive, + outcome: 'win', + position: positive, }); }); @@ -375,7 +382,8 @@ test('binary question too many', async t => { await eventLoopIteration(); t.like(storageRoot.getBody('mockChainStorageRoot'), { - outcome: 'No quorum', + outcome: 'fail', + reason: 'No quorum', }); }); @@ -410,7 +418,8 @@ test('binary no quorum', async t => { await eventLoopIteration(); t.like(storageRoot.getBody('mockChainStorageRoot'), { - outcome: 'No quorum', + outcome: 'fail', + reason: 'No quorum', }); }); From e67ab9b1a6e5069460d994988c50712b595a7722 Mon Sep 17 00:00:00 2001 From: Chris Hibbert Date: Thu, 15 Sep 2022 09:33:52 -0700 Subject: [PATCH 4/4] chore: drop extra declaration of OutcomeRecord --- packages/governance/src/committee.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/governance/src/committee.js b/packages/governance/src/committee.js index da6cb0fd85e..9c9006cd83e 100644 --- a/packages/governance/src/committee.js +++ b/packages/governance/src/committee.js @@ -22,10 +22,6 @@ import { const { ceilDivide } = natSafeMath; -/** - * @typedef {{ question: Handle<'Question'>, outcome: Position }} OutcomeRecord - */ - /** * Each Committee (an Electorate) represents a particular set of voters. The * number of voters is visible in the terms.