From 3a206344b1f3204d5ed89fb6a0932d00dc7e6b33 Mon Sep 17 00:00:00 2001 From: Chris Hibbert Date: Thu, 30 Sep 2021 15:44:38 -0700 Subject: [PATCH] feat: ContractGovernor manages parameter updating for a contract ContractGovernor is an ElectionManager that knows its Registrar, and starts up and manages one contract. The managed contract returns a facet that describes the managed parameters to the governor. The ContractGovernor returns a facet that allows the creation of new Ballot questions to ask the electorate to approve changing the values of those parameters. There's a README.md that gives on overview of the interdependent APIs. The swingsetTest exercise the ContractGovernor over a trivial governedContract that shows how a contract can configure its parameters, and the methods that have to be available in order for everything to be found and verified. The tests include a verification step that cross-checks the Registrar, ElectionManager (i.e. ContractGovernor in this case), and questions to see that they are consistent. add a helper for governedContracts add index.js add comments for CRUCIAL governance security attributes --- packages/governance/README.md | 221 +++++++ packages/governance/docs/AttackGuide.md | 74 +-- .../governance/docs/contractGovernance.png | Bin 0 -> 61990 bytes .../governance/docs/contractGovernance.puml | 47 ++ packages/governance/docs/coreArchitecture.png | Bin 0 -> 177482 bytes .../governance/docs/coreArchitecture.puml | 116 ++++ packages/governance/docs/example.png | Bin 0 -> 50164 bytes packages/governance/docs/example.puml | 53 ++ packages/governance/exported.js | 1 + packages/governance/src/ballotBuilder.js | 84 --- .../governance/src/binaryBallotCounter.js | 202 ------- packages/governance/src/binaryVoteCounter.js | 199 +++++++ packages/governance/src/closingRule.js | 2 +- packages/governance/src/committee.js | 144 +++++ packages/governance/src/committeeRegistrar.js | 92 --- packages/governance/src/contractGovernor.js | 177 ++++++ packages/governance/src/contractHelper.js | 65 +++ packages/governance/src/exported.js | 1 + packages/governance/src/governParam.js | 157 +++++ packages/governance/src/index.js | 31 + packages/governance/src/internalTypes.js | 6 + packages/governance/src/paramManager.js | 70 ++- packages/governance/src/question.js | 205 +++++++ packages/governance/src/registrarTools.js | 64 ++ packages/governance/src/types.js | 547 +++++++++++++++--- packages/governance/src/validators.js | 75 +++ .../committeeBinary/bootstrap.js | 189 +++--- .../committeeBinary/test-committee.js | 70 +-- .../committeeBinary/vat-voter.js | 80 +-- .../contractGovernor/bootstrap.js | 214 +++++++ .../contractGovernor/governedContract.js | 45 ++ .../contractGovernor/test-governor.js | 107 ++++ .../contractGovernor/vat-voter.js | 101 ++++ .../swingsetTests/contractGovernor/vat-zoe.js | 18 + .../test/unitTests/test-ballotBuilder.js | 180 ++++++ .../test/unitTests/test-ballotCount.js | 360 +++++++----- .../test/unitTests/test-committee.js | 105 ++-- .../{ => unitTests}/test-param-manager.js | 182 ++++-- .../test/unitTests/test-paramGovernance.js | 241 ++++++++ packages/zoe/src/contractSupport/ratio.js | 3 + 40 files changed, 3641 insertions(+), 887 deletions(-) create mode 100644 packages/governance/README.md create mode 100644 packages/governance/docs/contractGovernance.png create mode 100644 packages/governance/docs/contractGovernance.puml create mode 100644 packages/governance/docs/coreArchitecture.png create mode 100644 packages/governance/docs/coreArchitecture.puml create mode 100644 packages/governance/docs/example.png create mode 100644 packages/governance/docs/example.puml create mode 100644 packages/governance/exported.js delete mode 100644 packages/governance/src/ballotBuilder.js delete mode 100644 packages/governance/src/binaryBallotCounter.js create mode 100644 packages/governance/src/binaryVoteCounter.js create mode 100644 packages/governance/src/committee.js delete mode 100644 packages/governance/src/committeeRegistrar.js create mode 100644 packages/governance/src/contractGovernor.js create mode 100644 packages/governance/src/contractHelper.js create mode 100644 packages/governance/src/exported.js create mode 100644 packages/governance/src/governParam.js create mode 100644 packages/governance/src/index.js create mode 100644 packages/governance/src/internalTypes.js create mode 100644 packages/governance/src/question.js create mode 100644 packages/governance/src/registrarTools.js create mode 100644 packages/governance/src/validators.js create mode 100644 packages/governance/test/swingsetTests/contractGovernor/bootstrap.js create mode 100644 packages/governance/test/swingsetTests/contractGovernor/governedContract.js create mode 100644 packages/governance/test/swingsetTests/contractGovernor/test-governor.js create mode 100644 packages/governance/test/swingsetTests/contractGovernor/vat-voter.js create mode 100644 packages/governance/test/swingsetTests/contractGovernor/vat-zoe.js create mode 100644 packages/governance/test/unitTests/test-ballotBuilder.js rename packages/governance/test/{ => unitTests}/test-param-manager.js (50%) create mode 100644 packages/governance/test/unitTests/test-paramGovernance.js diff --git a/packages/governance/README.md b/packages/governance/README.md new file mode 100644 index 00000000000..2ca99a7f87d --- /dev/null +++ b/packages/governance/README.md @@ -0,0 +1,221 @@ +# Governance + +This package provides Electorates and VoteCounters to create a general +framework for governance. It has implementations for particular kinds of +electorates and different ways of tallying votes. + +The electorates and VoteCounters are self-describing and reveal what they are +connected to so that voters can verify that their votes mean what they say and +will be tabulated as expected. + +Any occasion of governance starts with the creation of an Electorate. Two kinds +exist currently that represent committees and stakeholders (Stakeholder support +is in review). The electorate may deal with many questions governing many +things, so the electorate has to exist before any questions can be posed. + +The next piece to be created is an ElectionManager. (A Contract Governor, is a +particular example, discussed below). An ElectionManager is tied to a particular +Electorate. It supports creation of questions, can manage what happens with the +results, and may limit the kinds of questions it can handle. The ElectionManager +is also responsible for specifying which VoteCounter will be used with any +particular question. Different VoteCounters will handle elections with two +positions or more, with plurality voting, single-transferable-vote, or +instant-runoff-voting. + +When a question is posed, it is only with respect to a particular Electorate, +(which identifies a collection of eligible voters) and a particular vote +counting contract. The QuestionSpec consists of `{ method, issue, positions, +electionType, maxChoices }`. The issue and positions can be strings or +structured objects. Method is one of UNRANKED and ORDER, which is sufficient to +describe all the most common kinds of votes. A vote between two candidates or +positions uses UNRANKED with a limit of one vote. ORDER will be useful for +Single Transferable Vote or Instant Runoff Voting. ElectionType distinguishes +PARAM_CHANGE, which has structured questions, from others where the issue is a +string. + +When posing a particular question to be voted on, the closingRule also has to be +specified. When voters are presented with a question to vote on, they have +access to QuestionDetails, which includes information from the QuestionSpec, the +closingRule, and the VoteCounter instance. The VoteCounter has the Electorate +in its terms, so voters can verify it. + +Voters get a voting facet via an invitation, so they're sure they're connected +to the Electorate that's responsible for this vote. They can subscribe with the +electorate to get a list of new questions. They can use the questionHandle from +the notifier to get the questionDetails. Voters cast their vote by sending their +selected list of positions to their electorate, which they know and trust. + +This structure of Electorates and VoteCounters allows voters and observers to +verify how votes will be counted, and who can vote on them, but doesn't +constrain the process of creating questions. ElectionManagers make that process +visible. ContractGovernor is a particular example of that that makes it possible +for a contract to publish details of how its parameters will be subject to +governance. + +## Electorate + +An Electorate represents a set of voters. Each voter receives an invitation +for a voterFacet, which allows voting in all elections supported by +that electorate. The Electorate starts a new VoteCounter instance for each +separate question, and gets the `creatorFacet`, which carries the `submitVote()` +method that registers votes with the voteCounter. The Electorate is responsible +for ensuring that `submitVote()` can only be called with the voter's unique +voterHandle. + +## ContractGovernor + +We want some contracts to be able to make it visible that their internal +parameters can be controlled by a public process, and allow observers to see who +has control, and how those changes can happen. To do so, the contract would +use a ParamManager to hold its mutable state. ParamManager has facets for +accessing the param values and for setting them. The governed contract would use +the access facet internally, and make that visible to anyone who should be able +to see the values, while ensuring that the private facet, which can control the +values, is only accessible to a visible ContractGovernor. The ContractGovernor +makes the Electorate visible, while tightly controlling the process of +creating new questions and presenting them to the electorate. + +The governor starts up the Contract and can see what params are subject to +governance. It provides a private facet that carries the ability to request +votes on changing particular parameters. Some day we may figure out how to make +the process and schedule of selecting parameter changes to vote on also subject +to governance, but that's too many meta-levels at this point. + +The party that has the question-creating facet of the ContractGovernor can +create a question that asks about changing a particular parameter on the +contract instance. The electorate creates new questions, and makes a new +instance of a VoteCounter so everyone can see how questions will be counted. + +Electorates have a public method to get from the questionHandle to a question. +Ballots include the questionSpec, the VoteCounter instance and closingRule. For +contract governance, the question specifies the governed contract instance, the +parameter to be changed, and the proposed new value. + +This is sufficient for voters and others to verify that the contract is managed +by the governor, the electorate is the one the governor uses, and a particular +voteCounter is in use. + +The governed contract can be inspected to verify that some parameter values are +held in a ParamManager, and that a ContractGovernor can cleanly start it up and +have exclusive access to the facet that allows the values to be set. The +contract would also make the read-only facet visible, so others can see the +current values. The initial values of the parameters, along with their types +remain visible in the contract's terms. + +### ParamManager + +`ContractGovernor` expects to work with contracts that use `ParamManager` to +manage their parameters. `buildParamManager()` is designed to be called within +the managed contract so that internal access to the parameter values is +synchronous. A separate facet allows visible management of changes to the +parameter values. + +`buildParamManager()` takes a list of parameter descriptions as its argument. +Descriptions give `{ name, type, value }` for each parameter. The parameter +values are retrieved by name. A separate facet of the paramManager allows the +holder to call `updateFoo()` to change the value. ContractGovernor wraps that +facet up so that usage can be monitored. + +The `type` part of the parameter description is a string. Current supported +values are +`{ AMOUNT, BRAND, INSTANCE, INSTALLATION, NAT, RATIO, STRING, UNKNOWN }`. +The list can be extended as we find more types that contracts want to manage. + +There's a contractHelper for the vast majority of expected clients that will +have a single set of parameters to manage. A contract only has to define the +parameters in a call to `handleParamGovernance()`, and add any needed methods +to the public and creator facets. This will + * validate that the declaration of the parameters is included in its terms, + * add the parameter retriever appropriately to the publicFacet and creatorFacet + +## Scenarios + +### Examining a Contract before use + +Governed contracts will make their governor and parameters visible, either +through the terms or the public facet. The governor, in turn, publicly shares +the electorate, which makes the list of questions visible. The questions show +their voteCounters, which makes it possible to tell how the counting will be +done. + +There isn't currently a way to verify the process of creating new questions. +We'll eventually need to spin a story that will make that more legible. +Currently, the ability to create new governance questions is provided as a +private facet that contains only the method `voteOnParamChange()`. + +When a prospective user of a contract receives a link to an instance of a +contract, they can check the terms to see if the contract names a governor. The +governor's public facet will also refer to the contract it governs. Once you +have the instance you can retrieve the installation from Zoe which allows you to +examine the source. + +The governedContract will provide the electorate, which allows you to check the +electorate, and retrieve a list of open questions. (We should add closed +questions and their resolution as well.) Each question refers to the +voteCounter it uses. + +### Participating in Governance + +Voters are managed by an Electorate. Prospective voters should only accept a +voting API as the outcome of an invitation. The invitation allows you to verify +the particular electorate instance in use. The electorate's public facet has +`getQuestionSubscription()`, which allows you to find out about new questions +for the electorate and `getOpenQuestions()` which lists questions that haven't +been resolved. + +Each question describes its subject. One field of the questionDetails is +`ElectionType`, which can be `PARAM_CHANGE`, `ELECTION`, or `SURVEY`. (I'm sure +we'll come up with more types.) When it is `PARAM_CHANGE`, the questionDetails +will also identify the contract instance, the particular parameter to be +changed, and the proposed new value. At present, all parameter change elections +are by majority vote, and if a majority doesn't vote in favor, then no change is +made. + +## Future Extensions + +The architecture is intended to support several scenarios that haven't been +filled in yet. + +### Electorates + +The initial Electorate represents a Committee, with has an opaque group of +voters. The +contract makes no attempt to make the voters legible to others. This might be +useful for a private group making a decision, or a case where a dictator has the +ability to appoint a committee that will make decisions. + +The AttestedElectorate (coming soon!) is an Electorate that gives the ability to +vote to anyone who has an Attestation payment from the Attestation contract. +Observers can't tell who the voters are, but they can validate the +qualifications to vote. + +Another plausible electorate would use the result of a public vote to give +voting facets to the election winners. There would have to be some kind of +public registration of the identities of the candidates to make them visible. + +### VoteCounters + +The only vote counter currently is the BinaryVoteCounter, which presumes +there are two positions on the ballot and assigns every vote to one or the other +or to 'spoiled'. At the end, it looks for a majority winner and announces that. +It can be configured to have one of the possible outcomes as the default +outcome. If there's a tie and no default, the winner is `undefined`. + +ContractGovernance uses this to make 'no change' be the default when voting on +parameter changes. + +We should have voteCounters for multiple candidate questions. I hope we'll +eventually have IRV (instant runoff) and various forms of proportional +representation. + +### ElectionManager + +The election manager has a role in governance, but not a specific API. The +manager's role is to make the setup of particular elections legible to voters +and other observers. The current example is the ContractGovernor, which manages +changes to contract parameters. There should also be managers that + +* take some action (i.e. add a new collateral type to the AMM) when a vote + passes. +* manage a plebiscite among stake holders to allow participants to express + opinions about the future of the chain. diff --git a/packages/governance/docs/AttackGuide.md b/packages/governance/docs/AttackGuide.md index 04e46f27de1..754a7dbe848 100644 --- a/packages/governance/docs/AttackGuide.md +++ b/packages/governance/docs/AttackGuide.md @@ -2,7 +2,7 @@ This is an incomplete list of potential weak points that an attacker might want to focus on when looking for ways to violate the integrity of the -governance system. It's here to help defenders, but "attacker's mindset" is a +governance system. It's here to help defenders, as "attacker's mindset" is a good way to focus attention for the defender. The list should correspond pretty closely to the set of assurances that the governance system aims to support. @@ -22,15 +22,16 @@ support for custom validation functions. ## Get a voter facet you shouldn't have Every module that handles voter facets should handle them carefully and ensure -they don't escape. If you can get access to a voter facet, you can cast a ballot -and override the preferences of the rightful voter. If you can manufacture voter -facets that are accepted, you can stuff the ballot box. +they don't escape. If you can get access to a voter facet (or the main voteCap +for a VoteCounter), you can cast a ballot and override the preferences of the +rightful voter. If you can manufacture voter facets that are accepted, you can +stuff the ballot box. -## Get a ballotCounter to accept votes from a single voter without replacement +## Get a voteCounter to accept votes from a single voter without replacement -I can't think of a way to evade the way BinaryBallotCounter ensures that each -ballot is only counted once, but it's something to be aware of with new -BallotCounters, and particularly in combination with new Registrars. +I can't think of a way to evade the way BinaryVoteCounter ensures that each +vote is only counted once, but it's something to be aware of with new +VoteCounters, and particularly in combination with new Electorates. ## Break notification so voters don't hear about elections @@ -40,57 +41,57 @@ provided for announcing new issues. ## Leak the ability to create new questions Currently creating new questions is seen as tightly held. If that is loosened -in a new Registrar or ElectionManager, that provides a path for spamming +in a new Electorate or ElectionManager, that provides a path for spamming voters. ## What shenanigans can be caused by creating multiple questions with the same or very similar text? -The question text used to be unique, but each ballot question now has a handle -for disambiguation. Voters ought to validate the particulars of any question -they intend to vote on. Can they be confused by corrections or replacement? Is -there a vulnerability here, or just a UI support need? +The question text used to be unique, but each question now has a +questionHandle for disambiguation. Voters ought to validate the particulars of +any question they intend to vote on. Can they be confused by corrections or +replacement? Is there a vulnerability here, or just a UI support need? -## Create a Ballot that refers to a different BallotCounter than the one the registrar will use +## Create a Question that refers to a different VoteCounter than the one the electorate will use -## Distribute ballots that don't match the official one from the Registrar +## Distribute questions that don't match the official one from the Electorate -Ballots themselves are not secure. The voter has to get a copy of the ballot -from the Registrar to have any assurance that it's valid. If someone else -provides a ballot, they can replace various pieces to fool the voter as to -what is being voted on. +Questions themselves are not secure. The voter has to get a copy of the question +from the Electorate to have any assurance that it's valid. If someone else +provides a question, they can replace various pieces to fool the voter as to +what is being voted on or how the votes will be tallied. -## Ordinary bugs in counting ballots, reporting, etc. +## Ordinary bugs in counting votes, reporting, etc. -If the code in BallotCounter, Registrar, ContractGovernor has subtle mistakes, +If the code in VoteCounter, Electorate, ContractGovernor has subtle mistakes, wrong results will obtain. -## Produce a discrepancy between Terms and actions in BallotCounter or Registrar +## Produce a discrepancy between Terms and actions in VoteCounter or Electorate -The voter's assurance that a particular ballot has the effect they expect -arises in part because the `terms` in the BallotCounter, Registrar, +The voter's assurance that a particular vote has the effect they expect +arises in part because the `terms` in the VoteCounter, Electorate, etc. dictate how those classes will act. If the code is changed to get info from hidden parameters or to ignore some of the terms, voters will be misled. ## Use a timer that is controlled by a party to the vote Everyone involved relies on the timers used to close voting being platform -timers, but the timers aren't self-revealing. Participants should compare the +timers, but timers aren't self-revealing. Participants should compare the timers to known platform-provided timers before relying on them. [A related bug has been filed](https://github.com/Agoric/agoric-sdk/issues/3748) -## Registrar allow unauthorized parties to cast ballots +## Electorate allow unauthorized parties to cast votes -Every registrar will have some notion of who the authorized voters are. They +Every electorate will have some notion of who the authorized voters are. They need to properly enforce that each voter can vote their weight once. The -current implementations only support equal weight ballots and known lists of -voters. Future Registrars and BallotCountes will support other models. The -combination of open-entry stake-holder votes with variable weight -ballotCounters will require even more diligence to ensure there are no avenues +initial implementation (Committee) supports equal weight votes and +known voters. Future Electorates and VoteCounters will support other models. +The combination of open-entry stake-holder votes with variable weight +voteCounters will require even more diligence to ensure there are no avenues for multiply counting votes. -## Registrar accidentally re-use a ballotCounter +## Electorate accidentally re-use a voteCounter -Each ballotCounter is intended to be used once. If there's a path that allows +Each voteCounter is intended to be used once. If there's a path that allows re-use, this would be a hazard. ## Does failed question creation throw detectably? @@ -118,12 +119,12 @@ than a weakness in the protocols or infrastructure. ## Get contractGovernor to leak paramManager private facet -The contractGovernor tries to ensure that the facet for calling `updateFoo()` +The contractGovernor needs to ensure that the facet for calling `updateFoo()` for a particular paramManager is only available in a visible way, but the code there is delicate. Is there a way to highjack the facet that wouldn't be detectable to voters or onlookers? -## Create ballot issue that claims to govern a contract it doesn't have control over +## Create question that claims to govern a contract it doesn't have control over It's possible to insert a layer between the contractGovernor and the paramManager or governedContract that allows external control of the @@ -135,7 +136,8 @@ obvious. Is there a way to evade that detection? ## Other Discrepancy between governedContract and ContractGovernor Are there ways to write governedContracts so they appear to be handled by -contractGovernor, but other intervention in the update parameters is possible? +contractGovernor, but other intervention in the update parameters calls is +possible? ## Can a cheating governor start up a contract with an invisible wrapper undetectably? diff --git a/packages/governance/docs/contractGovernance.png b/packages/governance/docs/contractGovernance.png new file mode 100644 index 0000000000000000000000000000000000000000..8bf5210d49bca0bccbcacda6411e24288bcd2d1e GIT binary patch literal 61990 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 z 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 { + Object ParamManager { + paramDesc[] { name, type, value } + -- + +getParams() + +getParam() + -updateFoo() + } + + class GovernedContract { + verifiable: Governor, params + -- + +terms: { electionManager, governedParams } + +getState() + +getContractGovernor() + -getParamManagerRetriever() + } + note left : calls buildParamManager(paramDesc);\nmakes paramMgr state public\nreturns paramMgr in creatorFacet +} + +class "ContractGovernor\n(an ElectionManager)" as ContractGovernor { + verifiable: governedInstance, electorateInstance + -- + +getElectorate() + +getGovernedContract() + +validateVoteCounter() + +validateElectorate() + +validateTimer() + -startGovernedInstance(electorate, governed, ...) +} +note left : ContractGovernor starts GovernedContract\nstartGovernedInstance() returns a tightly held facet\n with voteOnParamChange() for the creator. + +class Electorate { + Questions + === + -addQuestion() +} + +GovernedContract ..> ParamManager : creates > +GovernedContract --> ParamManager : access\nto params +ContractGovernor ..> GovernedContract : creates > +ContractGovernor --> Electorate +ContractGovernor ==> ParamManager : manages\nparams + +@enduml diff --git a/packages/governance/docs/coreArchitecture.png b/packages/governance/docs/coreArchitecture.png new file mode 100644 index 0000000000000000000000000000000000000000..0c15a79188ec0f1ffd55d8a4d4ec2395e23beace GIT binary patch literal 177482 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%0Aa^)yN8>**=_P)>yF?ks}GTP4uM2puKyiZ5cA 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

JMQ6pn;7N}rL&E&0!QwrxC*UvN+ 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 literal 0 HcmV?d00001 diff --git a/packages/governance/docs/example.puml b/packages/governance/docs/example.puml new file mode 100644 index 00000000000..a91fc36a2bf --- /dev/null +++ b/packages/governance/docs/example.puml @@ -0,0 +1,53 @@ +@startuml governance example + +package "Example with Vote Invitation" <> { + class ContractGovernor { + has a committee that wlll vote on questions. + can create binary (and other) questions. + } + + class BinaryVoteCounter { + quorumThreshold, issue + questionHandle, closingRule + -- + doesn't know who's voting. + knows how to count binary questions + } + + object "Question FeesTo2Percent" as FeesTo2Percent { + Contract + Issue: set fees at 2%? + Positions + } + + object governedContract { + ContractGovernor + } + + class TreasuryGovernanceElectorate1 { + Questions: FeesTo2Percent, ... + -- + distributed voterInvitations to creator. + doesn't know how questions are created. + } + + interface memberAVoterInvitation { + TreasuryGovernanceElectorate1 + } + + object memberAVoterFacet { + TreasuryGovernanceElectorate1 + -- + castBallotFor(questionHandle, [positions]) + } +} + +ContractGovernor --> BinaryVoteCounter : responds to\noutcome > +ContractGovernor -.[#blue]-|> TreasuryGovernanceElectorate1 : verifiable +memberAVoterInvitation --> memberAVoterFacet +memberAVoterFacet --> FeesTo2Percent + +ContractGovernor ==> governedContract : creates > +FeesTo2Percent => governedContract + +@enduml diff --git a/packages/governance/exported.js b/packages/governance/exported.js new file mode 100644 index 00000000000..f4cba017ea1 --- /dev/null +++ b/packages/governance/exported.js @@ -0,0 +1 @@ +import './src/types.js'; diff --git a/packages/governance/src/ballotBuilder.js b/packages/governance/src/ballotBuilder.js deleted file mode 100644 index ab0b5c1ff4f..00000000000 --- a/packages/governance/src/ballotBuilder.js +++ /dev/null @@ -1,84 +0,0 @@ -// @ts-check - -import { assert, details as X } from '@agoric/assert'; -import { Far } from '@agoric/marshal'; - -// CHOOSE_N: voter indicates up to N they find acceptable (N might be 1). -// ORDER: voter lists their choices from most to least favorite. -// WEIGHT: voter lists their choices, each with a numerical weight. High -// numbers are most preferred. - -/** - * @type {{ - * CHOOSE_N: 'choose_n', - * ORDER: 'order', - * WEIGHT: 'weight', - * }} - */ -const ChoiceMethod = { - CHOOSE_N: 'choose_n', - ORDER: 'order', - WEIGHT: 'weight', -}; - -const buildEqualWeightBallot = ( - method, - question, - positions, - maxChoices = 0, - instance, -) => { - const choose = chosenPositions => { - assert( - chosenPositions.length <= maxChoices, - X`only ${maxChoices} position(s) allowed`, - ); - assert( - chosenPositions.every(p => positions.includes(p)), - X`Some positions in ${chosenPositions} are not valid in ${positions}`, - ); - - /** @type {CompleteEqualWeightBallot} */ - return { question, chosen: chosenPositions }; - }; - - const getDetails = () => - harden({ - method, - question, - positions, - maxChoices, - instance, - }); - - return Far('ballot details', { - getBallotCounter: () => instance, - getDetails, - choose, - }); -}; - -/** @type {BuildBallot} */ -const buildBallot = (method, question, positions, maxChoices = 0, instance) => { - assert.typeof(question, 'string'); - - switch (method) { - case ChoiceMethod.CHOOSE_N: - return buildEqualWeightBallot( - method, - question, - positions, - maxChoices, - instance, - ); - case ChoiceMethod.ORDER: - case ChoiceMethod.WEIGHT: - throw Error(`choice method ${ChoiceMethod.WEIGHT} is unimplemented`); - default: - throw Error(`choice method unrecognized`); - } -}; - -harden(buildBallot); - -export { ChoiceMethod, buildBallot }; diff --git a/packages/governance/src/binaryBallotCounter.js b/packages/governance/src/binaryBallotCounter.js deleted file mode 100644 index 9320b70c1ef..00000000000 --- a/packages/governance/src/binaryBallotCounter.js +++ /dev/null @@ -1,202 +0,0 @@ -// @ts-check - -import { assert, details as X } from '@agoric/assert'; -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.js'; -import { scheduleClose } from './closingRule.js'; - -const makeWeightedBallot = (ballot, shares) => harden({ ballot, shares }); - -const makeQuorumCounter = quorumThreshold => { - const check = stats => { - const votes = stats.results.reduce( - (runningTotal, { total }) => runningTotal + total, - 0n, - ); - return votes >= quorumThreshold; - }; - /** @type {QuorumCounter} */ - return Far('checker', { check }); -}; - -// Exported for testing purposes -const makeBinaryBallotCounter = ( - question, - positions, - threshold, - tieOutcome = undefined, - closingRule, - instance, -) => { - assert( - positions.length === 2, - X`Binary ballots must have exactly two positions. had ${positions.length}: ${positions}`, - ); - assert.typeof(question, 'string'); - assert.typeof(positions[0], 'string'); - assert.typeof(positions[1], 'string'); - if (tieOutcome) { - assert( - positions.includes(tieOutcome), - X`The default outcome on a tie must be one of the positions, not ${tieOutcome}`, - ); - } - - const template = buildBallot( - ChoiceMethod.CHOOSE_N, - question, - positions, - 1, - instance, - ); - const ballotDetails = template.getDetails(); - - assert( - ballotDetails.method === ChoiceMethod.CHOOSE_N, - X`Binary ballot counter only works with CHOOSE_N`, - ); - let isOpen = true; - const outcomePromise = makePromiseKit(); - const tallyPromise = makePromiseKit(); - const allBallots = makeStore('seat'); - - 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 = () => { - assert(!isOpen, X`can't count votes while the election is open`); - - // ballot template has position choices; Each ballot in allBallots should - // match. count the valid ballots and report results. - let spoiled = 0n; - const tally = { - [positions[0]]: 0n, - [positions[1]]: 0n, - }; - - allBallots.values().forEach(({ ballot, shares }) => { - assert( - ballot.chosen.length === 1, - X`A binary ballot must contain exactly one choice.`, - ); - const choice = ballot.chosen[0]; - if (!ballotDetails.positions.includes(choice)) { - spoiled += shares; - } else { - tally[choice] += shares; - } - }); - - const stats = { - spoiled, - votes: allBallots.entries().length, - results: [ - { position: positions[0], total: tally[positions[0]] }, - { position: positions[1], total: tally[positions[1]] }, - ], - }; - tallyPromise.resolve(stats); - - if (!makeQuorumCounter(threshold).check(stats)) { - outcomePromise.reject('No quorum'); - return; - } - - if (tally[positions[0]] > tally[positions[1]]) { - outcomePromise.resolve(positions[0]); - } else if (tally[positions[1]] > tally[positions[0]]) { - outcomePromise.resolve(positions[1]); - } else { - outcomePromise.resolve(tieOutcome); - } - }; - - const closeVoting = () => { - isOpen = false; - countVotes(); - }; - - const sharedFacet = { - getBallotTemplate: () => template, - isOpen: () => isOpen, - getClosingRule: () => closingRule, - }; - - /** @type {VoterFacet} */ - const voterFacet = Far('voterFacet', { - ...sharedFacet, - submitVote: recordBallot, - }); - - // exposed for testing. In contracts, shouldn't be released. - const closeFacet = Far('closeFacet', { closeVoting }); - - /** @type {BallotCounterCreatorFacet} */ - const creatorFacet = Far('adminFacet', { - ...sharedFacet, - getVoterFacet: () => voterFacet, - }); - - const publicFacet = Far('preliminaryPublicFacet', { - ...sharedFacet, - getOutcome: () => outcomePromise.promise, - getStats: () => tallyPromise.promise, - }); - return { publicFacet, creatorFacet, closeFacet }; -}; - -const start = zcf => { - // 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 - // component. - // TODO(hibbert) checking the quorum should be pluggable and legible. - const { - question, - positions, - quorumThreshold, - tieOutcome, - closingRule, - } = zcf.getTerms(); - - // The closeFacet is exposed for testing, but doesn't escape from a contract - const { publicFacet, creatorFacet, closeFacet } = makeBinaryBallotCounter( - question, - positions, - quorumThreshold, - tieOutcome, - closingRule, - zcf.getInstance(), - ); - - scheduleClose(closingRule, closeFacet.closeVoting); - - /** @type {BallotCounterPublicFacet} */ - const publicFacetWithGetInstance = Far('publicFacet', { - ...publicFacet, - getInstance: zcf.getInstance, - }); - return { publicFacet: publicFacetWithGetInstance, creatorFacet }; -}; - -harden(start); -harden(makeBinaryBallotCounter); - -export { makeBinaryBallotCounter, start }; diff --git a/packages/governance/src/binaryVoteCounter.js b/packages/governance/src/binaryVoteCounter.js new file mode 100644 index 00000000000..a24ab07f603 --- /dev/null +++ b/packages/governance/src/binaryVoteCounter.js @@ -0,0 +1,199 @@ +// @ts-check + +import { Far } from '@agoric/marshal'; +import { makePromiseKit } from '@agoric/promise-kit'; +import { sameStructure } from '@agoric/same-structure'; +import { makeStore } from '@agoric/store'; + +import { + ChoiceMethod, + buildUnrankedQuestion, + positionIncluded, + looksLikeQuestionSpec, +} from './question.js'; +import { scheduleClose } from './closingRule.js'; + +const { details: X } = assert; + +const makeQuorumCounter = quorumThreshold => { + const check = stats => { + const votes = stats.results.reduce( + (runningTotal, { total }) => runningTotal + total, + 0n, + ); + return votes >= quorumThreshold; + }; + /** @type {QuorumCounter} */ + return Far('checker', { check }); +}; + +const validateBinaryQuestionSpec = questionSpec => { + looksLikeQuestionSpec(questionSpec); + + const positions = questionSpec.positions; + assert( + positions.length === 2, + X`Binary questions must have exactly two positions. had ${positions.length}: ${positions}`, + ); + + assert( + questionSpec.maxChoices === 1, + X`Can only choose 1 item on a binary question`, + ); + assert( + questionSpec.method === ChoiceMethod.UNRANKED, + X`${questionSpec.method} must be UNRANKED`, + ); + // We don't check the quorumRule or quorumThreshold here. The quorumThreshold + // is provided by the Electorate that creates this voteCounter, since only it + // can translate the quorumRule to a required number of votes. +}; + +// Notice that BinaryVoteCounter is designed to run as a Zoe contract. The +// business part of the contract is extracted here so it can be tested +// independently. The standard Zoe start function is at the bottom of this file. + +/** @type {BuildVoteCounter} */ +const makeBinaryVoteCounter = (questionSpec, threshold, instance) => { + validateBinaryQuestionSpec(questionSpec); + + const question = buildUnrankedQuestion(questionSpec, instance); + const details = question.getDetails(); + + let isOpen = true; + const positions = questionSpec.positions; + const outcomePromise = makePromiseKit(); + const tallyPromise = makePromiseKit(); + // The Electorate is responsible for creating a unique seat for each voter. + // This voteCounter allows voters to re-vote, and replaces their previous + // choice with the new selection. + + /** + * @typedef {Object} RecordedBallot + * @property {Position} chosen + * @property {bigint} shares + */ + /** @type {Store,RecordedBallot> } */ + const allBallots = makeStore('voterHandle'); + + /** @type {SubmitVote} */ + const submitVote = (voterHandle, chosenPositions, shares = 1n) => { + assert(chosenPositions.length === 1, 'only 1 position allowed'); + const [position] = chosenPositions; + assert( + positionIncluded(positions, position), + X`The specified choice is not a legal position: ${position}.`, + ); + + // CRUCIAL: If the voter cast a valid ballot, we'll record it, but we need + // to make sure that each voter's vote is recorded only once. + const completedBallot = harden({ chosen: position, shares }); + allBallots.has(voterHandle) + ? allBallots.set(voterHandle, completedBallot) + : allBallots.init(voterHandle, completedBallot); + }; + + const countVotes = () => { + assert(!isOpen, X`can't count votes while the election is open`); + + // question has position choices; Each ballot in allBallots should + // match. count the valid ballots and report results. + let spoiled = 0n; + const tally = [0n, 0n]; + + allBallots.values().forEach(({ chosen, shares }) => { + const choice = positions.findIndex(p => sameStructure(p, chosen)); + if (choice < 0) { + spoiled += shares; + } else { + tally[choice] += shares; + } + }); + + const stats = { + spoiled, + votes: allBallots.entries().length, + results: [ + { position: positions[0], total: tally[0] }, + { position: positions[1], total: tally[1] }, + ], + }; + + // CRUCIAL: countVotes only gets called once for each question. We want to + // ensure that tallyPromise and outcomePromise always get resolved. The + // tally gets the results regardless of the outcome. outcomePromise gets a + // different resolution depending on whether there was no quorum to make a + // decision, or the outcome is based on a majority either way, or a tie. + tallyPromise.resolve(stats); + + if (!makeQuorumCounter(threshold).check(stats)) { + outcomePromise.reject('No quorum'); + return; + } + + if (tally[0] > tally[1]) { + outcomePromise.resolve(positions[0]); + } else if (tally[1] > tally[0]) { + outcomePromise.resolve(positions[1]); + } else { + outcomePromise.resolve(questionSpec.tieOutcome); + } + }; + + const closeVoting = () => { + isOpen = false; + countVotes(); + }; + + // exposed for testing. In contracts, shouldn't be released. + /** @type {VoteCounterCloseFacet} */ + const closeFacet = Far('closeFacet', { closeVoting }); + + /** @type {VoteCounterCreatorFacet} */ + const creatorFacet = Far('VoteCounter vote Cap', { + submitVote, + }); + + /** @type {VoteCounterPublicFacet} */ + const publicFacet = Far('preliminaryPublicFacet', { + getQuestion: () => question, + isOpen: () => isOpen, + getOutcome: () => outcomePromise.promise, + getStats: () => tallyPromise.promise, + getDetails: () => details, + }); + return { publicFacet, creatorFacet, closeFacet }; +}; + +// The contract wrapper extracts the terms and runs makeBinaryVoteCounter(). +// It schedules the closing of the vote, finally inserting the contract +// instance in the publicFacet before returning public and creator facets. + +const start = zcf => { + // 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 + // component. + // TODO(hibbert) checking the quorum should be pluggable and legible. + const { questionSpec, quorumThreshold } = zcf.getTerms(); + // The closeFacet is exposed for testing, but doesn't escape from a contract + const { publicFacet, creatorFacet, closeFacet } = makeBinaryVoteCounter( + questionSpec, + quorumThreshold, + zcf.getInstance(), + ); + + scheduleClose(questionSpec.closingRule, closeFacet.closeVoting); + + /** @type {VoteCounterPublicFacet} */ + const publicFacetWithGetInstance = Far('publicFacet', { + ...publicFacet, + getInstance: zcf.getInstance, + }); + return { publicFacet: publicFacetWithGetInstance, creatorFacet }; +}; + +harden(start); +harden(makeBinaryVoteCounter); + +export { makeBinaryVoteCounter, start }; diff --git a/packages/governance/src/closingRule.js b/packages/governance/src/closingRule.js index f2999854b1a..61c55852677 100644 --- a/packages/governance/src/closingRule.js +++ b/packages/governance/src/closingRule.js @@ -4,8 +4,8 @@ // emergency votes that can close as soon as a quorum or other threshold is // reached. -import { Far } from '@agoric/marshal'; import { E } from '@agoric/eventual-send'; +import { Far } from '@agoric/marshal'; /** @type {CloseVoting} */ export const scheduleClose = (closingRule, closeVoting) => { diff --git a/packages/governance/src/committee.js b/packages/governance/src/committee.js new file mode 100644 index 00000000000..295260f5c81 --- /dev/null +++ b/packages/governance/src/committee.js @@ -0,0 +1,144 @@ +// @ts-check + +import { E } from '@agoric/eventual-send'; +import { Far } from '@agoric/marshal'; +import { makeSubscriptionKit } from '@agoric/notifier'; +import { allComparable } from '@agoric/same-structure'; +import { makeStore } from '@agoric/store'; +import { natSafeMath } from '@agoric/zoe/src/contractSupport/index.js'; + +import { makeHandle } from '@agoric/zoe/src/makeHandle'; +import { QuorumRule } from './question.js'; + +const { ceilDivide } = natSafeMath; + +/** + * Each Committee (an Electorate) represents a particular set of voters. The + * number of voters is visible in the terms. + * + * This contract creates an electorate whose membership is not visible to + * observers. There may be uses for such a structure, but it is not appropriate + * for elections where the set of voters needs to be known, unless the contract + * is used in a way that makes the distribution of voter facets visible. + * + * @type {ContractStartFn} + */ +const start = zcf => { + /** + * @typedef {Object} QuestionRecord + * @property {ERef} voteCap + * @property {VoteCounterPublicFacet} publicFacet + */ + + /** @type {Store, QuestionRecord>} */ + const allQuestions = makeStore('Question'); + const { subscription, publication } = makeSubscriptionKit(); + + const getOpenQuestions = async () => { + const isOpenPQuestions = allQuestions.keys().map(key => { + const { publicFacet } = allQuestions.get(key); + return [E(publicFacet).isOpen(), key]; + }); + + /** @type {[boolean, Handle<'Question'>][]} */ + const isOpenQuestions = await allComparable(harden(isOpenPQuestions)); + return isOpenQuestions + .filter(([open, _key]) => open) + .map(([_open, key]) => key); + }; + + const makeCommitteeVoterInvitation = index => { + /** @type {OfferHandler} */ + const offerHandler = Far('voter offerHandler', () => { + const voterHandle = makeHandle('Voter'); + return Far(`voter${index}`, { + // CRUCIAL: voteCap carries the ability to cast votes for any voter at + // any weight. It's wrapped here and given to the voter. + // + // Ensure that the voter can't get access to the unwrapped voteCap, and + // has no control over the voteHandle or weight + castBallotFor: (questionHandle, positions) => { + const { voteCap } = allQuestions.get(questionHandle); + return E(voteCap).submitVote(voterHandle, positions, 1n); + }, + }); + }); + + // https://github.com/Agoric/agoric-sdk/pull/3448/files#r704003612 + // This will produce unique descriptions because + // makeCommitteeVoterInvitation() is only called within the following loop, + // which is only called once per Electorate. + return zcf.makeInvitation(offerHandler, `Voter${index}`); + }; + + const { committeeName, committeeSize } = zcf.getTerms(); + + const invitations = harden( + [...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`); + } + }; + + /** @type {QuestionTerms} */ + const voteCounterTerms = { + questionSpec, + electorate: zcf.getInstance(), + quorumThreshold: quorumThreshold(questionSpec.quorumRule), + }; + + // facets of the vote counter. creatorInvitation and adminFacet not used + const { creatorFacet, publicFacet, instance } = await E( + zcf.getZoeService(), + ).startInstance(voteCounter, {}, voteCounterTerms); + const details = await E(publicFacet).getDetails(); + const voteCounterFacets = { voteCap: creatorFacet, publicFacet }; + allQuestions.init(details.questionHandle, voteCounterFacets); + + publication.updateState(details); + return { creatorFacet, publicFacet, instance }; + }; + + /** @type {ElectoratePublic} */ + const publicFacet = Far('publicFacet', { + getQuestionSubscription: () => subscription, + getOpenQuestions, + getName: () => committeeName, + getInstance: zcf.getInstance, + getQuestion: questionHandleP => + E.when(questionHandleP, questionHandle => + E(allQuestions.get(questionHandle).publicFacet).getQuestion(), + ), + }); + + const getPoserInvitation = () => { + const questionPoserHandler = () => Far(`questionPoser`, { addQuestion }); + return zcf.makeInvitation(questionPoserHandler, `questionPoser`); + }; + + /** @type {ElectorateCreatorFacet} */ + const creatorFacet = Far('adminFacet', { + getPoserInvitation, + addQuestion, + getVoterInvitations: () => invitations, + getQuestionSubscription: () => subscription, + getPublicFacet: () => publicFacet, + }); + + return { publicFacet, creatorFacet }; +}; + +harden(start); +export { start }; diff --git a/packages/governance/src/committeeRegistrar.js b/packages/governance/src/committeeRegistrar.js deleted file mode 100644 index c6d1309dfcf..00000000000 --- a/packages/governance/src/committeeRegistrar.js +++ /dev/null @@ -1,92 +0,0 @@ -// @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 = Far('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); - 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 }; diff --git a/packages/governance/src/contractGovernor.js b/packages/governance/src/contractGovernor.js new file mode 100644 index 00000000000..c6d7cd8dce4 --- /dev/null +++ b/packages/governance/src/contractGovernor.js @@ -0,0 +1,177 @@ +// @ts-check + +import { E } from '@agoric/eventual-send'; +import { Far } from '@agoric/marshal'; + +import { setupGovernance, validateParamChangeQuestion } from './governParam.js'; + +const { details: X } = assert; + +/** @type {ValidateQuestionDetails} */ +const validateQuestionDetails = async (zoe, electorate, details) => { + const { + counterInstance, + issue: { contract: governedInstance }, + } = details; + validateParamChangeQuestion(details); + + const governorInstance = await E.get(E(zoe).getTerms(governedInstance)) + .electionManager; + const governorPublic = E(zoe).getPublicFacet(governorInstance); + + return Promise.all([ + E(governorPublic).validateVoteCounter(counterInstance), + E(governorPublic).validateElectorate(electorate), + E(governorPublic).validateTimer(details), + ]); +}; + +/** @type {ValidateQuestionFromCounter} */ +const validateQuestionFromCounter = async (zoe, electorate, voteCounter) => { + const counterPublicP = E(zoe).getPublicFacet(voteCounter); + const questionDetails = await E(counterPublicP).getDetails(); + + return validateQuestionDetails(zoe, electorate, questionDetails); +}; + +/* + * ContractManager is an ElectionManager that starts up a contract and hands its + * own creator a facet that allows them to call for votes on parameters that + * were declared by the contract. + * + * The terms for this contract include the Timer, Electorate and + * the Installation to be started, as well as an issuerKeywordRecord or terms + * needed by the governed contract. Those details for the governed contract are + * included in this contract's terms as a "governed" record. + * + * terms = { + * timer, + * electorateInstance, + * governedContractInstallation, + * governed: { + * issuerKeywordRecord: governedIssuerKeywordRecord, + * terms: governedTerms, + * }, + * }; + * + * The governedContract is responsible for supplying getParamMgrRetriever() in + * its creatorFacet. getParamMgrRetriever() takes a ParamSpecification, which + * identifies the parameter to be voted on. A minimal ParamSpecification + * specifies the key which identifies a particular paramManager (even if there's + * only one) and the parameterName. The interpretation of ParamSpecification is + * up to the contract. + * + * The contractGovenor creatorFacet includes voteOnParamChange(), + * which is used to create questions that will automatically update + * contract parameters if passed. This facet will usually be closely held. The + * creatorFacet can also be used to retrieve the governed instance, publicFacet, + * and it's creatorFacet with voteOnParamChange() omitted. + * + * The governed contract's terms include the instance of this (governing) + * contract (as electionManager) so clients will be able to look up the state + * of the governed parameters. + * + * @type {ContractStartFn} + */ +const start = async (zcf, privateArgs) => { + const zoe = zcf.getZoeService(); + const { + timer, + electorateInstance, + governedContractInstallation, + governed: { + issuerKeywordRecord: governedIssuerKeywordRecord, + terms: governedTerms, + privateArgs: privateContractArgs, + }, + } = /** @type {ContractGovernorTerms} */ zcf.getTerms(); + + const { electorateCreatorFacet } = privateArgs; + + const augmentedTerms = harden({ + ...governedTerms, + electionManager: zcf.getInstance(), + }); + const poserInvitation = E(electorateCreatorFacet).getPoserInvitation(); + + const [ + { + creatorFacet: governedCF, + instance: governedInstance, + publicFacet: governedPF, + }, + invitationDetails, + ] = await Promise.all([ + E(zoe).startInstance( + governedContractInstallation, + governedIssuerKeywordRecord, + augmentedTerms, + privateContractArgs, + ), + E(zoe).getInvitationDetails(poserInvitation), + ]); + + assert( + invitationDetails.instance === electorateInstance, + X`questionPoserInvitation didn't match supplied Electorate`, + ); + + // CRUCIAL: only governedContract should get the ability to update params + /** @type {Promise} */ + const limitedCreatorFacet = E(governedCF).getLimitedCreatorFacet(); + + const { voteOnParamChange, createdQuestion } = await setupGovernance( + E(governedCF).getParamMgrRetriever(), + E(E(zoe).offer(poserInvitation)).getOfferResult(), + governedInstance, + timer, + ); + + const validateVoteCounter = async voteCounter => { + const created = await E(createdQuestion)(voteCounter); + assert(created, X`VoteCounter was not created by this contractGovernor`); + return true; + }; + + const validateTimer = details => { + assert( + details.closingRule.timer === timer, + X`closing rule must use my timer`, + ); + return true; + }; + + const validateElectorate = async regP => { + return E.when(regP, reg => { + assert( + reg === electorateInstance, + X`Electorate doesn't match my Electorate`, + ); + return true; + }); + }; + + /** @type {GovernedContractFacetAccess} */ + const creatorFacet = Far('governor creatorFacet', { + voteOnParamChange, + getCreatorFacet: () => limitedCreatorFacet, + getInstance: () => governedInstance, + getPublicFacet: () => governedPF, + }); + + /** @type {GovernorPublic} */ + const publicFacet = Far('contract governor public', { + getElectorate: () => electorateInstance, + getGovernedContract: () => governedInstance, + validateVoteCounter, + validateElectorate, + validateTimer, + }); + + return { creatorFacet, publicFacet }; +}; + +harden(start); +harden(validateQuestionDetails); +harden(validateQuestionFromCounter); +export { start, validateQuestionDetails, validateQuestionFromCounter }; diff --git a/packages/governance/src/contractHelper.js b/packages/governance/src/contractHelper.js new file mode 100644 index 00000000000..534f829f6fe --- /dev/null +++ b/packages/governance/src/contractHelper.js @@ -0,0 +1,65 @@ +// @ts-check + +import { Far } from '@agoric/marshal'; +import { sameStructure } from '@agoric/same-structure'; + +import { buildParamManager } from './paramManager.js'; + +const { details: X, quote: q } = assert; + +/** + * Helper for the 90% of contracts that will have only a single set of + * parameters. In order to support managed parameters, a contract only has to + * * define the parameter template, which includes name, type and value + * * call handleParamGovernance() to get makePublicFacet and makeCreatorFacet + * * add any methods needed in the public and creator facets. + * + * @type {HandleParamGovernance} + */ +const handleParamGovernance = (zcf, governedParamsTemplate) => { + const terms = zcf.getTerms(); + /** @type {ParamDescriptions} */ + const governedParams = terms.main; + const { electionManager } = terms; + + assert( + sameStructure(governedParams, governedParamsTemplate), + X`Terms must include ${q(governedParamsTemplate)}, but were ${q( + governedParams, + )}`, + ); + const paramManager = buildParamManager(governedParams); + + const makePublicFacet = (originalPublicFacet = {}) => { + return Far('publicFacet', { + ...originalPublicFacet, + getSubscription: () => paramManager.getSubscription(), + getContractGovernor: () => electionManager, + getGovernedParamsValues: () => { + return { main: paramManager.getParams() }; + }, + }); + }; + + /** @type {LimitedCreatorFacet} */ + const limitedCreatorFacet = Far('governedContract creator facet', { + getContractGovernor: () => electionManager, + }); + + const makeCreatorFacet = (originalCreatorFacet = Far('creatorFacet', {})) => { + return Far('creatorFacet', { + getParamMgrRetriever: () => { + return Far('paramRetriever', { get: () => paramManager }); + }, + getInternalCreatorFacet: () => originalCreatorFacet, + getLimitedCreatorFacet: () => limitedCreatorFacet, + }); + }; + + return harden({ + makePublicFacet, + makeCreatorFacet, + }); +}; +harden(handleParamGovernance); +export { handleParamGovernance }; diff --git a/packages/governance/src/exported.js b/packages/governance/src/exported.js new file mode 100644 index 00000000000..90d2c4eeb4a --- /dev/null +++ b/packages/governance/src/exported.js @@ -0,0 +1 @@ +import './types'; diff --git a/packages/governance/src/governParam.js b/packages/governance/src/governParam.js new file mode 100644 index 00000000000..faa1f7f9a5f --- /dev/null +++ b/packages/governance/src/governParam.js @@ -0,0 +1,157 @@ +// @ts-check + +import { E } from '@agoric/eventual-send'; +import { Far } from '@agoric/marshal'; +import { makePromiseKit } from '@agoric/promise-kit'; +import { sameStructure } from '@agoric/same-structure'; + +import { q } from '@agoric/assert'; +import { + ChoiceMethod, + QuorumRule, + ElectionType, + looksLikeQuestionSpec, +} from './question.js'; +import { assertType } from './paramManager.js'; + +const { details: X } = assert; + +/** @type {MakeParamChangePositions} */ +const makeParamChangePositions = (paramSpec, proposedValue) => { + const positive = harden({ changeParam: paramSpec, proposedValue }); + const negative = harden({ noChange: paramSpec }); + return { positive, negative }; +}; + +/** @type {ValidateParamChangeQuestion} */ +const validateParamChangeQuestion = details => { + assert( + details.method === ChoiceMethod.UNRANKED, + X`ChoiceMethod must be UNRANKED, not ${details.method}`, + ); + assert( + details.electionType === ElectionType.PARAM_CHANGE, + X`ElectionType must be PARAM_CHANGE, not ${details.electionType}`, + ); + assert( + details.maxChoices === 1, + X`maxChoices must be 1, not ${details.maxChoices}`, + ); + assert( + details.quorumRule === QuorumRule.MAJORITY, + X`QuorumRule must be MAJORITY, not ${details.quorumRule}`, + ); + assert( + details.tieOutcome.noChange, + X`tieOutcome must be noChange, not ${details.tieOutcome}`, + ); +}; + +/** @type {AssertBallotConcernsQuestion} */ +const assertBallotConcernsQuestion = (paramName, questionDetails) => { + assert( + // @ts-ignore typescript isn't sure the question is a paramChangeIssue + // if it isn't, the assertion will fail. + questionDetails.issue.paramSpec.parameterName === paramName, + X`expected ${q(paramName)} to be included`, + ); +}; + +/** @type {SetupGovernance} */ +const setupGovernance = async ( + paramManagerRetriever, + poserFacet, + contractInstance, + timer, +) => { + /** @type {WeakSet} */ + const voteCounters = new WeakSet(); + + /** @type {VoteOnParamChange} */ + const voteOnParamChange = async ( + paramSpec, + proposedValue, + voteCounterInstallation, + deadline, + ) => { + const paramMgr = E(paramManagerRetriever).get(paramSpec); + const paramName = paramSpec.parameterName; + const param = await E(paramMgr).getParam(paramName); + assertType(param.type, proposedValue, paramName); + const outcomeOfUpdateP = makePromiseKit(); + + const { positive, negative } = makeParamChangePositions( + paramSpec, + proposedValue, + ); + const issue = harden({ + paramSpec, + contract: contractInstance, + proposedValue, + }); + const questionSpec = looksLikeQuestionSpec({ + method: ChoiceMethod.UNRANKED, + issue, + positions: [positive, negative], + electionType: ElectionType.PARAM_CHANGE, + maxChoices: 1, + closingRule: { timer, deadline }, + quorumRule: QuorumRule.MAJORITY, + tieOutcome: negative, + }); + + const { publicFacet: counterPublicFacet, instance: voteCounter } = await E( + poserFacet, + ).addQuestion(voteCounterInstallation, questionSpec); + + voteCounters.add(voteCounter); + + // CRUCIAL: Here we wait for the voteCounter to declare an outcome, and then + // attempt to update the value of the parameter if that's what the vote + // decided. We need to make sure that outcomeOfUpdateP is updated whatever + // happens. + // * If the vote was negative, resolve to the outcome + // * If we update the value, say so + // * If the update fails, reject the promise + // * if the vote outcome failed, reject the promise. + E(counterPublicFacet) + .getOutcome() + .then(outcome => { + if (sameStructure(positive, outcome)) { + E(paramMgr) + [`update${paramName}`](proposedValue) + .then(newValue => outcomeOfUpdateP.resolve(newValue)) + .catch(e => { + outcomeOfUpdateP.reject(e); + }); + } else { + outcomeOfUpdateP.resolve(negative); + } + }) + .catch(e => { + outcomeOfUpdateP.reject(e); + }); + + return { + outcomeOfUpdate: outcomeOfUpdateP.promise, + instance: voteCounter, + details: E(counterPublicFacet).getDetails(), + }; + }; + + return Far('paramGovernor', { + voteOnParamChange, + createdQuestion: b => voteCounters.has(b), + }); +}; + +harden(setupGovernance); +harden(makeParamChangePositions); +harden(validateParamChangeQuestion); +harden(assertBallotConcernsQuestion); +export { + setupGovernance, + makeParamChangePositions, + validateParamChangeQuestion, + assertBallotConcernsQuestion, +}; diff --git a/packages/governance/src/index.js b/packages/governance/src/index.js new file mode 100644 index 00000000000..b418b12b438 --- /dev/null +++ b/packages/governance/src/index.js @@ -0,0 +1,31 @@ +// @ts-check + +export { + ChoiceMethod, + ElectionType, + QuorumRule, + looksLikeQuestionSpec, + positionIncluded, + looksLikeIssueForType, + buildUnrankedQuestion, +} from './question.js'; + +export { + validateQuestionDetails, + validateQuestionFromCounter, +} from './contractGovernor.js'; + +export { handleParamGovernance } from './contractHelper.js'; + +export { + makeParamChangePositions, + validateParamChangeQuestion, + assertBallotConcernsQuestion, +} from './governParam.js'; + +export { ParamType, assertType } from './paramManager.js'; + +export { + assertContractGovernance, + assertContractElectorate, +} from './validators.js'; diff --git a/packages/governance/src/internalTypes.js b/packages/governance/src/internalTypes.js new file mode 100644 index 00000000000..06df0008409 --- /dev/null +++ b/packages/governance/src/internalTypes.js @@ -0,0 +1,6 @@ +/** + * @typedef {Object} QuestionRecord + * @property {ERef} voteCap + * @property {VoteCounterPublicFacet} publicFacet + * @property {Timestamp} deadline + */ diff --git a/packages/governance/src/paramManager.js b/packages/governance/src/paramManager.js index 5530bc69274..91ee435dce4 100644 --- a/packages/governance/src/paramManager.js +++ b/packages/governance/src/paramManager.js @@ -1,11 +1,13 @@ // @ts-check -import { assert, details as X } from '@agoric/assert'; import { assertIsRatio } from '@agoric/zoe/src/contractSupport/index.js'; import { AmountMath, looksLikeBrand } from '@agoric/ertp'; import { Far } from '@agoric/marshal'; import { assertKeywordName } from '@agoric/zoe/src/cleanProposal.js'; import { Nat } from '@agoric/nat'; +import { makeSubscriptionKit } from '@agoric/notifier'; + +const { details: X } = assert; /** * @type {{ @@ -18,6 +20,10 @@ import { Nat } from '@agoric/nat'; * STRING: 'string', * UNKNOWN: 'unknown', * }} + * + * UNKNOWN is an escape hatch for types we haven't added yet. If you are + * developing a new contract and use UNKNOWN, please also file an issue to ask + * us to support the new type. */ const ParamType = { AMOUNT: 'amount', @@ -29,16 +35,18 @@ const ParamType = { STRING: 'string', UNKNOWN: 'unknown', }; -harden(ParamType); +/** @type {AssertParamManagerType} */ const assertType = (type, value, name) => { switch (type) { case ParamType.AMOUNT: // It would be nice to have a clean way to assert something is an amount. + // @ts-ignore value is undifferentiated to this point AmountMath.coerce(value.brand, value); break; case ParamType.BRAND: assert( + // @ts-ignore value is undifferentiated to this point looksLikeBrand(value), X`value for ${name} must be a brand, was ${value}`, ); @@ -46,7 +54,8 @@ const assertType = (type, value, name) => { case ParamType.INSTALLATION: // TODO(3344): add a better assertion once Zoe validates installations assert( - typeof value === 'object' && !Object.getOwnPropertyNames(value).length, + typeof value === 'object' && + Object.getOwnPropertyNames(value).length === 1, X`value for ${name} must be an Installation, was ${value}`, ); break; @@ -67,8 +76,6 @@ const assertType = (type, value, name) => { case ParamType.STRING: assert.typeof(value, 'string'); break; - // This is an escape hatch for types we haven't added yet. If you need to - // use it, please file an issue and ask us to support the new type. case ParamType.UNKNOWN: break; default: @@ -76,12 +83,13 @@ const assertType = (type, value, name) => { } }; -const parse = paramDesc => { +/** @type {BuildParamManager} */ +const buildParamManager = paramDescriptions => { const typesAndValues = {}; - // manager has an updateFoo() for each Foo param. It will be returned. - const manager = {}; - - paramDesc.forEach(({ name, value, type }) => { + // updateFns will have updateFoo() for each Foo param. + const updateFns = {}; + const { publication, subscription } = makeSubscriptionKit(); + paramDescriptions.forEach(({ name, value, type }) => { // we want to create function names like updateFeeRatio(), so we insist that // the name has Keyword-nature. assertKeywordName(name); @@ -93,37 +101,47 @@ const parse = paramDesc => { assertType(type, value, name); typesAndValues[name] = { type, value }; - manager[`update${name}`] = newValue => { + + // CRUCIAL: here we're creating the update functions that can change the + // values of the governed contract's parameters. We'll return the updateFns + // to our caller. They must handle them carefully to ensure that they end up + // in appropriate hands. + updateFns[`update${name}`] = newValue => { assertType(type, newValue, name); typesAndValues[name].value = newValue; + + publication.updateState({ name, type, value }); + return newValue; }; }); + const makeDescription = name => ({ + name, + type: typesAndValues[name].type, + value: typesAndValues[name].value, + }); const getParams = () => { /** @type {Record} */ const descriptions = {}; Object.getOwnPropertyNames(typesAndValues).forEach(name => { - descriptions[name] = { - name, - type: typesAndValues[name].type, - value: typesAndValues[name].value, - }; + descriptions[name] = makeDescription(name); }); return harden(descriptions); }; + const getParam = name => harden(makeDescription(name)); - return { getParams, manager }; -}; - -/** @type {BuildParamManager} */ -const buildParamManager = paramDesc => { - const { getParams, manager } = parse(paramDesc); - + // CRUCIAL: Contracts that call buildParamManager should only export the + // resulting paramManager to their creatorFacet, where it will be picked up by + // contractGovernor. The getParams method can be shared widely. return Far('param manager', { getParams, - ...manager, + getSubscription: () => subscription, + getParam, + ...updateFns, }); }; -harden(buildParamManager); -export { ParamType, buildParamManager }; +harden(ParamType); +harden(buildParamManager); +harden(assertType); +export { ParamType, buildParamManager, assertType }; diff --git a/packages/governance/src/question.js b/packages/governance/src/question.js new file mode 100644 index 00000000000..119a54998da --- /dev/null +++ b/packages/governance/src/question.js @@ -0,0 +1,205 @@ +// @ts-check + +import { Far, passStyleOf } from '@agoric/marshal'; +import { sameStructure } from '@agoric/same-structure'; +import { makeHandle } from '@agoric/zoe/src/makeHandle.js'; +import { Nat } from '@agoric/nat'; + +import { assertType, ParamType } from './paramManager.js'; + +const { details: X, quote: q } = assert; + +// Topics being voted on are 'Questions'. Before a Question is known to a +// electorate, the parameters can be described with a QuestionSpec. Once the +// question has been presented to an Electorate, there is a QuestionDetails +// record that also includes the VoteCounter which will determine the outcome +// and the questionHandle that uniquely identifies it. + +/** + * "unranked" is more formally known as "approval" voting, but this is hard for + * people to intuit when there are only two alternatives. + * + * @type {{ + * UNRANKED: 'unranked', + * ORDER: 'order', + * }} + */ +const ChoiceMethod = { + UNRANKED: 'unranked', + ORDER: 'order', +}; + +/** @type {{ + * PARAM_CHANGE: 'param_change', + * ELECTION: 'election', + * SURVEY: 'survey', + * }} + */ +const ElectionType = { + // A parameter is named, and a new value proposed + PARAM_CHANGE: 'param_change', + // choose one or multiple winners, depending on ChoiceMethod + ELECTION: 'election', + SURVEY: 'survey', +}; + +/** @type {{ + * MAJORITY: 'majority', + * NO_QUORUM: 'no_quorum', + * ALL: 'all', + * }} + */ +const QuorumRule = { + MAJORITY: 'majority', + NO_QUORUM: 'no_quorum', + // The election isn't valid unless all voters vote + ALL: 'all', +}; + +/** @type {LooksLikeSimpleIssue} */ +const looksLikeSimpleIssue = issue => { + assert.typeof(issue, 'object', X`Issue ("${issue}") must be a record`); + assert( + issue && typeof issue.text === 'string', + X`Issue ("${issue}") must be a record with text: aString`, + ); + return undefined; +}; + +/** @type {LooksLikeParamChangeIssue} */ +const looksLikeParamChangeIssue = issue => { + assert(issue, X`argument to looksLikeParamChangeIssue cannot be null`); + assert.typeof(issue, 'object', X`Issue ("${issue}") must be a record`); + assert.typeof( + issue && issue.paramSpec, + 'object', + X`Issue ("${issue}") must be a record with paramSpec: anObject`, + ); + assert(issue && issue.proposedValue); + assertType(ParamType.INSTANCE, issue.contract, 'contract'); +}; + +/** @type {LooksLikeIssueForType} */ +const looksLikeIssueForType = (electionType, issue) => { + assert( + passStyleOf(issue) === 'copyRecord', + X`A question can only be a pass-by-copy record: ${issue}`, + ); + + switch (electionType) { + case ElectionType.SURVEY: + case ElectionType.ELECTION: + looksLikeSimpleIssue(/** @type {SimpleIssue} */ (issue)); + break; + case ElectionType.PARAM_CHANGE: + looksLikeParamChangeIssue(/** @type {ParamChangeIssue} */ (issue)); + break; + default: + throw Error(`Election type unrecognized`); + } +}; + +/** @type {PositionIncluded} */ +const positionIncluded = (positions, p) => + positions.some(e => sameStructure(e, p)); + +// QuestionSpec contains the subset of QuestionDetails that can be specified before +/** @type {LooksLikeClosingRule} */ +function looksLikeClosingRule(closingRule) { + assert(closingRule, X`argument to looksLikeClosingRule cannot be null`); + assert.typeof( + closingRule, + 'object', + X`ClosingRule ("${closingRule}") must be a record`, + ); + Nat(closingRule && closingRule.deadline); + const timer = closingRule && closingRule.timer; + assert(passStyleOf(timer) === 'remotable', X`Timer must be a timer ${timer}`); +} + +const assertEnumIncludes = (enumeration, value, name) => { + assert( + Object.getOwnPropertyNames(enumeration) + .map(k => enumeration[k]) + .includes(value), + X`Illegal ${name}: ${value}`, + ); +}; + +/** @type {LooksLikeQuestionSpec} */ +const looksLikeQuestionSpec = ({ + method, + issue, + positions, + electionType, + maxChoices, + closingRule, + quorumRule, + tieOutcome, +}) => { + looksLikeIssueForType(electionType, issue); + + assert( + positions.every( + p => passStyleOf(p) === 'copyRecord', + X`positions must be records`, + ), + ); + assert( + positionIncluded(positions, tieOutcome), + X`tieOutcome must be a legal position: ${q(tieOutcome)}`, + ); + assertEnumIncludes(QuorumRule, quorumRule, 'QuorumRule'); + assertEnumIncludes(ElectionType, electionType, 'ElectionType'); + assertEnumIncludes(ChoiceMethod, method, 'ChoiceMethod'); + assert(maxChoices > 0, X`maxChoices must be positive: ${maxChoices}`); + assert(maxChoices <= positions.length, X`Choices must not exceed length`); + + looksLikeClosingRule(closingRule); + + return harden({ + method, + issue, + positions, + maxChoices: Number(maxChoices), + electionType, + closingRule, + quorumRule, + tieOutcome, + }); +}; + +/** @type {BuildUnrankedQuestion} */ +const buildUnrankedQuestion = (questionSpec, counterInstance) => { + const questionHandle = makeHandle('Question'); + + const getDetails = () => + harden({ + ...questionSpec, + questionHandle, + counterInstance, + }); + + /** @type {Question} */ + return Far('question details', { + getVoteCounter: () => counterInstance, + getDetails, + }); +}; + +harden(ChoiceMethod); +harden(QuorumRule); +harden(ElectionType); +harden(looksLikeIssueForType); +harden(positionIncluded); +harden(buildUnrankedQuestion); + +export { + ChoiceMethod, + ElectionType, + QuorumRule, + looksLikeQuestionSpec, + positionIncluded, + looksLikeIssueForType, + buildUnrankedQuestion, +}; diff --git a/packages/governance/src/registrarTools.js b/packages/governance/src/registrarTools.js new file mode 100644 index 00000000000..41847bc266d --- /dev/null +++ b/packages/governance/src/registrarTools.js @@ -0,0 +1,64 @@ +// @ts-check + +import { E } from '@agoric/eventual-send'; +import { allComparable } from '@agoric/same-structure'; +import { Far } from '@agoric/marshal'; + +const startCounter = async ( + zcf, + questionSpec, + quorumThreshold, + voteCounter, + questionStore, + publication, +) => { + const ballotCounterTerms = { + questionSpec, + electorate: zcf.getInstance(), + quorumThreshold, + }; + + // facets of the voteCounter. creatorInvitation and adminFacet not used + const { creatorFacet, publicFacet, instance } = await E( + zcf.getZoeService(), + ).startInstance(voteCounter, {}, ballotCounterTerms); + const details = await E(publicFacet).getDetails(); + const { deadline } = questionSpec.closingRule; + publication.updateState(details); + const questionHandle = details.questionHandle; + + const voteCounterFacets = { voteCap: creatorFacet, publicFacet, deadline }; + + questionStore.init(questionHandle, voteCounterFacets); + + return { creatorFacet, publicFacet, instance, deadline, questionHandle }; +}; + +const getOpenQuestions = async questionStore => { + const isOpenPQuestions = questionStore.keys().map(key => { + const { publicFacet } = questionStore.get(key); + return [E(publicFacet).isOpen(), key]; + }); + + const isOpenQuestions = await allComparable(harden(isOpenPQuestions)); + return isOpenQuestions + .filter(([open, _key]) => open) + .map(([_open, key]) => key); +}; + +const getQuestion = (questionHandleP, questionStore) => + E.when(questionHandleP, questionHandle => + E(questionStore.get(questionHandle).publicFacet).getQuestion(), + ); + +const getPoserInvitation = (zcf, addQuestion) => { + const questionPoserHandler = () => Far(`questionPoser`, { addQuestion }); + return zcf.makeInvitation(questionPoserHandler, `questionPoser`); +}; + +harden(startCounter); +harden(getOpenQuestions); +harden(getQuestion); +harden(getPoserInvitation); + +export { startCounter, getOpenQuestions, getQuestion, getPoserInvitation }; diff --git a/packages/governance/src/types.js b/packages/governance/src/types.js index cef25746770..bfbd84ab595 100644 --- a/packages/governance/src/types.js +++ b/packages/governance/src/types.js @@ -1,75 +1,139 @@ // @ts-check /** - * @typedef { 'amount' | 'brand' | 'installation' | 'instance' | 'nat' | 'ratio' | 'string' | 'unknown' } ParamType + * @typedef { 'unranked' | 'order' } ChoiceMethod + * * UNRANKED: "unranked voting" means that the voter specifies some number of + * positions, and is endorsing them equally. + * * ORDER: The voter assigns ordinal numbers to some of the positions. The + * positions will be treated as an ordered list with no gaps. + * + * When voters are limited to choosing a single candidate, either UNRANKED or + * ORDER would work. UNRANKED has a simpler representation so we use that. */ /** - * @typedef { Amount | Brand | Installation | Instance | bigint | Ratio | string | unknown } ParamValue + * @typedef { 'param_change' | 'election' | 'survey' } ElectionType + * param_change is very specific. Survey means multiple answers are possible, + * Election means some candidates are going to "win". It's not clear these are + * orthogonal. The important distinction is that param_change has a structured + * issue, while the others have a issue presented as a string. */ /** - * @typedef { 'choose_n' | 'order' | 'weight' } ChoiceMethod + * @typedef { 'amount' | 'brand' | 'instance' | 'installation' | 'nat' | + * 'ratio' | 'string' | 'unknown' } ParamType */ /** - * @typedef {Object} ParamDescription - * @property {string} name - * @property {ParamValue} value - * @property {ParamType} type + * @typedef { 'majority' | 'all' | 'no_quorum' } QuorumRule */ /** - * @typedef {Object} ParamManagerBase - * @property {() => Record} getParams - * - * @typedef {{ [updater: string]: (arg: ParamValue) => void }} ParamManagerUpdaters - * @typedef {ParamManagerBase & ParamManagerUpdaters} ParamManagerFull + * @typedef {Object} SimpleIssue + * @property {string} text */ /** - * @typedef {Array} ParamDescriptions + * @typedef { Amount | Brand | Installation | Instance | bigint | + * Ratio | string | unknown } ParamValue */ /** - * @callback BuildParamManager - * @param {ParamDescriptions} paramDesc - * @returns {ParamManagerFull} + * @template T + * @typedef {{ type: T, name: string }} ParamRecord */ /** - * @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 {ParamRecord<'amount'> & { value: Amount } | + * ParamRecord<'brand'> & { value: Brand } | + * ParamRecord<'installation'> & { value: Installation } | + * ParamRecord<'instance'> & { value: Instance } | + * ParamRecord<'nat'> & { value: bigint } | + * ParamRecord<'ratio'> & { value: Ratio } | + * ParamRecord<'string'> & { value: string } | + * ParamRecord<'unknown'> & { value: unknown } + * } ParamDescription + */ + +/** + * @typedef { SimpleIssue | ParamChangeIssue } Issue */ /** - * @typedef {Object} QuestionTerms - * BallotDetails after the Registrar adds its Instance + * @typedef {Object} QuestionTerms - QuestionSpec plus the Electorate Instance and + * a numerical threshold for the quorum. (The voteCounter doesn't know the + * size of the electorate, so the Electorate has to say what limit to enforce.) + * @property {QuestionSpec} questionSpec + * @property {number} quorumThreshold + * @property {Instance} electorate + */ + +/** + * @typedef {Object} TextPosition + * @property {string} text + */ + +/** + * @typedef { TextPosition | ChangeParamPosition | + * NoChangeParamPosition } Position + */ + +/** + * @typedef {Object} QuestionSpec + * Specification when requesting creation of a Question * @property {ChoiceMethod} method - * @property {string} question - * @property {string[]} positions + * @property {Issue} issue + * @property {Position[]} positions + * @property {ElectionType} electionType * @property {number} maxChoices * @property {ClosingRule} closingRule - * @property {Instance} registrar + * @property {QuorumRule} quorumRule + * @property {Position} tieOutcome */ + /** - * @typedef {Object} BallotDetails - * BallotDetails after the Registrar adds its Instance - * @property {ChoiceMethod} method - * @property {string} question - * @property {string[]} positions - * @property {number} maxChoices + * @typedef {Object} QuestionDetailsExtraProperties + * @property {Instance} counterInstance - instance of the VoteCounter + * @property {Handle<'Question'>} questionHandle */ /** - * @typedef {Object} Ballot - * @property {(positions: string[]) => CompletedBallot} choose - * @property {() => BallotDetails} getDetails + * @typedef {QuestionSpec & QuestionDetailsExtraProperties} QuestionDetails + * complete question details: questionSpec plus counter and questionHandle + */ + +/** + * @typedef {Object} GovernancePair + * @property {Instance} governor + * @property {Instance} governed + */ + +/** + * @typedef {Object} Question + * @property {() => Instance} getVoteCounter + * @property {() => QuestionDetails} getDetails + */ + +/** + * @typedef {Object} CompleteUnrankedQuestion + * @property {Handle<'Question'>} questionHandle + * @property {Position[]} chosen - a list of equal-weight preferred positions + */ + +// not yet in use +/** + * @typedef {Object} CompleteWeightedBallot + * @property {Handle<'Question'>} questionHandle + * @property {[Position,bigint][]} weighted - list of positions with + * weights. VoteCounter may limit weights to a range or require uniqueness. + */ + +// not yet in use +/** + * @typedef {Object} CompleteOrderedBallot + * @property {Handle<'Question'>} questionHandle + * @property {Position[]} ordered - ordered list of position from most preferred + * to least preferred */ /** @@ -87,79 +151,124 @@ /** * @typedef {Object} QuorumCounter - * @property {(VoteStatistics) => boolean} check + * @property {(stats: VoteStatistics) => boolean} check */ /** - * @callback BuildBallot - * @param {ChoiceMethod} method - * @param {string} question - * @param {string[]} positions - * @param {number} maxChoices - * @param {Instance} instance - ballotCounter instance - * @returns {Ballot} + * @callback BuildUnrankedQuestion + * @param {QuestionSpec} questionSpec + * @param {Instance} instance - voteCounter instance + * @returns {Question} */ /** - * @typedef {Object} BallotCounterCreatorFacet - * @property {() => boolean} isOpen - * @property {() => Ballot} getBallotTemplate - * @property {() => VoterFacet} getVoterFacet + * @typedef {Object} VoteCounterCreatorFacet - a facet that the Electorate should + * hold tightly. submitVote() is the core capability that allows the holder to + * specify the identity and choice of a voter. The voteCounter is making that + * available to the Electorate, which should wrap and attenuate it so each + * voter gets only the ability to cast their own vote at a weight specified by + * the electorate. + * @property {SubmitVote} submitVote */ /** - * @typedef {Object} BallotCounterPublicFacet + * @typedef {Object} VoteCounterPublicFacet * @property {() => boolean} isOpen - * @property {() => Ballot} getBallotTemplate - * @property {() => Promise} getOutcome + * @property {() => Question} getQuestion + * @property {() => Promise} getOutcome + * @property {() => QuestionDetails} getDetails * @property {() => Promise} getStats */ /** - * @typedef {Object} BallotCounterCloseFacet + * @typedef {Object} VoteCounterCloseFacet * TEST ONLY: Should not be allowed to escape from contracts * @property {() => void} closeVoting */ /** - * @typedef {Object} CompleteEqualWeightBallot - * @property {string} question - * @property {string[]} chosen - a list of equal-weight preferred positions + * @typedef {Object} VoteCounterFacets + * @property {VoteCounterPublicFacet} publicFacet + * @property {VoteCounterCreatorFacet} creatorFacet + * @property {VoteCounterCloseFacet} closeFacet */ /** - * @typedef {Object} CompleteWeightedBallot - * @property {string} question - * @property {Record[]} weighted - list of positions with weights. - * BallotCounter may limit weights to a range or require uniqueness. + * @callback BuildVoteCounter + * @param {QuestionSpec} questionSpec + * @param {bigint} threshold - questionSpec includes quorumRule; the electorate + * converts that to a number that the counter can enforce. + * @param {Instance} instance + * @returns {VoteCounterFacets} */ /** - * @typedef {Object} CompleteOrderedBallot - * @property {string} question - * @property {string[]} ordered - ordered list of position from most preferred to - * least preferred + * @callback LooksLikeQuestionSpec + * @param {unknown} allegedQuestionSpec + * @returns {QuestionSpec} + */ + +/** + * @callback LooksLikeParamChangeIssue + * @param {unknown} issue + * @returns { asserts issue is ParamChangeIssue } */ /** - * @typedef { CompleteEqualWeightBallot | CompleteOrderedBallot | CompleteWeightedBallot } CompletedBallot + * @callback LooksLikeIssueForType + * @param {ElectionType} electionType + * @param {unknown} issue + * @returns { asserts issue is Issue } + */ + +/** + * @callback LooksLikeSimpleIssue + * @param {unknown} issue + * @returns { asserts issue is SimpleIssue } + */ + +/** + * @callback LooksLikeClosingRule + * @param {unknown} closingRule + * @returns { asserts closingRule is ClosingRule } */ /** * @callback SubmitVote - * @param {Handle<'Voter'>} seat - * @param {ERef} filledBallot + * @param {Handle<'Voter'>} voterHandle + * @param {Position[]} chosenPositions * @param {bigint=} weight */ /** - * @typedef {Object} VoterFacet - * @property {SubmitVote} submitVote + * @typedef {Object} ElectoratePublic + * @property {() => Subscription} getQuestionSubscription + * @property {() => Promise[]>} getOpenQuestions, + * @property {() => string} getName + * @property {() => Instance} getInstance + * @property {(h: Handle<'Question'>) => Promise} getQuestion + */ + +/** + * @typedef {Object} PoserFacet + * @property {AddQuestion} addQuestion + */ + +/** + * @typedef {Object} ElectorateCreatorFacet + * addQuestion() can be used directly when the creator doesn't need any + * reassurance. When someone needs to connect addQuestion to the Electorate + * instance, getPoserInvitation() lets them get addQuestion with assurance. + * @property {() => Promise} getPoserInvitation + * @property {AddQuestion} addQuestion + * @property {() => Promise[]} getVoterInvitations + * @property {() => Subscription} getQuestionSubscription + * @property {() => ElectoratePublic} getPublicFacet */ /** * @typedef {Object} ClosingRule - * @property {Timer} timer + * @property {ERef} timer * @property {Timestamp} deadline */ @@ -171,14 +280,306 @@ /** * @typedef {Object} AddQuestionReturn - * @property {BallotCounterPublicFacet} publicFacet - * @property {BallotCounterCreatorFacet} creatorFacet + * @property {VoteCounterPublicFacet} publicFacet + * @property {VoteCounterCreatorFacet} creatorFacet * @property {Instance} instance */ /** * @callback AddQuestion * @param {Installation} voteCounter - * @param {QuestionTermsShort} questionDetailsShort + * @param {QuestionSpec} questionSpec * @returns {Promise} */ + +/** + * @typedef QuestionCreator + * @property {AddQuestion} addQuestion + */ + +/** + * @callback CreateQuestion + * + * @param {string} name - The name of the parameter to change + * @param {ParamValue} proposedValue - the proposed value for the named + * parameter + * @param {Installation} voteCounterInstallation - the voteCounter to + * instantiate to count votes. Expected to be a binaryVoteCounter. Other + * voteCounters might be added here, or might require separate governors. + * under management so users can trace it back and see that it would use + * this electionManager to manage parameters + * @param {Instance} contractInstance - include the instance of the contract + * @param {ClosingRule} closingRule - deadline and timer for closing voting + * @returns {Promise} + */ + +/** + * @typedef {Object} ParamChangeIssue + * @property {ParamSpecification} paramSpec + * @property {Instance} contract + * @property {ParamValue} proposedValue + */ + +/** + * @typedef {Object} ParamChangePositions + * @property {ChangeParamPosition} positive + * @property {NoChangeParamPosition} negative + */ + +/** + * @callback MakeParamChangePositions + * + * Return a record containing the positive and negative positions for a + * question on changing the param to the proposedValue. + * + * @param {ParamSpecification} paramSpec + * @param {ParamValue} proposedValue + * @returns {ParamChangePositions} + */ + +/** + * @typedef {Object} ParamChangeIssueDetails + * details for a question that can change a contract parameter + * @property {ChoiceMethod} method + * @property {ParamChangeIssue} issue + * @property {ParamChangePositions} positions + * @property {ElectionType} electionType + * @property {number} maxChoices + * @property {ClosingRule} closingRule + * @property {QuorumRule} quorumRule + * @property {NoChangeParamPosition} tieOutcome + * @property {Instance} counterInstance - instance of the VoteCounter + * @property {Handle<'Question'>} questionHandle + */ + +/** + * @typedef {Object} ParamManagerBase + * @property {() => Record} getParams + * @property {(name: string) => ParamDescription} getParam + * @property {() => Subscription} getSubscription + */ + +/** + * @typedef {{ [updater: string]: (arg: ParamValue) => void + * }} ParamManagerUpdaters + * @typedef {ParamManagerBase & ParamManagerUpdaters} ParamManagerFull + */ + +/** + * @typedef {Array} ParamDescriptions + */ + +/** + * @typedef {Record} ParameterNameList + */ + +/** + * @callback AssertParamManagerType + * @param {ParamType} type + * @param {ParamValue} value + * @param {string} name + */ + +/** + * @callback BuildParamManager - ParamManager is a facility that governed + * contracts can use to manage their visible state in a way that allows the + * ContractGovernor to update values using governance. When paramManager is + * instantiated inside the contract, the contract has synchronous access to + * the values, and clients of the contract can verify that a ContractGovernor + * can change the values in a legible way. + * @param {ParamDescriptions} paramDescriptions + * @returns {ParamManagerFull} + */ + +/** + * @typedef {Object} ChangeParamPosition + * @property {ParamSpecification} changeParam + * @property {ParamValue} proposedValue + */ + +/** + * @typedef {Object} NoChangeParamPosition + * @property {ParamSpecification} noChange + */ + +/** + * @typedef {Object} Governor + * @property {CreateQuestion} createQuestion + */ + +/** + * @typedef {Object} GovernorPublic + * @property {() => Instance} getElectorate + * @property {() => Instance} getGovernedContract + * @property {(voteCounter: Instance) => Promise} validateVoteCounter + * @property {(regP: ERef) => Promise} validateElectorate + * @property {(details: QuestionDetails) => boolean} validateTimer + */ + +/** + * @typedef {Object} ParamSpecification + * @property {string} key + * @property {string} parameterName + */ + +/** + * @typedef {Object} ParamChangeVoteResult + * @property {Instance} instance - instance of the VoteCounter + * @property {ERef} details + * @property {Promise} outcomeOfUpdate - A promise for the result + * of updating the parameter value. Primarily useful for its behavior on + * rejection. + */ + +/** + * @typedef {Object} LimitedCreatorFacet + * + * The creatorFacet for the governed contract that will be passed to the + * responsible party. It does not have access to the paramManager. + * @property {() => Instance} getContractGovernor + */ + +/** + * @typedef {Object} ContractPowerfulCreatorFacet + * + * A powerful facet that carries access to both the creatorFacet to be passed + * to the caller and the paramManager, which will be used exclusively by the + * ContractGovenor. + * @property {() => Promise} getLimitedCreatorFacet + * @property {() => ParamManagerRetriever} getParamMgrRetriever + */ + +/** + * @typedef {Object} GovernedContractFacetAccess + * @property {VoteOnParamChange} voteOnParamChange + * @property {() => Promise} getCreatorFacet - creator + * facet of the governed contract, without the tightly held ability to change + * param values. + * @property {() => any} getPublicFacet - public facet of the governed contract + * @property {() => Promise} getInstance - instance of the governed + * contract + */ + +/** + * @callback HandleParamGovernance + * @param {ContractFacet} zcf + * @param {ParamDescriptions} governedParamsTemplate + */ + +/** + * @callback AssertBallotConcernsQuestion + * @param {string} paramName + * @param {QuestionDetails} questionDetails + */ + +/** + * @typedef {Object} ParamManagerRetriever + * @property {(paramSpec: ParamSpecification) => ParamManagerFull} get + */ + +/** + * @callback VoteOnParamChange + * @param {ParamSpecification} paramSpec + * @param {ParamValue} proposedValue + * @param {Installation} voteCounterInstallation + * @param {bigint} deadline + * @returns {ParamChangeVoteResult} + */ + +/** + * @typedef {Object} ParamGovernor + * @property {VoteOnParamChange} voteOnParamChange + * @property {CreatedQuestion} createdQuestion + */ + +/** + * @callback SetupGovernance + * @param {ERef} retriever + * @param {ERef} poserFacet + * @param {Instance} contractInstance + * @param {Timer} timer + * @returns {ParamGovernor} + */ + +/** + * @callback CreatedQuestion + * Was this question created by this ContractGovernor? + * @param {Instance} questionInstance + * @returns {boolean} + */ + +/** + * @callback PositionIncluded + * @param {Position[]} positions + * @param {Position} position + * @returns {boolean} + */ + +/** + * @typedef {Object} GovernedContractTerms + * @property {Timer} timer + * @property {IssuerKeywordRecord} issuerKeywordRecord + * @property {Object} privateArgs + */ + +/** + * @typedef {Object} ContractGovernorTerms + * @property {VoteOnParamChange} timer + * @property {Instance} electorateInstance + * @property {Installation} governedContractInstallation + * @property {GovernedContractTerms} governed + */ + +/** + * @callback AssertContractGovernance + * + * @param {ERef} zoe + * @param {Instance} allegedGoverned + * @param {Instance} allegedGovernor + * @param {Installation} contractGovernorInstallation + * @returns {Promise} + */ + +/** + * @callback AssertContractElectorate - assert that the contract uses the + * electorate + * + * @param {ERef} zoe + * @param {Instance} allegedGovernor + * @param {Instance} allegedElectorate + */ + +/** + * @callback ValidateQuestionDetails + * + * Validate that the question details correspond to a parameter change question + * that the electorate hosts, and that the voteCounter and other details are + * consistent with it. + * + * @param {ERef} zoe + * @param {Instance} electorate + * @param {ParamChangeIssueDetails} details + * @returns {Promise<*>} + */ + +/** + * @callback ValidateQuestionFromCounter + * + * Validate that the questions counted by the voteCounter correspond to a + * parameter change question that the electorate hosts, and that the + * voteCounter and other details are consistent. + * + * @param {ERef} zoe + * @param {Instance} electorate + * @param {Instance} voteCounter + * @returns {Promise<*>} + */ + +/** + * @callback ValidateParamChangeQuestion + * + * Validate that the details are appropriate for an election concerning a + * parameter change for a governed contract. + * + * @param {ParamChangeIssueDetails} details + */ diff --git a/packages/governance/src/validators.js b/packages/governance/src/validators.js new file mode 100644 index 00000000000..17a6f8fee77 --- /dev/null +++ b/packages/governance/src/validators.js @@ -0,0 +1,75 @@ +// @ts-check + +import { E } from '@agoric/eventual-send'; + +const { details: X, quote: q } = assert; + +/** + * Assert that the governed contract was started by the governor. Throws if + * either direction can't be established. If the call succeeds, then the + * governor got exclusive access to the governed contract's creatorFacet, and + * can be trusted to manage its parameters. + * + * @type {AssertContractGovernance} + */ +const assertContractGovernance = async ( + zoe, + allegedGoverned, + allegedGovernor, + contractGovernorInstallation, +) => { + const allegedGovernorPF = E(zoe).getPublicFacet(allegedGovernor); + const realGovernedP = E(allegedGovernorPF).getGovernedContract(); + const allegedGovernedTermsP = E(zoe).getTerms(allegedGoverned); + + const [ + { electionManager: realGovernorInstance }, + realGovernedInstance, + ] = await Promise.all([allegedGovernedTermsP, realGovernedP]); + + assert( + allegedGovernor === realGovernorInstance, + X`The alleged governor did not match the governor retrieved from the governed contract`, + ); + + assert( + allegedGoverned === realGovernedInstance, + X`The alleged governed did not match the governed contract retrieved from the governor`, + ); + + const governorInstallationFromGoverned = await E( + zoe, + ).getInstallationForInstance(realGovernorInstance); + + assert( + governorInstallationFromGoverned === contractGovernorInstallation, + X`The governed contract is not governed by an instance of the provided installation.`, + ); + + return { governor: realGovernorInstance, governed: realGovernedInstance }; +}; + +/** + * Assert that the governor refers to the indicated electorate. + * + * @type {AssertContractElectorate} + */ +const assertContractElectorate = async ( + zoe, + allegedGovernor, + allegedElectorate, +) => { + const allegedGovernorPF = E(zoe).getPublicFacet(allegedGovernor); + const electorate = await E(allegedGovernorPF).getElectorate(); + + assert( + electorate === allegedElectorate, + X`The allegedElectorate didn't match the actual ${q(electorate)}`, + ); + + return true; +}; + +harden(assertContractGovernance); +harden(assertContractElectorate); +export { assertContractGovernance, assertContractElectorate }; diff --git a/packages/governance/test/swingsetTests/committeeBinary/bootstrap.js b/packages/governance/test/swingsetTests/committeeBinary/bootstrap.js index d5c0de5079a..306b189c5eb 100644 --- a/packages/governance/test/swingsetTests/committeeBinary/bootstrap.js +++ b/packages/governance/test/swingsetTests/committeeBinary/bootstrap.js @@ -4,112 +4,140 @@ import { E } from '@agoric/eventual-send'; import { Far } from '@agoric/marshal'; import buildManualTimer from '@agoric/zoe/tools/manualTimer.js'; +import { + ChoiceMethod, + QuorumRule, + ElectionType, + looksLikeQuestionSpec, +} from '../../../src/index.js'; + +const { quote: q } = assert; + const makeVoterVat = async (log, vats, zoe) => { const voterCreator = E(vats.voter).build(zoe); log(`=> voter vat is set up`); return voterCreator; }; -async function addQuestion(qDetails, closingTime, tools) { - const { registrarFacet, installations } = tools; - const { question, positions } = qDetails; +const createQuestion = async (qDetails, closingTime, tools, quorumRule) => { + const { electorateFacet, installations } = tools; + const { issue, positions, electionType } = qDetails; const closingRule = { timer: tools.timer, - deadline: 3n, + deadline: closingTime, }; - const ballotDetails = { - question, - positions, - quorumThreshold: 3n, - tieOutcome: undefined, - closingRule, - }; - const { instance: ballotInstance } = await E(registrarFacet).addQuestion( - installations.binaryBallotCounter, - ballotDetails, + const questionSpec = looksLikeQuestionSpec( + harden({ + method: ChoiceMethod.UNRANKED, + issue, + positions, + electionType, + maxChoices: 1, + closingRule, + quorumRule, + tieOutcome: positions[1], + }), + ); + + const { instance: counterInstance } = await E(electorateFacet).addQuestion( + installations.binaryVoteCounter, + questionSpec, ); - return ballotInstance; -} + return { counterInstance }; +}; -async function committeeBinaryStart( +const committeeBinaryStart = async ( zoe, voterCreator, timer, log, installations, -) { - const registrarTerms = { committeeName: 'TheCommittee', committeeSize: 5 }; - const { creatorFacet: registrarFacet, instance: registrarInstance } = await E( - zoe, - ).startInstance(installations.committeeRegistrar, {}, registrarTerms); - - const choose = 'Choose'; - const details = { question: choose, positions: ['Eeny', 'Meeny'] }; - const tools = { registrarFacet, installations, timer }; - const ballotInstance = await addQuestion(details, 3n, tools); - - const invitations = await E(registrarFacet).getVoterInvitations(); +) => { + const electorateTerms = { committeeName: 'TheCommittee', committeeSize: 5 }; + const { + creatorFacet: electorateFacet, + instance: electorateInstance, + } = await E(zoe).startInstance(installations.committee, {}, electorateTerms); + + const choose = { text: 'Choose' }; + const electionType = ElectionType.SURVEY; + const details = { + issue: choose, + positions: [harden({ text: 'Eeny' }), harden({ text: 'Meeny' })], + electionType, + }; + const [eeny, meeny] = details.positions; + const tools = { electorateFacet, installations, timer }; + const { counterInstance } = await createQuestion( + details, + 3n, + tools, + QuorumRule.MAJORITY, + ); + + const invitations = await E(electorateFacet).getVoterInvitations(); const details2 = await E(zoe).getInvitationDetails(invitations[2]); log( - `invitation details check: ${details2.instance === registrarInstance} ${ + `invitation details check: ${details2.instance === electorateInstance} ${ details2.description }`, ); - const aliceP = E(voterCreator).createVoter('Alice', invitations[0], 'Eeny'); - const bobP = E(voterCreator).createVoter('Bob', invitations[1], 'Meeny'); - const carolP = E(voterCreator).createVoter('Carol', invitations[2], 'Eeny'); - const daveP = E(voterCreator).createVoter('Dave', invitations[3], 'Eeny'); - const emmaP = E(voterCreator).createVoter('Emma', invitations[4], 'Meeny'); + const aliceP = E(voterCreator).createVoter('Alice', invitations[0], eeny); + const bobP = E(voterCreator).createVoter('Bob', invitations[1], meeny); + const carolP = E(voterCreator).createVoter('Carol', invitations[2], eeny); + const daveP = E(voterCreator).createVoter('Dave', invitations[3], eeny); + const emmaP = E(voterCreator).createVoter('Emma', invitations[4], meeny); const [alice] = await Promise.all([aliceP, bobP, carolP, daveP, emmaP]); // At least one voter should verify that everything is on the up-and-up - const instances = { registrarInstance, ballotInstance }; + const instances = { electorateInstance, counterInstance }; await E(alice).verifyBallot(choose, instances); await E(timer).tick(); await E(timer).tick(); await E(timer).tick(); - const publicFacet = E(zoe).getPublicFacet(ballotInstance); + const publicFacet = E(zoe).getPublicFacet(counterInstance); await E(publicFacet) .getOutcome() - .then(outcome => log(`vote outcome: ${outcome}`)) + .then(outcome => log(`vote outcome: ${q(outcome)}`)) .catch(e => log(`vote failed ${e}`)); -} +}; -async function committeeBinaryTwoQuestions( +const committeeBinaryTwoQuestions = async ( zoe, voterCreator, timer, log, installations, -) { +) => { log('starting TWO questions test'); - const registrarTerms = { committeeName: 'TheCommittee', committeeSize: 5 }; - const { creatorFacet: registrarFacet, instance: registrarInstance } = await E( - zoe, - ).startInstance(installations.committeeRegistrar, {}, registrarTerms); + const electorateTerms = { committeeName: 'TheCommittee', committeeSize: 5 }; + const { + creatorFacet: electorateFacet, + instance: electorateInstance, + } = await E(zoe).startInstance(installations.committee, {}, electorateTerms); - const invitations = await E(registrarFacet).getVoterInvitations(); + const invitations = await E(electorateFacet).getVoterInvitations(); const details2 = await E(zoe).getInvitationDetails(invitations[2]); log( - `invitation details check: ${details2.instance === registrarInstance} ${ + `invitation details check: ${details2.instance === electorateInstance} ${ details2.description }`, ); - const tools = { registrarFacet, installations, timer }; - const twoPotato = 'Two Potato'; - const onePotato = 'One Potato'; - const choose = 'Choose'; - const howHigh = 'How high?'; - const oneFoot = '1 foot'; - const twoFeet = '2 feet'; + const tools = { electorateFacet, installations, timer }; + const twoPotato = harden({ text: 'Two Potato' }); + const onePotato = harden({ text: 'One Potato' }); + const choose = { text: 'Choose' }; + const howHigh = { text: 'How high?' }; + const oneFoot = harden({ text: '1 foot' }); + const twoFeet = harden({ text: '2 feet' }); const aliceP = E(voterCreator).createMultiVoter('Alice', invitations[0], [ [choose, onePotato], @@ -132,21 +160,39 @@ async function committeeBinaryTwoQuestions( [howHigh, twoFeet], ]); - const potato = { question: choose, positions: [onePotato, twoPotato] }; - const potatoBallotInstance = await addQuestion(potato, 3n, tools); + const potato = { + issue: choose, + positions: [onePotato, twoPotato], + electionType: ElectionType.SURVEY, + }; + const { counterInstance: potatoCounterInstance } = await createQuestion( + potato, + 3n, + tools, + QuorumRule.MAJORITY, + ); - const height = { question: howHigh, positions: [oneFoot, twoFeet] }; - const heightBallotInstance = await addQuestion(height, 4n, tools); + const height = { + issue: howHigh, + positions: [oneFoot, twoFeet], + electionType: ElectionType.SURVEY, + }; + const { counterInstance: heightCounterInstance } = await createQuestion( + height, + 4n, + tools, + QuorumRule.MAJORITY, + ); const [alice, bob] = await Promise.all([aliceP, bobP, carolP, daveP, emmaP]); // At least one voter should verify that everything is on the up-and-up await E(alice).verifyBallot(choose, { - registrarInstance, - ballotInstance: potatoBallotInstance, + electorateInstance, + counterInstance: potatoCounterInstance, }); await E(bob).verifyBallot(howHigh, { - registrarInstance, - ballotInstance: heightBallotInstance, + electorateInstance, + counterInstance: heightCounterInstance, }); await E(timer).tick(); @@ -154,31 +200,32 @@ async function committeeBinaryTwoQuestions( await E(timer).tick(); await E(timer).tick(); - await E(E(zoe).getPublicFacet(potatoBallotInstance)) + await E(E(zoe).getPublicFacet(potatoCounterInstance)) .getOutcome() - .then(outcome => log(`vote outcome: ${outcome}`)) + .then(outcome => log(`vote outcome: ${q(outcome)}`)) .catch(e => log(`vote failed ${e}`)); - await E(E(zoe).getPublicFacet(heightBallotInstance)) + await E(E(zoe).getPublicFacet(heightCounterInstance)) .getOutcome() - .then(outcome => log(`vote outcome: ${outcome}`)) + .then(outcome => log(`vote outcome: ${q(outcome)}`)) .catch(e => log(`vote failed ${e}`)); -} +}; const makeBootstrap = (argv, cb, vatPowers) => async (vats, devices) => { const log = vatPowers.testLog; const vatAdminSvc = await E(vats.vatAdmin).createVatAdminService( devices.vatAdmin, ); + /** @type { ERef } */ const zoe = E(vats.zoe).buildZoe(vatAdminSvc); - const [committeeRegistrar, binaryBallotCounter] = await Promise.all([ - E(zoe).install(cb.committeeRegistrar), - E(zoe).install(cb.binaryBallotCounter), + const [committee, binaryVoteCounter] = await Promise.all([ + E(zoe).install(cb.committee), + E(zoe).install(cb.binaryVoteCounter), ]); const timer = buildManualTimer(log); - const installations = { committeeRegistrar, binaryBallotCounter }; + const installations = { committee, binaryVoteCounter }; const voterCreator = await makeVoterVat(log, vats, zoe); diff --git a/packages/governance/test/swingsetTests/committeeBinary/test-committee.js b/packages/governance/test/swingsetTests/committeeBinary/test-committee.js index f72c6f7887b..01f5efb4e86 100644 --- a/packages/governance/test/swingsetTests/committeeBinary/test-committee.js +++ b/packages/governance/test/swingsetTests/committeeBinary/test-committee.js @@ -12,7 +12,7 @@ import { buildVatController, buildKernelBundles } from '@agoric/swingset-vat'; import bundleSource from '@agoric/bundle-source'; import path from 'path'; -const CONTRACT_FILES = ['committeeRegistrar', 'binaryBallotCounter']; +const CONTRACT_FILES = ['committee', 'binaryVoteCounter']; const filename = new URL(import.meta.url).pathname; const dirname = path.dirname(filename); @@ -24,14 +24,8 @@ test.before(async t => { const contractBundles = {}; await Promise.all( CONTRACT_FILES.map(async settings => { - let bundleName; - let contractPath; - if (typeof settings === 'string') { - bundleName = settings; - contractPath = settings; - } else { - ({ bundleName, contractPath } = settings); - } + const bundleName = settings; + const contractPath = settings; const source = `${dirname}/../../../src/${contractPath}`; const bundle = await bundleSource(source); contractBundles[bundleName] = bundle; @@ -77,19 +71,19 @@ const expectedCommitteeBinaryStartLog = [ '=> voter vat is set up', '@@ schedule task for:3, currently: 0 @@', 'invitation details check: true Voter2', - 'Alice cast a ballot on Choose for Eeny', - 'Bob cast a ballot on Choose for Meeny', - 'Carol cast a ballot on Choose for Eeny', - 'Dave cast a ballot on Choose for Eeny', - 'Emma cast a ballot on Choose for Meeny', - 'Verify ballot from instance: Choose, Eeny,Meeny, choose_n', - 'Verify: q: Choose, max: 1, committee: TheCommittee', - 'Verify instances: registrar: true, counter: true', + 'Alice voted for {"text":"Eeny"}', + 'Bob voted for {"text":"Meeny"}', + 'Carol voted for {"text":"Eeny"}', + 'Dave voted for {"text":"Eeny"}', + 'Emma voted for {"text":"Meeny"}', + 'verify question from instance: {"text":"Choose"}, [{"text":"Eeny"},{"text":"Meeny"}], unranked', + 'Verify: q: {"text":"Choose"}, max: 1, committee: TheCommittee', + 'Verify instances: electorate: true, counter: true', '@@ tick:1 @@', '@@ tick:2 @@', '@@ tick:3 @@', '&& running a task scheduled for 3. &&', - 'vote outcome: Eeny', + 'vote outcome: {"text":"Eeny"}', ]; test.serial('zoe - committee binary vote - valid inputs', async t => { @@ -102,31 +96,31 @@ const expectedCommitteeBinaryTwoQuestionsLog = [ 'starting TWO questions test', 'invitation details check: true Voter2', '@@ schedule task for:3, currently: 0 @@', - 'Alice cast a ballot on Choose for One Potato', - 'Bob cast a ballot on Choose for One Potato', - 'Carol cast a ballot on Choose for Two Potato', - 'Dave cast a ballot on Choose for One Potato', - 'Emma cast a ballot on Choose for One Potato', - '@@ schedule task for:3, currently: 0 @@', - 'Alice cast a ballot on How high? for 1 foot', - 'Bob cast a ballot on How high? for 2 feet', - 'Carol cast a ballot on How high? for 1 foot', - 'Dave cast a ballot on How high? for 1 foot', - 'Emma cast a ballot on How high? for 2 feet', - 'Verify ballot from instance: Choose, One Potato,Two Potato, choose_n', - 'Verify: q: Choose, max: 1, committee: TheCommittee', - 'Verify instances: registrar: true, counter: true', - 'Verify ballot from instance: How high?, 1 foot,2 feet, choose_n', - 'Verify: q: How high?, max: 1, committee: TheCommittee', - 'Verify instances: registrar: true, counter: true', + 'Alice voted on {"text":"Choose"} for {"text":"One Potato"}', + 'Bob voted on {"text":"Choose"} for {"text":"One Potato"}', + 'Carol voted on {"text":"Choose"} for {"text":"Two Potato"}', + 'Dave voted on {"text":"Choose"} for {"text":"One Potato"}', + 'Emma voted on {"text":"Choose"} for {"text":"One Potato"}', + '@@ schedule task for:4, currently: 0 @@', + 'Alice voted on {"text":"How high?"} for {"text":"1 foot"}', + 'Bob voted on {"text":"How high?"} for {"text":"2 feet"}', + 'Carol voted on {"text":"How high?"} for {"text":"1 foot"}', + 'Dave voted on {"text":"How high?"} for {"text":"1 foot"}', + 'Emma voted on {"text":"How high?"} for {"text":"2 feet"}', + 'verify question from instance: {"text":"Choose"}, [{"text":"One Potato"},{"text":"Two Potato"}], unranked', + 'Verify: q: {"text":"Choose"}, max: 1, committee: TheCommittee', + 'Verify instances: electorate: true, counter: true', + 'verify question from instance: {"text":"How high?"}, [{"text":"1 foot"},{"text":"2 feet"}], unranked', + 'Verify: q: {"text":"How high?"}, max: 1, committee: TheCommittee', + 'Verify instances: electorate: true, counter: true', '@@ tick:1 @@', '@@ tick:2 @@', '@@ tick:3 @@', '&& running a task scheduled for 3. &&', - '&& running a task scheduled for 3. &&', '@@ tick:4 @@', - 'vote outcome: One Potato', - 'vote outcome: 1 foot', + '&& running a task scheduled for 4. &&', + 'vote outcome: {"text":"One Potato"}', + 'vote outcome: {"text":"1 foot"}', ]; test.serial('zoe - committee binary vote - TwoQuestions', async t => { diff --git a/packages/governance/test/swingsetTests/committeeBinary/vat-voter.js b/packages/governance/test/swingsetTests/committeeBinary/vat-voter.js index 075cf66e594..4f488b81cec 100644 --- a/packages/governance/test/swingsetTests/committeeBinary/vat-voter.js +++ b/packages/governance/test/swingsetTests/committeeBinary/vat-voter.js @@ -2,74 +2,84 @@ import { E } from '@agoric/eventual-send'; import { Far } from '@agoric/marshal'; -import { observeNotifier } from '@agoric/notifier'; +import { observeIteration } from '@agoric/notifier'; +import { sameStructure } from '@agoric/same-structure'; -const verify = async (log, question, registrarPublicFacet, instances) => { - const ballotTemplate = E(registrarPublicFacet).getBallot(question); - const { positions, method, question: q, maxChoices, instance } = await E( - ballotTemplate, - ).getDetails(); - log(`Verify ballot from instance: ${question}, ${positions}, ${method}`); - const c = await E(registrarPublicFacet).getName(); - log(`Verify: q: ${q}, max: ${maxChoices}, committee: ${c}`); - const registrarInstance = await E(registrarPublicFacet).getInstance(); +const { quote: q } = assert; + +const verify = async (log, issue, electoratePublicFacet, instances) => { + const questionHandles = await E(electoratePublicFacet).getOpenQuestions(); + const detailsP = questionHandles.map(h => { + const question = E(electoratePublicFacet).getQuestion(h); + return E(question).getDetails(); + }); + const detailsPlural = await Promise.all(detailsP); + const details = detailsPlural.find(d => sameStructure(d.issue, issue)); + + const { positions, method, issue: iss, maxChoices } = details; + log(`verify question from instance: ${q(issue)}, ${q(positions)}, ${method}`); + const c = await E(electoratePublicFacet).getName(); + log(`Verify: q: ${q(iss)}, max: ${maxChoices}, committee: ${c}`); + const electorateInstance = await E(electoratePublicFacet).getInstance(); log( - `Verify instances: registrar: ${registrarInstance === - instances.registrarInstance}, counter: ${instance === - instances.ballotInstance}`, + `Verify instances: electorate: ${electorateInstance === + instances.electorateInstance}, counter: ${details.counterInstance === + instances.counterInstance}`, ); }; const build = async (log, zoe) => { return Far('voter', { createVoter: async (name, invitation, choice) => { - const registrarInstance = await E(zoe).getInstance(invitation); - const registrarPublicFacet = E(zoe).getPublicFacet(registrarInstance); + const electorateInstance = await E(zoe).getInstance(invitation); + const electoratePublicFacet = E(zoe).getPublicFacet(electorateInstance); const seat = E(zoe).offer(invitation); const voteFacet = E(seat).getOfferResult(); const votingObserver = Far('voting observer', { - updateState: question => { - log(`${name} cast a ballot on ${question} for ${choice}`); - return E(voteFacet).castBallotFor(question, [choice]); + updateState: details => { + log(`${name} voted for ${q(choice)}`); + return E(voteFacet).castBallotFor(details.questionHandle, [choice]); }, }); - const notifier = E(registrarPublicFacet).getQuestionNotifier(); - observeNotifier(notifier, votingObserver); + const subscription = E(electoratePublicFacet).getQuestionSubscription(); + observeIteration(subscription, votingObserver); return Far(`Voter ${name}`, { verifyBallot: (question, instances) => - verify(log, question, registrarPublicFacet, instances), + verify(log, question, electoratePublicFacet, instances), }); }, createMultiVoter: async (name, invitation, choices) => { - const registrarInstance = await E(zoe).getInstance(invitation); - const registrarPublicFacet = E(zoe).getPublicFacet(registrarInstance); + const electorateInstance = await E(zoe).getInstance(invitation); + const electoratePublicFacet = E(zoe).getPublicFacet(electorateInstance); const seat = E(zoe).offer(invitation); const voteFacet = E(seat).getOfferResult(); - const voteMap = new Map(choices); + const voteMap = new Map(); + choices.forEach(entry => { + const [issue, position] = entry; + voteMap.set(issue.text, position); + }); const votingObserver = Far('voting observer', { - updateState: question => { - const choice = voteMap.get(question); - - log(`${name} cast a ballot on ${question} for ${choice}`); - return E(voteFacet).castBallotFor(question, [choice]); + updateState: details => { + const choice = voteMap.get(details.issue.text); + log(`${name} voted on ${q(details.issue)} for ${q(choice)}`); + return E(voteFacet).castBallotFor(details.questionHandle, [choice]); }, }); - const notifier = E(registrarPublicFacet).getQuestionNotifier(); - observeNotifier(notifier, votingObserver); + const subscription = E(electoratePublicFacet).getQuestionSubscription(); + observeIteration(subscription, votingObserver); return Far(`Voter ${name}`, { verifyBallot: (question, instances) => - verify(log, question, registrarPublicFacet, instances), + verify(log, question, electoratePublicFacet, instances), }); }, }); }; -export function buildRootObject(vatPowers) { - return Far('root', { +export const buildRootObject = vatPowers => + Far('root', { build: (...args) => build(vatPowers.testLog, ...args), }); -} diff --git a/packages/governance/test/swingsetTests/contractGovernor/bootstrap.js b/packages/governance/test/swingsetTests/contractGovernor/bootstrap.js new file mode 100644 index 00000000000..d348db8c7e4 --- /dev/null +++ b/packages/governance/test/swingsetTests/contractGovernor/bootstrap.js @@ -0,0 +1,214 @@ +// @ts-check + +import { E } from '@agoric/eventual-send'; +import { Far } from '@agoric/marshal'; +import buildManualTimer from '@agoric/zoe/tools/manualTimer.js'; +import { observeIteration } from '@agoric/notifier'; +import { governedParameterInitialValues } from './governedContract.js'; + +const { quote: q } = assert; + +/** + * @param {ERef} zoe + * @param {(string:string) => undefined} log + * @param {Record} installations + * @param {ERef} contractFacetAccess + * @returns {Promise<*>} + */ +const contractGovernorStart = async ( + zoe, + log, + installations, + contractFacetAccess, +) => { + const { details, instance, outcomeOfUpdate } = await E( + contractFacetAccess, + ).voteOnParamChange( + { key: 'main', parameterName: 'MalleableNumber' }, + 299792458n, + installations.binaryVoteCounter, + 3n, + ); + + E(E(zoe).getPublicFacet(instance)) + .getOutcome() + .then(outcome => log(`vote outcome: ${q(outcome)}`)) + .catch(e => log(`vote failed ${e}`)); + + E.when(outcomeOfUpdate, outcome => log(`updated to ${q(outcome)}`)).catch(e => + log(`update failed: ${e}`), + ); + return details; +}; + +const installContracts = async (zoe, cb) => { + const [ + committee, + binaryVoteCounter, + contractGovernor, + governedContract, + ] = await Promise.all([ + E(zoe).install(cb.committee), + E(zoe).install(cb.binaryVoteCounter), + E(zoe).install(cb.contractGovernor), + E(zoe).install(cb.governedContract), + ]); + const installations = { + committee, + binaryVoteCounter, + contractGovernor, + governedContract, + }; + return installations; +}; + +const startElectorate = async (zoe, installations) => { + const electorateTerms = { + committeeName: 'TwentyCommittee', + committeeSize: 5, + }; + const { + creatorFacet: electorateCreatorFacet, + instance: electorateInstance, + } = await E(zoe).startInstance(installations.committee, {}, electorateTerms); + return { electorateCreatorFacet, electorateInstance }; +}; + +const createVoters = async (electorateCreatorFacet, voterCreator) => { + const invitations = await E(electorateCreatorFacet).getVoterInvitations(); + + const aliceP = E(voterCreator).createVoter('Alice', invitations[0]); + const bobP = E(voterCreator).createVoter('Bob', invitations[1]); + const carolP = E(voterCreator).createVoter('Carol', invitations[2]); + const daveP = E(voterCreator).createVoter('Dave', invitations[3]); + const emmaP = E(voterCreator).createVoter('Emma', invitations[4]); + return Promise.all([aliceP, bobP, carolP, daveP, emmaP]); +}; + +const votersVote = async (detailsP, votersP, selections) => { + const [voters, details] = await Promise.all([votersP, detailsP]); + const { positions, questionHandle } = details; + + await Promise.all( + voters.map((v, i) => { + return E(v).castBallotFor(questionHandle, positions[selections[i]]); + }), + ); +}; + +const oneVoterValidate = async ( + votersP, + detailsP, + governedInstanceP, + electorateInstance, + governorInstanceP, + installations, + timer, +) => { + const [ + voters, + details, + governedInstance, + governorInstance, + ] = await Promise.all([ + votersP, + detailsP, + governedInstanceP, + governorInstanceP, + ]); + const { counterInstance } = details; + + E(voters[0]).validate( + counterInstance, + governedInstance, + electorateInstance, + governorInstance, + installations, + timer, + ); +}; + +const checkContractState = async (zoe, contractInstanceP, log) => { + const contractInstance = await contractInstanceP; + const contractPublic = E(zoe).getPublicFacet(contractInstance); + let paramValues = await E(contractPublic).getGovernedParamsValues(); + const subscription = await E(contractPublic).getSubscription(); + const paramChangeObserver = Far('param observer', { + updateState: update => { + log(`${update.name} was changed to ${q(update.value)}`); + }, + }); + observeIteration(subscription, paramChangeObserver); + + // it takes a while for the update to propagate. The second time it seems good + paramValues = await E(contractPublic).getGovernedParamsValues(); + const malleableNumber = paramValues.main.MalleableNumber; + + log(`current value of ${malleableNumber.name} is ${malleableNumber.value}`); +}; + +const makeBootstrap = (argv, cb, vatPowers) => async (vats, devices) => { + const log = vatPowers.testLog; + const vatAdminSvc = await E(vats.vatAdmin).createVatAdminService( + devices.vatAdmin, + ); + /** @type { ERef } */ + const zoe = E(vats.zoe).buildZoe(vatAdminSvc); + const installations = await installContracts(zoe, cb); + const timer = buildManualTimer(log); + const voterCreator = E(vats.voter).build(zoe); + const { electorateCreatorFacet, electorateInstance } = await startElectorate( + zoe, + installations, + ); + + log(`=> voter and electorate vats are set up`); + + const terms = { + timer, + electorateInstance, + governedContractInstallation: installations.governedContract, + governed: { + issuerKeywordRecord: {}, + terms: { main: governedParameterInitialValues }, + }, + }; + const privateArgs = { electorateCreatorFacet }; + const { creatorFacet: governor, instance: governorInstance } = await E( + zoe, + ).startInstance(installations.contractGovernor, {}, terms, privateArgs); + const governedInstance = E(governor).getInstance(); + + const [testName] = argv; + switch (testName) { + case 'contractGovernorStart': { + const votersP = createVoters(electorateCreatorFacet, voterCreator); + const detailsP = contractGovernorStart(zoe, log, installations, governor); + await votersVote(detailsP, votersP, [0, 1, 1, 0, 0]); + + await oneVoterValidate( + votersP, + detailsP, + governedInstance, + electorateInstance, + governorInstance, + installations, + timer, + ); + + await E(timer).tick(); + await E(timer).tick(); + await E(timer).tick(); + + await checkContractState(zoe, governedInstance, log); + break; + } + default: + log(`didn't find test: ${argv}`); + } +}; + +export const buildRootObject = (vatPowers, vatParameters) => { + const { argv, contractBundles: cb } = vatParameters; + return Far('root', { bootstrap: makeBootstrap(argv, cb, vatPowers) }); +}; diff --git a/packages/governance/test/swingsetTests/contractGovernor/governedContract.js b/packages/governance/test/swingsetTests/contractGovernor/governedContract.js new file mode 100644 index 00000000000..1df92f35638 --- /dev/null +++ b/packages/governance/test/swingsetTests/contractGovernor/governedContract.js @@ -0,0 +1,45 @@ +// @ts-check + +import { handleParamGovernance } from '../../../src/contractHelper.js'; +import { ParamType } from '../../../src/paramManager.js'; + +const MALLEABLE_NUMBER = 'MalleableNumber'; + +/** @type {ParameterNameList} */ +const governedParameterTerms = { + main: [MALLEABLE_NUMBER], +}; + +/** @type {ParamDescriptions} */ +const governedParameterInitialValues = [ + { + name: MALLEABLE_NUMBER, + value: 602214090000000000000000n, + type: ParamType.NAT, + }, +]; +harden(governedParameterInitialValues); + +/** @type {ContractStartFn} */ +const start = async zcf => { + const { makePublicFacet, makeCreatorFacet } = handleParamGovernance( + zcf, + governedParameterInitialValues, + ); + + return { + publicFacet: makePublicFacet({}), + creatorFacet: makeCreatorFacet({}), + }; +}; + +harden(start); +harden(MALLEABLE_NUMBER); +harden(governedParameterTerms); + +export { + start, + governedParameterTerms, + MALLEABLE_NUMBER, + governedParameterInitialValues, +}; diff --git a/packages/governance/test/swingsetTests/contractGovernor/test-governor.js b/packages/governance/test/swingsetTests/contractGovernor/test-governor.js new file mode 100644 index 00000000000..6acc28ab569 --- /dev/null +++ b/packages/governance/test/swingsetTests/contractGovernor/test-governor.js @@ -0,0 +1,107 @@ +// @ts-check + +// TODO Remove babel-standalone preinitialization +// https://github.com/endojs/endo/issues/768 +// eslint-disable-next-line import/no-extraneous-dependencies +import '@agoric/babel-standalone'; +// eslint-disable-next-line import/no-extraneous-dependencies +import '@agoric/install-ses'; +// eslint-disable-next-line import/no-extraneous-dependencies +import test from 'ava'; +import path from 'path'; + +import { buildVatController, buildKernelBundles } from '@agoric/swingset-vat'; +import bundleSource from '@agoric/bundle-source'; + +const CONTRACT_FILES = [ + 'committee', + 'contractGovernor', + 'binaryVoteCounter', + { + contractPath: '/governedContract', + bundleName: 'governedContract', + }, +]; + +const filename = new URL(import.meta.url).pathname; +const dirname = path.dirname(filename); + +test.before(async t => { + const start = Date.now(); + const kernelBundles = await buildKernelBundles(); + const step2 = Date.now(); + const contractBundles = {}; + await Promise.all( + CONTRACT_FILES.map(async settings => { + let bundleName; + let contractPath; + if (typeof settings === 'string') { + bundleName = settings; + contractPath = `/../../../src/${settings}`; + } else { + ({ bundleName, contractPath } = settings); + } + const source = `${dirname}${contractPath}`; + const bundle = await bundleSource(source); + contractBundles[bundleName] = bundle; + }), + ); + const step3 = Date.now(); + + const vats = {}; + await Promise.all( + ['voter', 'zoe'].map(async name => { + const source = `${dirname}/vat-${name}.js`; + const bundle = await bundleSource(source); + vats[name] = { bundle }; + }), + ); + const bootstrapSource = `${dirname}/bootstrap.js`; + vats.bootstrap = { + bundle: await bundleSource(bootstrapSource), + parameters: { contractBundles }, // argv will be added to this + }; + const config = { bootstrap: 'bootstrap', vats }; + config.defaultManagerType = 'xs-worker'; + + const step4 = Date.now(); + const ktime = `${(step2 - start) / 1000}s kernel`; + const ctime = `${(step3 - step2) / 1000}s contracts`; + const vtime = `${(step4 - step3) / 1000}s vats`; + const ttime = `${(step4 - start) / 1000}s total`; + console.log(`bundling: ${ktime}, ${ctime}, ${vtime}, ${ttime}`); + + // @ts-ignore + t.context.data = { kernelBundles, config }; +}); + +const main = async (t, argv) => { + const { kernelBundles, config } = t.context.data; + const controller = await buildVatController(config, argv, { kernelBundles }); + await controller.run(); + return controller.dump(); +}; + +const expectedcontractGovernorStartLog = [ + '=> voter and electorate vats are set up', + '@@ schedule task for:3, currently: 0 @@', + 'Voter Alice voted for {"changeParam":{"key":"main","parameterName":"MalleableNumber"},"proposedValue":"[299792458n]"}', + 'Voter Bob voted for {"noChange":{"key":"main","parameterName":"MalleableNumber"}}', + 'Voter Carol voted for {"noChange":{"key":"main","parameterName":"MalleableNumber"}}', + 'Voter Dave voted for {"changeParam":{"key":"main","parameterName":"MalleableNumber"},"proposedValue":"[299792458n]"}', + 'Voter Emma voted for {"changeParam":{"key":"main","parameterName":"MalleableNumber"},"proposedValue":"[299792458n]"}', + '@@ tick:1 @@', + '@@ tick:2 @@', + '@@ tick:3 @@', + '&& running a task scheduled for 3. &&', + 'vote outcome: {"changeParam":{"key":"main","parameterName":"MalleableNumber"},"proposedValue":"[299792458n]"}', + 'updated to "[299792458n]"', + 'MalleableNumber was changed to "[602214090000000000000000n]"', + 'current value of MalleableNumber is 299792458', + 'Voter Alice validated all the things', +]; + +test('zoe - contract governance', async t => { + const dump = await main(t, ['contractGovernorStart']); + t.deepEqual(dump.log, expectedcontractGovernorStartLog); +}); diff --git a/packages/governance/test/swingsetTests/contractGovernor/vat-voter.js b/packages/governance/test/swingsetTests/contractGovernor/vat-voter.js new file mode 100644 index 00000000000..04e774f0037 --- /dev/null +++ b/packages/governance/test/swingsetTests/contractGovernor/vat-voter.js @@ -0,0 +1,101 @@ +// @ts-check + +import { E } from '@agoric/eventual-send'; +import { Far } from '@agoric/marshal'; + +import { + assertContractElectorate, + assertContractGovernance, +} from '../../../src/validators.js'; +import { + validateQuestionFromCounter, + validateQuestionDetails, +} from '../../../src/contractGovernor.js'; +import { assertBallotConcernsQuestion } from '../../../src/governParam.js'; + +const { details: X, quote: q } = assert; + +const build = async (log, zoe) => { + return Far('voter', { + createVoter: async (name, invitation) => { + const seat = E(zoe).offer(invitation); + const voteFacet = E(seat).getOfferResult(); + + return Far(`Voter ${name}`, { + castBallotFor: async (questionHandle, choice) => { + log(`Voter ${name} voted for ${q(choice)}`); + return E(voteFacet).castBallotFor(questionHandle, [choice]); + }, + validate: async ( + counterInstance, + governedInstance, + electorateInstance, + governorInstance, + installations, + ) => { + const validateQuestionFromCounterP = validateQuestionFromCounter( + zoe, + electorateInstance, + counterInstance, + ); + + const contractGovernanceP = assertContractGovernance( + zoe, + governedInstance, + governorInstance, + installations.contractGovernor, + ); + + const [ + governedParam, + questionDetails, + electorateInstallation, + voteCounterInstallation, + governedInstallation, + governorInstallation, + validatedQuestion, + contractGovernance, + ] = await Promise.all([ + E.get(E(zoe).getTerms(governedInstance)).main, + E(E(zoe).getPublicFacet(counterInstance)).getDetails(), + E(zoe).getInstallationForInstance(electorateInstance), + E(zoe).getInstallationForInstance(counterInstance), + E(zoe).getInstallationForInstance(governedInstance), + E(zoe).getInstallationForInstance(governorInstance), + validateQuestionFromCounterP, + contractGovernanceP, + ]); + + assertBallotConcernsQuestion(governedParam[0].name, questionDetails); + assert(installations.binaryVoteCounter === voteCounterInstallation); + assert(installations.governedContract === governedInstallation); + assert(installations.contractGovernor === governorInstallation); + assert(installations.committee === electorateInstallation); + await assertContractElectorate( + zoe, + governorInstance, + electorateInstance, + ); + + await validateQuestionDetails( + zoe, + electorateInstance, + questionDetails, + ); + assert(validatedQuestion, X`governor failed to validate electorate`); + assert( + contractGovernance, + X`governor and governed aren't tightly linked`, + ); + + log(`Voter ${name} validated all the things`); + }, + }); + }, + }); +}; + +export const buildRootObject = vatPowers => + Far('root', { + build: (...args) => build(vatPowers.testLog, ...args), + }); diff --git a/packages/governance/test/swingsetTests/contractGovernor/vat-zoe.js b/packages/governance/test/swingsetTests/contractGovernor/vat-zoe.js new file mode 100644 index 00000000000..7544a16749c --- /dev/null +++ b/packages/governance/test/swingsetTests/contractGovernor/vat-zoe.js @@ -0,0 +1,18 @@ +// @ts-check + +import { Far } from '@agoric/marshal'; + +import { makeZoeKit } from '@agoric/zoe'; +import { E } from '@agoric/eventual-send'; + +export function buildRootObject(vatPowers) { + return Far('root', { + buildZoe: vatAdminSvc => { + const shutdownZoeVat = vatPowers.exitVatWithFailure; + const { zoeService } = makeZoeKit(vatAdminSvc, shutdownZoeVat); + const feePurse = E(zoeService).makeFeePurse(); + const zoe = E(zoeService).bindDefaultFeePurse(feePurse); + return zoe; + }, + }); +} diff --git a/packages/governance/test/unitTests/test-ballotBuilder.js b/packages/governance/test/unitTests/test-ballotBuilder.js new file mode 100644 index 00000000000..d9d58fd45dc --- /dev/null +++ b/packages/governance/test/unitTests/test-ballotBuilder.js @@ -0,0 +1,180 @@ +// @ts-check + +// TODO Remove babel-standalone preinitialization +// https://github.com/endojs/endo/issues/768 +// eslint-disable-next-line import/no-extraneous-dependencies +import '@agoric/babel-standalone'; +// eslint-disable-next-line import/no-extraneous-dependencies +import '@agoric/install-ses'; +// eslint-disable-next-line import/no-extraneous-dependencies +import test from 'ava'; +import buildManualTimer from '@agoric/zoe/tools/manualTimer.js'; +import { + looksLikeQuestionSpec, + ChoiceMethod, + ElectionType, + QuorumRule, +} from '../../src/index.js'; + +const issue = harden({ text: 'will it blend?' }); +const positions = [harden({ text: 'yes' }), harden({ text: 'no' })]; +const timer = buildManualTimer(console.log); +const closingRule = { timer, deadline: 37n }; + +test('good QuestionSpec', t => { + t.truthy( + looksLikeQuestionSpec( + harden({ + method: ChoiceMethod.UNRANKED, + issue, + positions, + electionType: ElectionType.SURVEY, + maxChoices: 2, + closingRule, + quorumRule: QuorumRule.MAJORITY, + tieOutcome: positions[1], + }), + ), + ); +}); + +test('bad Question', t => { + t.throws( + () => + looksLikeQuestionSpec( + // @ts-ignore Illegal Question + harden({ + method: ChoiceMethod.UNRANKED, + issue: 'will it blend?', + positions, + electionType: ElectionType.SURVEY, + maxChoices: 2, + closingRule, + quorumRule: QuorumRule.MAJORITY, + tieOutcome: positions[1], + }), + ), + { + message: 'A question can only be a pass-by-copy record: "will it blend?"', + }, + ); +}); + +test('bad timer', t => { + t.throws( + () => + looksLikeQuestionSpec( + // @ts-ignore Illegal timer + harden({ + method: ChoiceMethod.UNRANKED, + issue, + positions, + electionType: ElectionType.SURVEY, + maxChoices: 2, + closingRule: { timer: 37, deadline: 37n }, + quorumRule: QuorumRule.MAJORITY, + tieOutcome: positions[1], + }), + ), + { message: 'Timer must be a timer 37' }, + ); +}); + +test('bad method', t => { + t.throws( + () => + looksLikeQuestionSpec( + // @ts-ignore Illegal Method + harden({ + method: 'choose', + issue, + positions, + electionType: ElectionType.SURVEY, + maxChoices: 2, + closingRule, + quorumRule: QuorumRule.MAJORITY, + tieOutcome: positions[1], + }), + ), + { message: 'Illegal "ChoiceMethod": "choose"' }, + ); +}); + +test('bad Quorum', t => { + t.throws( + () => + looksLikeQuestionSpec( + // @ts-ignore Illegal Quorum + harden({ + method: ChoiceMethod.ORDER, + issue, + positions, + electionType: ElectionType.SURVEY, + maxChoices: 2, + closingRule, + quorumRule: 0.5, + tieOutcome: positions[1], + }), + ), + { message: 'Illegal "QuorumRule": 0.5' }, + ); +}); + +test('bad tieOutcome', t => { + t.throws( + () => + looksLikeQuestionSpec( + // @ts-ignore Illegal tieOutcome + harden({ + method: ChoiceMethod.ORDER, + issue, + positions, + electionType: ElectionType.SURVEY, + maxChoices: 2, + closingRule, + quorumRule: QuorumRule.NO_QUORUM, + tieOutcome: 'try again', + }), + ), + { message: 'tieOutcome must be a legal position: "try again"' }, + ); +}); + +test('bad maxChoices', t => { + t.throws( + () => + looksLikeQuestionSpec( + harden({ + method: ChoiceMethod.ORDER, + issue, + positions, + electionType: ElectionType.SURVEY, + maxChoices: 0, + closingRule, + quorumRule: QuorumRule.NO_QUORUM, + tieOutcome: positions[1], + }), + ), + { message: 'maxChoices must be positive: 0' }, + ); +}); + +test('bad positions', t => { + t.throws( + () => + looksLikeQuestionSpec({ + method: ChoiceMethod.ORDER, + issue, + positions: [{ text: 'yes' }, { text: 'no' }], + electionType: ElectionType.SURVEY, + maxChoices: 1, + closingRule, + quorumRule: QuorumRule.NO_QUORUM, + tieOutcome: positions[1], + }), + { + message: + 'Cannot pass non-frozen objects like {"text":"yes"}. Use harden()', + }, + ); +}); diff --git a/packages/governance/test/unitTests/test-ballotCount.js b/packages/governance/test/unitTests/test-ballotCount.js index ddebe023951..fc6214fc289 100644 --- a/packages/governance/test/unitTests/test-ballotCount.js +++ b/packages/governance/test/unitTests/test-ballotCount.js @@ -3,262 +3,313 @@ import { test } from '@agoric/zoe/tools/prepare-test-env-ava.js'; import '@agoric/zoe/exported.js'; import { E } from '@agoric/eventual-send'; - +import buildManualTimer from '@agoric/zoe/tools/manualTimer.js'; import { makeHandle } from '@agoric/zoe/src/makeHandle.js'; -import { makeBinaryBallotCounter } from '../../src/binaryBallotCounter.js'; -const QUESTION = 'Fish or cut bait?'; -const FISH = 'Fish'; -const BAIT = 'Cut Bait'; +import { makeBinaryVoteCounter } from '../../src/binaryVoteCounter.js'; +import { + ChoiceMethod, + ElectionType, + QuorumRule, + looksLikeQuestionSpec, + makeParamChangePositions, +} from '../../src/index.js'; + +const ISSUE = harden({ text: 'Fish or cut bait?' }); +const FISH = harden({ text: 'Fish' }); +const BAIT = harden({ text: 'Cut Bait' }); + +const PARAM_CHANGE_SPEC = { parameterName: 'arbitrary', key: 'simple' }; +const { positive, negative } = makeParamChangePositions(PARAM_CHANGE_SPEC, 37); +const PARAM_CHANGE_ISSUE = harden({ + paramSpec: PARAM_CHANGE_SPEC, + contract: makeHandle('Instance'), + proposedValue: 37, +}); -test('binary ballot', async t => { - const { publicFacet, creatorFacet, closeFacet } = makeBinaryBallotCounter( - QUESTION, - [FISH, BAIT], - 1n, +const FAKE_CLOSING_RULE = { + timer: buildManualTimer(console.log), + deadline: 3n, +}; + +const FAKE_COUNTER_INSTANCE = makeHandle('Instance'); + +test('binary question', async t => { + const questionSpec = looksLikeQuestionSpec({ + method: ChoiceMethod.UNRANKED, + issue: ISSUE, + positions: [FISH, BAIT], + electionType: ElectionType.SURVEY, + maxChoices: 1, + closingRule: FAKE_CLOSING_RULE, + quorumRule: QuorumRule.NO_QUORUM, + tieOutcome: BAIT, + }); + const { publicFacet, creatorFacet, closeFacet } = makeBinaryVoteCounter( + questionSpec, + 0n, + FAKE_COUNTER_INSTANCE, ); - const voterFacet = E(creatorFacet).getVoterFacet(); - const aliceTemplate = publicFacet.getBallotTemplate(); + const aliceTemplate = publicFacet.getQuestion(); const aliceSeat = makeHandle('Voter'); const alicePositions = aliceTemplate.getDetails().positions; t.deepEqual(alicePositions.length, 2); t.deepEqual(alicePositions[0], FISH); - await E(voterFacet).submitVote( - aliceSeat, - aliceTemplate.choose([alicePositions[0]]), - ); + await E(creatorFacet).submitVote(aliceSeat, [alicePositions[0]]); closeFacet.closeVoting(); const outcome = await E(publicFacet).getOutcome(); t.deepEqual(outcome, FISH); }); test('binary spoiled', async t => { - const { publicFacet, creatorFacet } = makeBinaryBallotCounter( - QUESTION, - [FISH, BAIT], + const questionSpec = looksLikeQuestionSpec({ + method: ChoiceMethod.UNRANKED, + issue: ISSUE, + positions: [FISH, BAIT], + electionType: ElectionType.ELECTION, + maxChoices: 1, + closingRule: FAKE_CLOSING_RULE, + quorumRule: QuorumRule.NO_QUORUM, + tieOutcome: BAIT, + }); + const { publicFacet, creatorFacet } = makeBinaryVoteCounter( + questionSpec, 0n, + FAKE_COUNTER_INSTANCE, ); - const voterFacet = E(creatorFacet).getVoterFacet(); - const aliceTemplate = publicFacet.getBallotTemplate(); + const aliceTemplate = publicFacet.getQuestion(); const aliceSeat = makeHandle('Voter'); const alicePositions = aliceTemplate.getDetails().positions; t.deepEqual(alicePositions.length, 2); t.deepEqual(alicePositions[0], FISH); + await t.throwsAsync( - () => - E(voterFacet).submitVote(aliceSeat, { - question: QUESTION, - chosen: ['no'], - }), + () => E(creatorFacet).submitVote(aliceSeat, [harden({ text: 'no' })]), { - message: `The ballot's choice is not a legal position: "no".`, + message: `The specified choice is not a legal position: {"text":"no"}.`, }, ); }); test('binary tied', async t => { - const { publicFacet, creatorFacet, closeFacet } = makeBinaryBallotCounter( - QUESTION, - [FISH, BAIT], - 2n, - ); - const voterFacet = E(creatorFacet).getVoterFacet(); - const aliceTemplate = publicFacet.getBallotTemplate(); - const aliceSeat = makeHandle('Voter'); - const bobSeat = makeHandle('Voter'); - - const positions = aliceTemplate.getDetails().positions; - E(voterFacet).submitVote(aliceSeat, aliceTemplate.choose([positions[0]])); - await E(voterFacet).submitVote(bobSeat, aliceTemplate.choose([positions[1]])); - closeFacet.closeVoting(); - const outcome = await E(publicFacet).getOutcome(); - t.deepEqual(outcome, undefined); -}); - -test('binary tied w/fallback', async t => { - const { publicFacet, creatorFacet, closeFacet } = makeBinaryBallotCounter( - QUESTION, - [FISH, BAIT], + const questionSpec = looksLikeQuestionSpec({ + method: ChoiceMethod.UNRANKED, + issue: PARAM_CHANGE_ISSUE, + positions: [positive, negative], + electionType: ElectionType.PARAM_CHANGE, + maxChoices: 1, + closingRule: FAKE_CLOSING_RULE, + quorumRule: QuorumRule.NO_QUORUM, + tieOutcome: negative, + }); + const { publicFacet, creatorFacet, closeFacet } = makeBinaryVoteCounter( + questionSpec, 2n, - BAIT, + FAKE_COUNTER_INSTANCE, ); - const voterFacet = E(creatorFacet).getVoterFacet(); - const aliceTemplate = publicFacet.getBallotTemplate(); + const aliceTemplate = publicFacet.getQuestion(); const aliceSeat = makeHandle('Voter'); const bobSeat = makeHandle('Voter'); const positions = aliceTemplate.getDetails().positions; - E(voterFacet).submitVote(aliceSeat, aliceTemplate.choose([positions[0]])); - await E(voterFacet).submitVote(bobSeat, aliceTemplate.choose([positions[1]])); + E(creatorFacet).submitVote(aliceSeat, [positions[0]]); + await E(creatorFacet).submitVote(bobSeat, [positions[1]]); closeFacet.closeVoting(); const outcome = await E(publicFacet).getOutcome(); - t.deepEqual(outcome, BAIT); + t.deepEqual(outcome, negative); }); test('binary bad vote', async t => { - const { publicFacet, creatorFacet } = makeBinaryBallotCounter( - QUESTION, - [FISH, BAIT], - 1n, - ); - const voterFacet = E(creatorFacet).getVoterFacet(); - const aliceTemplate = publicFacet.getBallotTemplate(); - const aliceSeat = makeHandle('Voter'); - - t.throws( - () => E(voterFacet).submitVote(aliceSeat, aliceTemplate.choose(['worms'])), - { - message: - 'Some positions in ["worms"] are not valid in ["Fish","Cut Bait"]', - }, + const questionSpec = looksLikeQuestionSpec({ + method: ChoiceMethod.UNRANKED, + issue: PARAM_CHANGE_ISSUE, + positions: [positive, negative], + electionType: ElectionType.PARAM_CHANGE, + maxChoices: 1, + closingRule: FAKE_CLOSING_RULE, + quorumRule: QuorumRule.NO_QUORUM, + tieOutcome: negative, + }); + const { creatorFacet } = makeBinaryVoteCounter( + questionSpec, + 0n, + FAKE_COUNTER_INSTANCE, ); -}); - -test('binary counter does not match ballot', async t => { - const { creatorFacet } = makeBinaryBallotCounter(QUESTION, [FISH, BAIT], 1n); - const voterFacet = E(creatorFacet).getVoterFacet(); const aliceSeat = makeHandle('Voter'); - await t.throwsAsync( - () => - E(voterFacet).submitVote(aliceSeat, { - question: 'Hop, skip or jump?', - chosen: [FISH], - }), - { - message: - 'Ballot not for this question "Hop, skip or jump?" should have been "Fish or cut bait?"', - }, - ); - await t.throwsAsync( - () => - E(voterFacet).submitVote(aliceSeat, { - question: QUESTION, - chosen: ['jump'], - }), - { - message: `The ballot's choice is not a legal position: "jump".`, - }, - ); + await t.throwsAsync(() => E(creatorFacet).submitVote(aliceSeat, [BAIT]), { + message: `The specified choice is not a legal position: {"text":"Cut Bait"}.`, + }); }); test('binary no votes', async t => { - const { publicFacet, closeFacet } = makeBinaryBallotCounter( - QUESTION, - [FISH, BAIT], + const questionSpec = looksLikeQuestionSpec({ + method: ChoiceMethod.UNRANKED, + issue: PARAM_CHANGE_ISSUE, + positions: [positive, negative], + electionType: ElectionType.PARAM_CHANGE, + maxChoices: 1, + closingRule: FAKE_CLOSING_RULE, + quorumRule: QuorumRule.NO_QUORUM, + tieOutcome: negative, + }); + const { publicFacet, closeFacet } = makeBinaryVoteCounter( + questionSpec, 0n, + FAKE_COUNTER_INSTANCE, ); closeFacet.closeVoting(); const outcome = await E(publicFacet).getOutcome(); - t.deepEqual(outcome, undefined); + t.deepEqual(outcome, negative); }); test('binary varying share weights', async t => { - const { publicFacet, creatorFacet, closeFacet } = makeBinaryBallotCounter( - QUESTION, - [FISH, BAIT], + const questionSpec = looksLikeQuestionSpec({ + method: ChoiceMethod.UNRANKED, + issue: ISSUE, + positions: [positive, negative], + electionType: ElectionType.SURVEY, + maxChoices: 1, + closingRule: FAKE_CLOSING_RULE, + quorumRule: QuorumRule.NO_QUORUM, + tieOutcome: negative, + }); + const { publicFacet, creatorFacet, closeFacet } = makeBinaryVoteCounter( + questionSpec, 1n, + FAKE_COUNTER_INSTANCE, ); - const voterFacet = E(creatorFacet).getVoterFacet(); - const template = publicFacet.getBallotTemplate(); const aceSeat = makeHandle('Voter'); const austinSeat = makeHandle('Voter'); const saraSeat = makeHandle('Voter'); await Promise.all([ - E(voterFacet).submitVote(aceSeat, template.choose([FISH]), 37n), - E(voterFacet).submitVote(austinSeat, template.choose([BAIT]), 24n), - E(voterFacet).submitVote(saraSeat, template.choose([BAIT]), 11n), + E(creatorFacet).submitVote(aceSeat, [positive], 37n), + E(creatorFacet).submitVote(austinSeat, [negative], 24n), + E(creatorFacet).submitVote(saraSeat, [negative], 11n), ]); closeFacet.closeVoting(); const outcome = await E(publicFacet).getOutcome(); - t.deepEqual(outcome, 'Fish'); + t.deepEqual(outcome, positive); }); test('binary contested', async t => { - const { publicFacet, creatorFacet, closeFacet } = makeBinaryBallotCounter( - QUESTION, - [FISH, BAIT], + const questionSpec = looksLikeQuestionSpec({ + method: ChoiceMethod.UNRANKED, + issue: ISSUE, + positions: [positive, negative], + electionType: ElectionType.ELECTION, + maxChoices: 1, + closingRule: FAKE_CLOSING_RULE, + quorumRule: QuorumRule.NO_QUORUM, + tieOutcome: negative, + }); + const { publicFacet, creatorFacet, closeFacet } = makeBinaryVoteCounter( + questionSpec, 3n, + FAKE_COUNTER_INSTANCE, ); - const voterFacet = E(creatorFacet).getVoterFacet(); - const template = publicFacet.getBallotTemplate(); + const template = publicFacet.getQuestion(); const aliceSeat = makeHandle('Voter'); const bobSeat = makeHandle('Voter'); const positions = template.getDetails().positions; t.deepEqual(positions.length, 2); - E(voterFacet).submitVote(aliceSeat, template.choose([positions[0]]), 23n); - await E(voterFacet).submitVote(bobSeat, template.choose([positions[1]]), 47n); + E(creatorFacet).submitVote(aliceSeat, [positions[0]], 23n); + await E(creatorFacet).submitVote(bobSeat, [positions[1]], 47n); closeFacet.closeVoting(); const outcome = await E(publicFacet).getOutcome(); - t.deepEqual(outcome, BAIT); + t.deepEqual(outcome, negative); }); test('binary revote', async t => { - const { publicFacet, creatorFacet, closeFacet } = makeBinaryBallotCounter( - QUESTION, - [FISH, BAIT], + const questionSpec = looksLikeQuestionSpec({ + method: ChoiceMethod.UNRANKED, + issue: PARAM_CHANGE_ISSUE, + positions: [positive, negative], + electionType: ElectionType.PARAM_CHANGE, + maxChoices: 1, + closingRule: FAKE_CLOSING_RULE, + quorumRule: QuorumRule.NO_QUORUM, + tieOutcome: negative, + }); + const { publicFacet, creatorFacet, closeFacet } = makeBinaryVoteCounter( + questionSpec, 5n, + FAKE_COUNTER_INSTANCE, ); - const voterFacet = E(creatorFacet).getVoterFacet(); - const template = publicFacet.getBallotTemplate(); + const template = publicFacet.getQuestion(); const aliceSeat = makeHandle('Voter'); const bobSeat = makeHandle('Voter'); const positions = template.getDetails().positions; t.deepEqual(positions.length, 2); - E(voterFacet).submitVote(aliceSeat, template.choose([positions[0]]), 23n); - E(voterFacet).submitVote(bobSeat, template.choose([positions[1]]), 47n); - await E(voterFacet).submitVote(bobSeat, template.choose([positions[1]]), 15n); + E(creatorFacet).submitVote(aliceSeat, [positions[0]], 23n); + E(creatorFacet).submitVote(bobSeat, [positions[1]], 47n); + await E(creatorFacet).submitVote(bobSeat, [positions[1]], 15n); closeFacet.closeVoting(); const outcome = await E(publicFacet).getOutcome(); - t.deepEqual(outcome, FISH); + t.deepEqual(outcome, positive); }); -test('binary ballot too many', async t => { - const { publicFacet, creatorFacet } = makeBinaryBallotCounter( - QUESTION, - [FISH, BAIT], +test('binary question too many', async t => { + const questionSpec = looksLikeQuestionSpec({ + method: ChoiceMethod.UNRANKED, + issue: ISSUE, + positions: [FISH, BAIT], + electionType: ElectionType.SURVEY, + maxChoices: 1, + closingRule: FAKE_CLOSING_RULE, + quorumRule: QuorumRule.NO_QUORUM, + tieOutcome: BAIT, + }); + const { publicFacet, creatorFacet } = makeBinaryVoteCounter( + questionSpec, 1n, + FAKE_COUNTER_INSTANCE, ); - const voterFacet = E(creatorFacet).getVoterFacet(); - const aliceTemplate = publicFacet.getBallotTemplate(); + const aliceTemplate = publicFacet.getQuestion(); const aliceSeat = makeHandle('Voter'); const alicePositions = aliceTemplate.getDetails().positions; - t.throws( - () => - E(voterFacet).submitVote(aliceSeat, aliceTemplate.choose(alicePositions)), + await t.throwsAsync( + // @ts-ignore illegal value for testing + () => E(creatorFacet).submitVote(aliceSeat, alicePositions), { - message: 'only 1 position(s) allowed', + message: 'only 1 position allowed', }, ); }); test('binary no quorum', async t => { - const { publicFacet, creatorFacet, closeFacet } = makeBinaryBallotCounter( - QUESTION, - [FISH, BAIT], + const questionSpec = looksLikeQuestionSpec({ + method: ChoiceMethod.UNRANKED, + issue: ISSUE, + positions: [FISH, BAIT], + electionType: ElectionType.ELECTION, + maxChoices: 1, + closingRule: FAKE_CLOSING_RULE, + quorumRule: QuorumRule.NO_QUORUM, + tieOutcome: BAIT, + }); + const { publicFacet, creatorFacet, closeFacet } = makeBinaryVoteCounter( + questionSpec, 2n, + FAKE_COUNTER_INSTANCE, ); - const voterFacet = E(creatorFacet).getVoterFacet(); - const aliceTemplate = publicFacet.getBallotTemplate(); + const aliceTemplate = publicFacet.getQuestion(); const aliceSeat = makeHandle('Voter'); const positions = aliceTemplate.getDetails().positions; - await E(voterFacet).submitVote( - aliceSeat, - aliceTemplate.choose([positions[0]]), - ); + await E(creatorFacet).submitVote(aliceSeat, [positions[0]]); closeFacet.closeVoting(); await E(publicFacet) .getOutcome() @@ -267,8 +318,21 @@ test('binary no quorum', async t => { }); test('binary too many positions', async t => { - t.throws(() => makeBinaryBallotCounter(QUESTION, [FISH, BAIT, 'sleep'], 1n), { - message: - 'Binary ballots must have exactly two positions. had 3: ["Fish","Cut Bait","sleep"]', + const questionSpec = looksLikeQuestionSpec({ + method: ChoiceMethod.UNRANKED, + issue: ISSUE, + positions: [FISH, BAIT, harden({ text: 'sleep' })], + electionType: ElectionType.SURVEY, + maxChoices: 1, + closingRule: FAKE_CLOSING_RULE, + quorumRule: QuorumRule.NO_QUORUM, + tieOutcome: BAIT, }); + t.throws( + () => makeBinaryVoteCounter(questionSpec, 0n, FAKE_COUNTER_INSTANCE), + { + message: + 'Binary questions must have exactly two positions. had 3: [{"text":"Fish"},{"text":"Cut Bait"},{"text":"sleep"}]', + }, + ); }); diff --git a/packages/governance/test/unitTests/test-committee.js b/packages/governance/test/unitTests/test-committee.js index e39d97898a3..38b3879d4a8 100644 --- a/packages/governance/test/unitTests/test-committee.js +++ b/packages/governance/test/unitTests/test-committee.js @@ -11,103 +11,119 @@ import fakeVatAdmin from '@agoric/zoe/tools/fakeVatAdmin.js'; import bundleSource from '@agoric/bundle-source'; import buildManualTimer from '@agoric/zoe/tools/manualTimer.js'; -import { ChoiceMethod } from '../../src/ballotBuilder.js'; +import { + ChoiceMethod, + ElectionType, + QuorumRule, + looksLikeQuestionSpec, +} from '../../src/index.js'; const filename = new URL(import.meta.url).pathname; const dirname = path.dirname(filename); -const registrarRoot = `${dirname}/../../src/committeeRegistrar.js`; -const counterRoot = `${dirname}/../../src/binaryBallotCounter.js`; +const electorateRoot = `${dirname}/../../src/committee.js`; +const counterRoot = `${dirname}/../../src/binaryVoteCounter.js`; -async function setupContract() { +const setupContract = async () => { const { zoeService } = makeZoeKit(fakeVatAdmin); const feePurse = E(zoeService).makeFeePurse(); const zoe = E(zoeService).bindDefaultFeePurse(feePurse); // pack the contract - const [registrarBundle, counterBundle] = await Promise.all([ - bundleSource(registrarRoot), + const [electorateBundle, counterBundle] = await Promise.all([ + bundleSource(electorateRoot), bundleSource(counterRoot), ]); // install the contract - const [registrarInstallation, counterInstallation] = await Promise.all([ - E(zoe).install(registrarBundle), + const [electorateInstallation, counterInstallation] = await Promise.all([ + E(zoe).install(electorateBundle), E(zoe).install(counterBundle), ]); const terms = { committeeName: 'illuminati', committeeSize: 13 }; - const registrarStartResult = await E(zoe).startInstance( - registrarInstallation, + const electorateStartResult = await E(zoe).startInstance( + electorateInstallation, {}, terms, ); /** @type {ContractFacet} */ - return { registrarStartResult, counterInstallation }; -} + return { electorateStartResult, counterInstallation }; +}; -test('committee-open questions:none', async t => { +test('committee-open no questions', async t => { const { - registrarStartResult: { publicFacet }, + electorateStartResult: { publicFacet }, } = await setupContract(); t.deepEqual(await publicFacet.getOpenQuestions(), []); }); test('committee-open question:one', async t => { const { - registrarStartResult: { creatorFacet, publicFacet }, + electorateStartResult: { creatorFacet, publicFacet }, counterInstallation, } = await setupContract(); - const details = harden({ - method: ChoiceMethod.CHOOSE_N, - question: 'why', - positions: ['because', 'why not?'], + const positions = [harden({ text: 'because' }), harden({ text: 'why not?' })]; + const questionSpec = looksLikeQuestionSpec({ + method: ChoiceMethod.UNRANKED, + issue: harden({ text: 'why' }), + positions, + electionType: ElectionType.SURVEY, maxChoices: 1, closingRule: { timer: buildManualTimer(console.log), deadline: 2n, }, + quorumRule: QuorumRule.MAJORITY, + tieOutcome: positions[1], }); - await E(creatorFacet).addQuestion(counterInstallation, details); - t.deepEqual(await publicFacet.getOpenQuestions(), ['why']); + await E(creatorFacet).addQuestion(counterInstallation, questionSpec); + const questions = await publicFacet.getOpenQuestions(); + const question = E(publicFacet).getQuestion(questions[0]); + const questionDetails = await E(question).getDetails(); + t.deepEqual(questionDetails.issue.text, 'why'); }); test('committee-open question:mixed', async t => { const { - registrarStartResult: { creatorFacet, publicFacet }, + electorateStartResult: { creatorFacet, publicFacet }, counterInstallation, } = await setupContract(); const timer = buildManualTimer(console.log); - const details = harden({ - method: ChoiceMethod.CHOOSE_N, - question: 'why', - positions: ['because', 'why not?'], + const positions = [harden({ text: 'because' }), harden({ text: 'why not?' })]; + const questionSpec = looksLikeQuestionSpec({ + method: ChoiceMethod.UNRANKED, + issue: harden({ text: 'why' }), + positions, + electionType: ElectionType.SURVEY, maxChoices: 1, - closingRule: { - timer, - deadline: 4n, - }, + closingRule: { timer, deadline: 4n }, + quorumRule: QuorumRule.MAJORITY, + tieOutcome: positions[1], }); - await E(creatorFacet).addQuestion(counterInstallation, details); - - const details2 = harden({ - ...details, - question: 'why2', - }); - await E(creatorFacet).addQuestion(counterInstallation, details2); - - const details3 = harden({ - ...details, - question: 'why3', + await E(creatorFacet).addQuestion(counterInstallation, questionSpec); + + const questionSpec2 = { + ...questionSpec, + issue: harden({ text: 'why2' }), + closingRule: questionSpec.closingRule, + quorumRule: QuorumRule.MAJORITY, + }; + await E(creatorFacet).addQuestion(counterInstallation, questionSpec2); + + const questionSpec3 = { + ...questionSpec, + issue: harden({ text: 'why3' }), closingRule: { timer, deadline: 1n, }, - }); + quorumRule: QuorumRule.MAJORITY, + }; const { publicFacet: counterPublic } = await E(creatorFacet).addQuestion( counterInstallation, - details3, + questionSpec3, ); // We didn't add any votes. getOutcome() will eventually return a broken // promise, but not until some time after tick(). Add a .catch() for it. @@ -117,5 +133,6 @@ test('committee-open question:mixed', async t => { timer.tick(); - t.deepEqual(await publicFacet.getOpenQuestions(), ['why', 'why2']); + const questions = await publicFacet.getOpenQuestions(); + t.deepEqual(questions.length, 2); }); diff --git a/packages/governance/test/test-param-manager.js b/packages/governance/test/unitTests/test-param-manager.js similarity index 50% rename from packages/governance/test/test-param-manager.js rename to packages/governance/test/unitTests/test-param-manager.js index d434de5cbf6..290270d3568 100644 --- a/packages/governance/test/test-param-manager.js +++ b/packages/governance/test/unitTests/test-param-manager.js @@ -6,7 +6,9 @@ import { AmountMath, AssetKind, makeIssuerKit } from '@agoric/ertp'; import { makeRatio } from '@agoric/zoe/src/contractSupport/index.js'; import { makeHandle } from '@agoric/zoe/src/makeHandle.js'; -import { buildParamManager, ParamType } from '../src/paramManager.js'; +import { Far } from '@agoric/marshal'; +import { buildParamManager, ParamType } from '../../src/paramManager.js'; +import { makeParamChangePositions } from '../../src/governParam.js'; const BASIS_POINTS = 10_000n; @@ -17,10 +19,10 @@ test('params one Nat', async t => { value: 13n, type: ParamType.NAT, }; - const { getParams, updateNumber } = buildParamManager([numberDescription]); - t.deepEqual(getParams()[numberKey], numberDescription); + const { getParam, updateNumber } = buildParamManager([numberDescription]); + t.deepEqual(getParam(numberKey), numberDescription); updateNumber(42n); - t.deepEqual(getParams()[numberKey].value, 42n); + t.deepEqual(getParam(numberKey).value, 42n); t.throws( () => updateNumber(18.1), @@ -45,10 +47,10 @@ test('params one String', async t => { value: 'foo', type: ParamType.STRING, }; - const { getParams, updateString } = buildParamManager([stringDescription]); - t.deepEqual(getParams()[stringKey], stringDescription); + const { getParam, updateString } = buildParamManager([stringDescription]); + t.deepEqual(getParam(stringKey), stringDescription); updateString('bar'); - t.deepEqual(getParams()[stringKey].value, 'bar'); + t.deepEqual(getParam(stringKey).value, 'bar'); t.throws( () => updateString(18.1), @@ -67,10 +69,10 @@ test('params one Amount', async t => { value: AmountMath.makeEmpty(brand), type: ParamType.AMOUNT, }; - const { getParams, updateAmount } = buildParamManager([amountDescription]); - t.deepEqual(getParams()[amountKey], amountDescription); + const { getParam, updateAmount } = buildParamManager([amountDescription]); + t.deepEqual(getParam(amountKey), amountDescription); updateAmount(AmountMath.make(brand, [13])); - t.deepEqual(getParams()[amountKey].value, AmountMath.make(brand, [13])); + t.deepEqual(getParam(amountKey).value, AmountMath.make(brand, [13])); t.throws( () => updateAmount(18.1), @@ -88,10 +90,10 @@ test('params one BigInt', async t => { value: 314159n, type: ParamType.NAT, }; - const { getParams, updateBigint } = buildParamManager([bigIntDescription]); - t.deepEqual(getParams()[bigintKey], bigIntDescription); + const { getParam, updateBigint } = buildParamManager([bigIntDescription]); + t.deepEqual(getParam(bigintKey), bigIntDescription); updateBigint(271828182845904523536n); - t.deepEqual(getParams()[bigintKey].value, 271828182845904523536n); + t.deepEqual(getParam(bigintKey).value, 271828182845904523536n); t.throws( () => updateBigint(18.1), @@ -117,8 +119,12 @@ test('params one ratio', async t => { value: makeRatio(7n, brand), type: ParamType.RATIO, }; - const { getParams, updateRatio } = buildParamManager([ratioDescription]); - t.deepEqual(getParams()[ratioKey], ratioDescription); + + const { getParam, getParams, updateRatio } = buildParamManager([ + ratioDescription, + ]); + // t.deepEqual(getParams()[ratioKey], ratioDescription); + t.deepEqual(getParam(ratioKey), ratioDescription); updateRatio(makeRatio(701n, brand, BASIS_POINTS)); t.deepEqual( getParams()[ratioKey].value, @@ -128,7 +134,7 @@ test('params one ratio', async t => { t.throws( () => updateRatio(18.1), { - message: 'Ratio 18.1 must be a record with 2 fields.', + message: 'Expected "number" is same as "copyRecord"', }, 'value should be a ratio', ); @@ -143,10 +149,10 @@ test('params one brand', async t => { value: roseBrand, type: ParamType.BRAND, }; - const { getParams, updateBrand } = buildParamManager([brandDescription]); - t.deepEqual(getParams()[brandKey], brandDescription); + const { getParam, updateBrand } = buildParamManager([brandDescription]); + t.deepEqual(getParam(brandKey), brandDescription); updateBrand(thornBrand); - t.deepEqual(getParams()[brandKey].value, thornBrand); + t.deepEqual(getParam(brandKey).value, thornBrand); t.throws( () => updateBrand(18.1), @@ -165,10 +171,10 @@ test('params one unknown', async t => { value: stiltonBrand, type: ParamType.UNKNOWN, }; - const { getParams, updateStuff } = buildParamManager([stuffDescription]); - t.deepEqual(getParams()[stuffKey], stuffDescription); + const { getParam, updateStuff } = buildParamManager([stuffDescription]); + t.deepEqual(getParam(stuffKey), stuffDescription); updateStuff(18.1); - t.deepEqual(getParams()[stuffKey].value, 18.1); + t.deepEqual(getParam(stuffKey).value, 18.1); }); test('params one instance', async t => { @@ -181,10 +187,8 @@ test('params one instance', async t => { value: instanceHandle, type: ParamType.INSTANCE, }; - const { getParams, updateInstance } = buildParamManager([ - instanceDescription, - ]); - t.deepEqual(getParams()[instanceKey], instanceDescription); + const { getParam, updateInstance } = buildParamManager([instanceDescription]); + t.deepEqual(getParam(instanceKey), instanceDescription); t.throws( () => updateInstance(18.1), { @@ -194,23 +198,25 @@ test('params one instance', async t => { ); const handle2 = makeHandle('another Instance'); updateInstance(handle2); - t.deepEqual(getParams()[instanceKey].value, handle2); + t.deepEqual(getParam(instanceKey).value, handle2); }); test('params one installation', async t => { const installationKey = 'Installation'; // this is sufficient for the current type check. When we add // isInstallation() (#3344), we'll need to make a mockZoe. - const installationHandle = makeHandle('installation'); + const installationHandle = Far('fake Installation', { + getBundle: () => ({ obfuscated: 42 }), + }); const installationDescription = { name: installationKey, value: installationHandle, type: ParamType.INSTALLATION, }; - const { getParams, updateInstallation } = buildParamManager([ + const { getParam, updateInstallation } = buildParamManager([ installationDescription, ]); - t.deepEqual(getParams()[installationKey], installationDescription); + t.deepEqual(getParam(installationKey), installationDescription); t.throws( () => updateInstallation(18.1), { @@ -218,9 +224,11 @@ test('params one installation', async t => { }, 'value should be an installation', ); - const handle2 = makeHandle('another installation'); + const handle2 = Far('another fake Installation', { + getBundle: () => ({ condensed: '() => {})' }), + }); updateInstallation(handle2); - t.deepEqual(getParams()[installationKey].value, handle2); + t.deepEqual(getParam(installationKey).value, handle2); }); test('params duplicate entry', async t => { @@ -273,23 +281,123 @@ test('params multiple values', t => { value: 602214076000000000000000n, type: ParamType.NAT, }; - const { getParams, updateNat, updateStuff } = buildParamManager([ + const { getParams, getParam, updateNat, updateStuff } = buildParamManager([ cheeseDescription, constantDescription, ]); - t.deepEqual(getParams()[stuffKey], cheeseDescription); + t.deepEqual(getParam(stuffKey), cheeseDescription); updateStuff(18.1); const floatDescription = { name: stuffKey, value: 18.1, type: ParamType.UNKNOWN, }; - t.deepEqual(getParams()[stuffKey], floatDescription); - t.deepEqual(getParams()[natKey], constantDescription); + t.deepEqual(getParam(stuffKey), floatDescription); + t.deepEqual(getParam(natKey), constantDescription); t.deepEqual(getParams(), { Nat: constantDescription, Stuff: floatDescription, }); updateNat(299792458n); - t.deepEqual(getParams()[natKey].value, 299792458n); + t.deepEqual(getParam(natKey).value, 299792458n); +}); + +const positive = (name, val) => { + return { changeParam: name, proposedValue: val }; +}; + +const negative = name => { + return { noChange: name }; +}; + +test('positions amount', t => { + const amountSpec = { parameterName: 'amount', key: 'something' }; + const { brand } = makeIssuerKit('roses', AssetKind.SET); + const amount = AmountMath.makeEmpty(brand); + + const positions = makeParamChangePositions(amountSpec, amount); + t.deepEqual(positions.positive, positive(amountSpec, amount)); + t.deepEqual(positions.negative, negative(amountSpec)); + t.notDeepEqual(positions.positive, positive(AmountMath.make(brand, [1]))); +}); + +test('positions brand', t => { + const brandSpec = { parameterName: 'brand', key: 'params' }; + const { brand: roseBrand } = makeIssuerKit('roses', AssetKind.SET); + const { brand: thornBrand } = makeIssuerKit('thorns', AssetKind.SET); + + const positions = makeParamChangePositions(brandSpec, roseBrand); + t.deepEqual(positions.positive, positive(brandSpec, roseBrand)); + t.deepEqual(positions.negative, negative(brandSpec)); + t.not(positions.positive, positive(brandSpec, thornBrand)); +}); + +test('positions instance', t => { + const instanceSpec = { parameterName: 'instance', key: 'something' }; + // this is sufficient for the current type check. When we add + // isInstallation() (#3344), we'll need to make a mockZoe. + const instanceHandle = makeHandle('Instance'); + + const positions = makeParamChangePositions(instanceSpec, instanceHandle); + t.deepEqual(positions.positive, positive(instanceSpec, instanceHandle)); + t.deepEqual(positions.negative, negative(instanceSpec)); + t.not(positions.positive, positive(instanceSpec, makeHandle('Instance'))); +}); + +test('positions Installation', t => { + const installationSpec = { parameterName: 'installation', key: 'something' }; + // this is sufficient for the current type check. When we add + // isInstallation() (#3344), we'll need to make a mockZoe. + const installationHandle = makeHandle('Installation'); + + const positions = makeParamChangePositions( + installationSpec, + installationHandle, + ); + t.deepEqual( + positions.positive, + positive(installationSpec, installationHandle), + ); + t.deepEqual(positions.negative, negative(installationSpec)); + t.not( + positions.positive, + positive(installationSpec, makeHandle('Installation')), + ); +}); + +test('positions Nat', t => { + const natSpec = { parameterName: 'nat', key: 'something' }; + const nat = 3n; + + const positions = makeParamChangePositions(natSpec, nat); + t.deepEqual(positions.positive, positive(natSpec, nat)); + t.deepEqual(positions.negative, negative(natSpec)); + t.notDeepEqual(positions.positive, positive(natSpec, 4n)); +}); + +test('positions Ratio', t => { + const ratioSpec = { parameterName: 'ratio', key: 'something' }; + const { brand } = makeIssuerKit('elo', AssetKind.NAT); + const ratio = makeRatio(2500n, brand, 2400n); + + const positions = makeParamChangePositions(ratioSpec, ratio); + t.deepEqual(positions.positive, positive(ratioSpec, ratio)); + t.deepEqual(positions.negative, negative(ratioSpec)); + t.notDeepEqual( + positions.positive, + positive(ratioSpec, makeRatio(2500n, brand, 2200n)), + ); +}); + +test('positions string', t => { + const stringSpec = { parameterName: 'string', key: 'something' }; + const string = 'When in the course'; + + const positions = makeParamChangePositions(stringSpec, string); + t.deepEqual(positions.positive, positive(stringSpec, string)); + t.deepEqual(positions.negative, negative(stringSpec)); + t.notDeepEqual( + positions.positive, + positive(stringSpec, 'We hold these truths'), + ); }); diff --git a/packages/governance/test/unitTests/test-paramGovernance.js b/packages/governance/test/unitTests/test-paramGovernance.js new file mode 100644 index 00000000000..4f11cfe14a8 --- /dev/null +++ b/packages/governance/test/unitTests/test-paramGovernance.js @@ -0,0 +1,241 @@ +// @ts-check + +import { test } from '@agoric/zoe/tools/prepare-test-env-ava.js'; +import '@agoric/zoe/exported.js'; + +import { makeZoeKit } from '@agoric/zoe'; +import bundleSource from '@agoric/bundle-source'; +import buildManualTimer from '@agoric/zoe/tools/manualTimer.js'; +import { Far } from '@agoric/marshal'; +import { makePromiseKit } from '@agoric/promise-kit'; +import fakeVatAdmin from '@agoric/zoe/tools/fakeVatAdmin.js'; +import { E } from '@agoric/eventual-send'; + +import { makeHandle } from '@agoric/zoe/src/makeHandle.js'; +import path from 'path'; + +import { + setupGovernance, + makeParamChangePositions, +} from '../../src/governParam.js'; +import { + governedParameterInitialValues, + MALLEABLE_NUMBER, +} from '../swingsetTests/contractGovernor/governedContract.js'; + +const filename = new URL(import.meta.url).pathname; +const dirname = path.dirname(filename); + +const voteCounterRoot = `${dirname}/../../src/binaryVoteCounter.js`; +const governedRoot = `${dirname}/../swingsetTests/contractGovernor/governedContract.js`; + +const makeInstall = async (sourceRoot, zoe) => { + const bundle = await bundleSource(sourceRoot); + return E(zoe).install(bundle); +}; + +test('governParam happy path with fakes', async t => { + const { zoeService } = makeZoeKit(fakeVatAdmin); + const feePurse = E(zoeService).makeFeePurse(); + const zoe = E(zoeService).bindDefaultFeePurse(feePurse); + + const timer = buildManualTimer(console.log); + + const governedInstall = await makeInstall(governedRoot, zoe); + const voteCounterInstall = await makeInstall(voteCounterRoot, zoe); + + const governedFacets = await E(zoe).startInstance( + governedInstall, + {}, + { main: governedParameterInitialValues }, + ); + const Retriever = governedFacets.creatorFacet.getParamMgrRetriever(); + + const paramSpec = { key: 'contractParams', parameterName: MALLEABLE_NUMBER }; + const { positive } = makeParamChangePositions(paramSpec, 25n); + + const fakeCounterPublic = Far('fake voteCounter public', { + getOutcome: () => positive, + getDetails: () => undefined, + }); + const questionPoser = Far('poser', { + addQuestion: () => { + return { + publicFacet: fakeCounterPublic, + instance: makeHandle('counter'), + }; + }, + }); + + const paramGovernor = setupGovernance( + Retriever, + // @ts-ignore questionPoser is a fake + questionPoser, + governedFacets.instance, + timer, + ); + + const { outcomeOfUpdate } = await E(paramGovernor).voteOnParamChange( + paramSpec, + 25n, + voteCounterInstall, + 2n, + ); + + await E.when(outcomeOfUpdate, outcome => t.is(outcome, 25n)).catch(e => + t.fail(e), + ); + + t.deepEqual(governedFacets.publicFacet.getGovernedParamsValues(), { + main: { + MalleableNumber: { + name: MALLEABLE_NUMBER, + type: 'nat', + value: 25n, + }, + }, + }); +}); + +test('governParam no votes', async t => { + const timer = buildManualTimer(console.log); + const { zoeService } = makeZoeKit(fakeVatAdmin); + const feePurse = E(zoeService).makeFeePurse(); + const zoe = E(zoeService).bindDefaultFeePurse(feePurse); + + const voteCounterInstall = await makeInstall(voteCounterRoot, zoe); + const governedInstall = await makeInstall(governedRoot, zoe); + + const governedFacets = await E(zoe).startInstance( + governedInstall, + {}, + { main: governedParameterInitialValues }, + ); + const Retriever = governedFacets.creatorFacet.getParamMgrRetriever(); + + const paramSpec = { key: 'contractParams', parameterName: MALLEABLE_NUMBER }; + + const outcomeKit = makePromiseKit(); + outcomeKit.reject('no quorum'); + + const fakeCounterPublic = Far('fake voteCounter public', { + getOutcome: () => outcomeKit.promise, + getDetails: () => undefined, + }); + + outcomeKit.promise.catch(() => {}); + fakeCounterPublic.getOutcome().catch(() => {}); + + const questionPoser = Far('poser', { + addQuestion: () => { + return { + publicFacet: fakeCounterPublic, + instance: makeHandle('counter'), + }; + }, + }); + + const paramGovernor = setupGovernance( + Retriever, + // @ts-ignore questionPoser is a fake + questionPoser, + governedFacets.instance, + timer, + ); + + const { outcomeOfUpdate } = await E(paramGovernor).voteOnParamChange( + paramSpec, + 25n, + voteCounterInstall, + 2n, + ); + + await E.when(outcomeOfUpdate, outcome => t.fail(`${outcome}`)).catch(e => + t.is(e, 'no quorum'), + ); + + t.deepEqual(governedFacets.publicFacet.getGovernedParamsValues(), { + main: { + MalleableNumber: { + name: MALLEABLE_NUMBER, + type: 'nat', + value: 602214090000000000000000n, + }, + }, + }); +}); + +test('governParam bad update', async t => { + const { zoeService } = makeZoeKit(fakeVatAdmin); + const feePurse = E(zoeService).makeFeePurse(); + const zoe = E(zoeService).bindDefaultFeePurse(feePurse); + const timer = buildManualTimer(console.log); + + const voteCounterInstall = await makeInstall(voteCounterRoot, zoe); + const governedInstall = await makeInstall(governedRoot, zoe); + + const governedFacets = await E(zoe).startInstance( + governedInstall, + {}, + { main: governedParameterInitialValues }, + ); + const brokenParamMgr = Far('broken ParamMgr', { + getParam: () => { + return harden({ type: 'nat' }); + }, + }); + const retriever = Far('paramMgr retriever', { + get: () => brokenParamMgr, + }); + + const paramSpec = { key: 'contractParams', parameterName: MALLEABLE_NUMBER }; + const { positive } = makeParamChangePositions(paramSpec, 25n); + + const fakeDetails = { stuff: 'nonsense' }; + const fakeCounterPublic = Far('fake voteCounter public', { + getOutcome: () => positive, + getDetails: () => fakeDetails, + }); + + const fakeVoteCounter = makeHandle('vote counter'); + const questionPoser = Far('poser', { + addQuestion: () => { + return { publicFacet: fakeCounterPublic, instance: fakeVoteCounter }; + }, + }); + + const paramGovernor = setupGovernance( + // @ts-ignore retriever is a fake + retriever, + questionPoser, + governedFacets.instance, + timer, + ); + + const { details, outcomeOfUpdate } = await E(paramGovernor).voteOnParamChange( + paramSpec, + 25n, + voteCounterInstall, + 2n, + ); + // @ts-ignore details are from a fake + t.deepEqual(await details, fakeDetails); + + await t.throwsAsync( + outcomeOfUpdate, + { + message: 'target has no method "updateMalleableNumber", has ["getParam"]', + }, + 'Expected a throw', + ); + + t.deepEqual(governedFacets.publicFacet.getGovernedParamsValues(), { + main: { + MalleableNumber: { + name: MALLEABLE_NUMBER, + type: 'nat', + value: 602214090000000000000000n, + }, + }, + }); +}); diff --git a/packages/zoe/src/contractSupport/ratio.js b/packages/zoe/src/contractSupport/ratio.js index 0be4ae05925..cdeb484485a 100644 --- a/packages/zoe/src/contractSupport/ratio.js +++ b/packages/zoe/src/contractSupport/ratio.js @@ -4,6 +4,8 @@ import './types.js'; import { assert, details as X, q } from '@agoric/assert'; import { Nat } from '@agoric/nat'; import { AmountMath } from '@agoric/ertp'; +import { passStyleOf } from '@agoric/marshal'; + import { natSafeMath } from './safeMath.js'; const { multiply, floorDivide, ceilDivide, add, subtract } = natSafeMath; @@ -38,6 +40,7 @@ const PERCENT = 100n; const ratioPropertyNames = ['numerator', 'denominator']; export const assertIsRatio = ratio => { + assert.equal(passStyleOf(ratio), 'copyRecord'); const propertyNames = Object.getOwnPropertyNames(ratio); assert( propertyNames.length === 2,

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 SxxsyhgayPOrrm$~=Klcvi*+CX literal 0 HcmV?d00001 diff --git a/packages/governance/docs/coreArchitecture.puml b/packages/governance/docs/coreArchitecture.puml new file mode 100644 index 00000000000..2346b92e673 --- /dev/null +++ b/packages/governance/docs/coreArchitecture.puml @@ -0,0 +1,116 @@ +@startuml governance invitation linkages + +package Legend <> #EEEEEE { + + class ContractA { + constract terms are documented above the line + -- + accessiblePublicState + + publicMethod() + # methodShared() + - closelyHeldMethod() + } + + interface InvitationB { + verifiable invitation fields are above the line + -- + offerResults - below the line + } + + note "Contracts have a 'C' marker.\nInvitations have an 'I'.\nblue arrows show verifiable connections.\ncreator-created links are labelled" as NC +} + +package "Electorate Vat" <> { + class Electorate { + terms: committeeSize, committeeName + -- + Questions[] + +getQuestionSubscription() + +getOpenQuestions() + +getQuestion(questionHandle) + #getVoterInvitation(): (via some mechanism) + -getQuestionPoserInvitation() + -addQuestion(voteCounterInstall, question, details) + } + + note "produces VoterInvitations.\nPolymorphic over VoteCounters.\nquestions are enumerable." as N1 + Electorate .. N1 + + interface QuestionPoserInvitation { + Electorate + -- + addQuestion() + } + + interface VoterInvitation { + Electorate + -- + getVoterFacet() + } + + object VoterFacet { + --- + castBallotFor(QuestionoHandle, ...positions) + } + note "instances held by\nindividual voters" as NVF + VoterFacet . NVF + + Electorate --> VoterFacet : creates > +} + +object QuestionDetails { + Method { UNRANKED | ORDER } + Type { ParamChange | Election | Survey } + -- + issue, positions, tieOutcome, maxChoices + quorumRule + closingRule: { timer, deadline } + questionHandle + counterInstance +} + +note "QuestionDetails is a widely accessible record.\nverifiable copies are obtained from an Electorate" as N3 +QuestionDetails .. N3 + +package "VoteCounter Vat" <> { + class VoteCounter { + terms\n {questionSpec, quorum, closingRule, tieOutcome} + -- + +getDetails() + +getOutcome(): Promise + +getStats() + +getQuestion() + #countVotes() + -submitVote() + -getCreatorFacet() + } + + object VoteCap { + --- + submitVote(VoterHandle, ...positions) + } + note top: VoteCap is passed to and\ntightly held by Electorate. + + note "unaware of voter registration.\n Only Electorate hands out voterFacets" as N2 + VoteCounter .. N2 + + VoteCounter --> VoteCap : creates > +} + +class ElectionManager { + Electorate + addQuestion() +} +note top : ElectionManager is responsible\n for letting an appropriate\n party call addQuestion() + +ElectionManager -.[#blue]-|> Electorate : verifiable + +Electorate *. VoterInvitation +Electorate *. QuestionPoserInvitation +VoterInvitation -> VoterFacet +Electorate -> QuestionDetails : creates > +VoteCounter <|-.[#blue]-|> QuestionDetails : verifiable + +VoterFacet --|> VoteCap : encapsulates + +@enduml \ No newline at end of file diff --git a/packages/governance/docs/example.png b/packages/governance/docs/example.png new file mode 100644 index 0000000000000000000000000000000000000000..105f9e3390ba08a848b59e9b09ce294d4ff7dbe8 GIT binary patch literal 50164 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