Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: durability for governance #8157

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion packages/ERTP/src/typeGuards.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const SetValueShape = M.arrayOf(M.key());
*/
const CopyBagValueShape = M.bag();

const AmountValueShape = M.or(
export const AmountValueShape = M.or(
NatValueShape,
CopySetValueShape,
SetValueShape,
Expand Down Expand Up @@ -228,3 +228,9 @@ export const makeIssuerInterfaces = (
});
};
harden(makeIssuerInterfaces);

/** @param {Amount} amount */
export const makeBrandedAmountPattern = amount => {
return { brand: amount.brand, value: M.nat() };
};
harden(makeBrandedAmountPattern);
4 changes: 2 additions & 2 deletions packages/agoric-cli/src/commands/auction.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import { outputActionAndHint } from '../lib/wallet.js';

const { Fail } = assert;

/** @typedef {import('@agoric/governance/src/contractGovernance/typedParamManager.js').ParamTypesMap} ParamTypesMap */
/** @typedef {import('@agoric/governance/src/contractGovernance/paramManager.js').ParamTypesMap} ParamTypesMap */

/**
* @template {ParamStateRecord} M
* @typedef {import('@agoric/governance/src/contractGovernance/typedParamManager.js').ParamTypesMapFromRecord<M>} ParamTypesMapFromRecord
* @typedef {import('@agoric/governance/src/contractGovernance/paramManager.js').ParamTypesMapFromRecord<M>} ParamTypesMapFromRecord
*/

/**
Expand Down
1 change: 1 addition & 0 deletions packages/boot/test/bootstrapTests/test-vaults-upgrade.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Offers } from '@agoric/inter-protocol/src/clientSupport.js';
import { Far, makeMarshal } from '@endo/marshal';
import { SECONDS_PER_YEAR } from '@agoric/inter-protocol/src/interest.js';
import { makeAgoricNamesRemotesFromFakeStorage } from '@agoric/vats/tools/board-utils.js';

import { makeSwingsetTestKit } from './supports.js';
import { makeWalletFactoryDriver } from './drivers.js';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"consume": {
"vatAdminSvc": true,
"vaultFactoryKit": true,
"chainStorage": true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// to turn on ts-check:
/* global E */

// import { E } from "@endo/far";

const GOV_BUNDLE_ID = 'b1-';
const VAULTS_BUNDLE_ID = 'b1-';

console.info('Vaults upgrade: evaluating script');

/*
* Test an upgrade of the VaultFactory and its governing contract.
*/
const upgradeVaultFactory = async powers => {
console.info('upgrade vaultFactory');
const {
consume: {
chainStorage,
vatAdminSvc,
vaultFactoryKit: {
governorAdminFacet,
adminFacet: vaultsAdminFacet,
instance,
privateArgs,
},
},
} = powers;

const newGovernorBundleCap = await E(vatAdminSvc).getBundleCap(GOV_BUNDLE_ID);
const newVaultsBundleCap = await E(vatAdminSvc).getBundleCap(
VAULTS_BUNDLE_ID,
);

// consider modifying privateArgs.
await E(governorAdminFacet).upgrade(newGovernorBundleCap, {});

// not write, but perhaps visible?
const storageNode = await E(chainStorage).makeChildNode('vaults');
await E(vaultsAdminFacet).upgrade(newVaultsBundleCap, { storageNode });
};

upgradeVaultFactory;
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/bin/bash

. ./upgrade-test-scripts/env_setup.sh

# Enable debugging
set -x

# CWD is agoric-sdk
upgrade11=./upgrade-test-scripts/agoric-upgrade-11

yarn --silent bundle-source --cache-json /tmp packages/governance/src/contractGovernor.js contractGovernor-upgrade
yarn --silent bundle-source --cache-json /tmp packages/inter-protocol/src/psm/psm.js psm-upgrade
yarn --silent bundle-source --cache-json /tmp packages/inter-protocol/src/reserve/reserve.js reserve-upgrade
yarn --silent bundle-source --cache-json /tmp packages/inter-protocol/src/auction/auctioneer.js auctioneer-upgrade
yarn --silent bundle-source --cache-json /tmp packages/inter-protocol/src/vaultFactory/vaultFactory.js vaultFactory-upgrade

# fluxAggregator, smartWallet

# Start by upgrading the governance facet, which will do a null upgrad on the
# contract, and then upgrade the contract itself.


GOV_HASH=`jq -r .endoZipBase64Sha512 /tmp/bundle-contractGovernor-upgrade.json
echo bundle-contractGovernor-upgrade.json $GOV_HASH
PSM_HASH=`jq -r .endoZipBase64Sha512 /tmp/bundle-psm-upgrade.json
echo bundle-psm-upgrade.json $PSM_HASH
RESERVE_HASH=`jq -r .endoZipBase64Sha512 /tmp/bundle-reserve-upgrade.json
echo bundle-reserve-upgrade.json $RESERVE_HASH
AUCTIONEER_HASH=`jq -r .endoZipBase64Sha512 /tmp/bundle-auctioneer-upgrade.json
echo bundle-auctioneer-upgrade.json $AUCTIONEER_HASH
VAULTS_HASH=`jq -r .endozipbase64sha512 /tmp/bundle-vaultfactory-upgrade.json
echo bundle-vaultfactory-upgrade.json $vaults_hash
# grep for hashes in upgrade scripts



echo +++++ install bundles +++++
for f in /tmp/bundle-[a-v]*-upgrade.json; do
echo installing $f
agd tx swingset install-bundle "@$f" \
--from gov1 --keyring-backend=test --gas=auto \
--chain-id=agoriclocal -bblock --yes
done

69 changes: 36 additions & 33 deletions packages/governance/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ the Electorate is a required parameter in all governed contracts. Invitations
are an unusual kind of managed parameter. Most parameters are copy-objects that
don't carry any power. Since invitations convey rights, only the
invitation's amount appears in `terms`. The actual invitation must
be passed to the contract using `privateArg`. This combination makes it
be passed to the contract using `privateArgs`. This combination makes it
possible for clients to see what the invitation is for, but only the contract
has the ability to exercise it. Similarly, when there will be a vote to change
the Electorate (or any other Invitation-valued parameter), observers can see the
Expand All @@ -166,36 +166,46 @@ exercised if/when the vote is successful.
### ParamManager

`ContractGovernor` expects to work with contracts that use `ParamManager` to
manage their parameters. `makeParamManager()` is designed to be called
within the managed contract so that internal access to the parameter values is
manage their parameters. In order to support upgrade, all governed contracts
will be durable, upgradeable contracts. When using the `contractHelper`, the way
to create a paramManager is to call `handleParamGovernance` from within the
governed contract so that internal access to the parameter values is
synchronous. A separate facet allows visible management of changes to the
parameter values.

`makeParamManager(zoe)` makes a ParamManager:
`handleParamGovernance(zcf, invitation, paramType, ...)` makes a ParamManager:

```javascript
const paramManager = await makeParamManager(
const facetHelpers = await handleParamGovernance(
zcf,
invitation,
{
'MyChangeableNumber': ['nat', startingValue],
'ContractElectorate': ['invitation', initialPoserInvitation],
},
zcf.getZoeService(),
makeRecorderKit,
storageNode,
);

paramManager.getMyChangeableNumber() === startingValue;
paramManager.updatetMyChangeableNumber((newValue);
paramManager.getMyChangeableNumber() === newValue;
const { publicMixin, publicMixinGuards } = facetHelpers;
const { augmentPublicFacet, makeGovernorFacet, params } = facetHelpers;
```

If you don't need any parameters that depend on the Zoe service, there's
an alternative function that returns synchronously:
```javascript
const paramManager = await makeParamManagerSync(
{
'Collateral': ['brand', drachmaBrand],
},
);
```
`augmentPublicFacet` is a function that can be applied to a `publicFacet` to
produce a publicFacet that also includes accessors for all the defined
parameters as well as `getParamDescriptions` and `getPublicTopics`.

Similarly, `makeGovernorFacet` can be applied to the `creatorFacet` to create
the facet that the contractGovernor will use as well as the
`limitedCreatorFacet` that can be handed out to those outside of governance who
should have access to the creator functionality of the governed contract.

`makeRecorderKit` and `storageNode` are provided to paramGovernance so it can
publish the original values and any updates to governed values to the off-chain
storage. `makeRecorderKit` is a function that creates a durable recorderKit.
Since durable constructors must be defined exactly once per vat, and
recorderKits will be needed elsewhere in the contract, it has to be passed in.
`storageNode` is the node where governance will be able to write updates.

See [ParamTypes definition](./src/constants.js) for all supported types. More
types will be supported as we learn what contracts need to manage. (If you find
Expand All @@ -212,7 +222,7 @@ the methods to be called.
### Governed Contracts

`contractHelper` provides support for the vast majority of expected clients that
will have a single set of parameters to manage. A contract only has to define
will have a single set of parameters to manage. A contract only has to declare
the parameters (including `CONTRACT_ELECTORATE`) in a call to
`handleParamGovernance()`, and add any needed methods to the public and creator
facets. This will
Expand All @@ -222,16 +232,10 @@ facets. This will
It's convenient for the contract to export a function (e.g. `makeParamTerms`)
for the use of those starting up the contract to insert in the `terms`. They
would otherwise need to write boilerplate functions to declare all the required
parameters.

When a governed contract starts up, it should get the parameter declarations
from `terms`, use them to create a paramManager, and pass that to
`handleParamGovernance`. `handleParamGovernance()` returns functions
(`augmentPublicFacet()` and `makeGovernorFacet()`) that add
required methods to the public and creator facets. Since the governed contract
uses the values passed in `terms` to create the paramManager, reviewers of the
contract can verify that all and only the declared parameters are under the
control of the paramManager and made visible to the contract's clients.
parameters. Since the governed contract uses the values passed in `terms` to
create the paramManager, reviewers of the contract can verify that all and only
the declared parameters are under the control of the paramManager and made
visible to the contract's clients.

Governed methods and parameters must be included in terms.

Expand All @@ -249,10 +253,9 @@ Governed methods and parameters must be included in terms.
```

When a contract is written without benefit of `contractHelper`, it is
responsible for adding `getSubscription`, and
`getGovernedParams` to its `PublicFacet`, and for adding
`getParamMgrRetriever`, `getInvitation` and `getLimitedCreatorFacet` to its
`CreatorFacet`.
responsible for adding `getParamDescriptions`, and `getPublicTopics` to its
`PublicFacet`, and for adding `getParamMgrRetriever`, `getInvitation` and
`getLimitedCreatorFacet` to its `CreatorFacet`.

## Scenarios

Expand Down
Loading