Skip to content

Commit

Permalink
refactor: vaultFactory upgrade
Browse files Browse the repository at this point in the history
migrate from taking Auction's publicFacet in terms to taking the instance in privateArgs
Allow Parameter values to be overridden on restart

repair some erroneous guards

Deal with reconstituted invitations

Governance:
  Allow the electorate invitation to change on upgrade
  simplify and speed up invitation validation
  • Loading branch information
Chris-Hibbert committed Apr 24, 2024
1 parent 677415b commit 4b43fed
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 30 deletions.
6 changes: 3 additions & 3 deletions packages/governance/src/contractGovernance/paramManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,10 +224,10 @@ const makeParamManagerBuilder = (publisherKit, zoe) => {
if (!zoe) {
throw Fail`zoe must be provided for governed Invitations ${zoe}`;
}
const { instance, installation } = await E(zoe).getInvitationDetails(i);

// @ts-expect-error typedefs say they're guaranteed truthy but just to be safe
assert(instance && installation, 'must be an invitation');
// local check on isLive() gives better report than .getInvitationDetails()
const isLive = await E(E(zoe).getInvitationIssuer()).isLive(i);
isLive || Fail`Invitation passed to paramManager is not live ${i}`;
};

/**
Expand Down
25 changes: 10 additions & 15 deletions packages/governance/src/contractHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@ import { Far } from '@endo/marshal';
import { makeStoredPublisherKit } from '@agoric/notifier';
import { getMethodNames, objectMap } from '@agoric/internal';
import { ignoreContext, prepareExo } from '@agoric/vat-data';
import { keyEQ, M } from '@agoric/store';
import { M } from '@agoric/store';
import { AmountShape, BrandShape } from '@agoric/ertp';
import { RelativeTimeRecordShape, TimestampRecordShape } from '@agoric/time';
import { E } from '@endo/eventual-send';
import { assertElectorateMatches } from './contractGovernance/paramManager.js';
import { makeParamManagerFromTerms } from './contractGovernance/typedParamManager.js';
import { GovernorFacetShape } from './typeGuards.js';
import { CONTRACT_ELECTORATE } from './contractGovernance/governParam.js';

/**
* @import {VoteCounterCreatorFacet, VoteCounterPublicFacet, QuestionSpec, OutcomeRecord, AddQuestion, AddQuestionReturn, GovernanceSubscriptionState, GovernanceTerms, GovernedApis, GovernedCreatorFacet, GovernedPublicFacet} from './types.js';
*/

const { Fail, quote: q } = assert;
const { Fail } = assert;

export const GOVERNANCE_STORAGE_KEY = 'governance';

Expand All @@ -35,22 +35,17 @@ const publicMixinAPI = harden({
});

/**
* Verify that the electorate is represented by a live invitation.
*
* @param {ZCF<GovernanceTerms<{}> & {}>} zcf
* @param {import('./contractGovernance/typedParamManager.js').TypedParamManager<any>} paramManager
*/
export const validateElectorate = (zcf, paramManager) => {
const { governedParams } = zcf.getTerms();
return E.when(paramManager.getParams(), finishedParams => {
try {
keyEQ(governedParams, finishedParams) ||
Fail`The 'governedParams' term must be an object like ${q(
finishedParams,
)}, but was ${q(governedParams)}`;
assertElectorateMatches(paramManager, governedParams);
} catch (err) {
zcf.shutdownWithFailure(err);
}
});
const invitation = paramManager.getInternalParamValue(CONTRACT_ELECTORATE);
return E.when(
E(zcf.getInvitationIssuer()).isLive(invitation),
isLive => isLive || Fail`Electorate invitation is not live.`,
);
};
harden(validateElectorate);

Expand Down
45 changes: 39 additions & 6 deletions packages/inter-protocol/src/vaultFactory/params.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// @jessie-check

/// <reference path="./types.js" />

import {
Expand All @@ -12,6 +10,7 @@ import { M, makeScalarMapStore } from '@agoric/store';
import { TimeMath } from '@agoric/time';
import { provideDurableMapStore } from '@agoric/vat-data';
import { subtractRatios } from '@agoric/zoe/src/contractSupport/ratio.js';
import { makeTracer } from '@agoric/internal/src/index.js';
import { amountPattern, ratioPattern } from '../contractSupport.js';

export const CHARGING_PERIOD_KEY = 'ChargingPeriod';
Expand All @@ -35,6 +34,8 @@ export const vaultDirectorParamTypes = {
};
harden(vaultDirectorParamTypes);

const trace = makeTracer('Vault Params');

/**
* @param {Amount<'set'>} electorateInvitationAmount
* @param {Amount<'nat'>} minInitialDebt
Expand Down Expand Up @@ -163,6 +164,19 @@ export const makeGovernedTerms = ({
});
};
harden(makeGovernedTerms);

// XXX Better to declare this as VaultManagerParamValues + brand. How?
/**
* @typedef {object} VaultManagerParams
* @property {Brand} brand
* @property {Ratio} liquidationMargin
* @property {Ratio} liquidationPenalty
* @property {Ratio} interestRate
* @property {Ratio} mintFee
* @property {Amount<'nat'>} debtLimit
* @property {Ratio} [liquidationPadding]
*/

/**
* Stop-gap which restores initial param values UNTIL
* https://github.com/Agoric/agoric-sdk/issues/5200
Expand All @@ -171,8 +185,14 @@ harden(makeGovernedTerms);
*
* @param {import('@agoric/vat-data').Baggage} baggage
* @param {ERef<Marshaller>} marshaller
* @param {Record<string, VaultManagerParams>} managerParamValues - sets of
* parameters (plus brand:) keyed by Keyword. override stored initial values
*/
export const provideVaultParamManagers = (baggage, marshaller) => {
export const provideVaultParamManagers = (
baggage,
marshaller,
managerParamValues,
) => {
/** @type {MapStore<Brand, VaultParamManager>} */
const managers = makeScalarMapStore();

Expand All @@ -197,10 +217,23 @@ export const provideVaultParamManagers = (baggage, marshaller) => {
return manager;
};

// restore from baggage
// [...managerArgs.entries()].map(([brand, args]) => makeManager(brand, args));
// restore from baggage, unless `managerParamValues` overrides.
for (const [brand, args] of managerArgs.entries()) {
makeManager(brand, args);
let values;
for (const key of Object.keys(managerParamValues)) {
if (managerParamValues[key]?.brand === brand) {
values = managerParamValues[key];
break;
}
}

if (values) {
trace(`reviving params, override`, brand, values);
makeManager(brand, { ...args, initialParamValues: values });
} else {
trace(`reviving params, keeping`, brand, args.initialParamValues);
makeManager(brand, args);
}
}

return {
Expand Down
9 changes: 6 additions & 3 deletions packages/inter-protocol/src/vaultFactory/vaultDirector.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ const prepareVaultDirector = (
marshaller,
makeRecorderKit,
makeERecorderKit,
managerParams,
) => {
/** @type {import('../reserve/assetReserve.js').ShortfallReporter} */
let shortfallReporter;
Expand All @@ -120,7 +121,11 @@ const prepareVaultDirector = (
// Non-durable map because param managers aren't durable.
// In the event they're needed they can be reconstructed from contract terms and off-chain data.
/** a powerful object; can modify parameters */
const vaultParamManagers = provideVaultParamManagers(baggage, marshaller);
const vaultParamManagers = provideVaultParamManagers(
baggage,
marshaller,
managerParams,
);

const metricsNode = E(storageNode).makeChildNode('metrics');

Expand All @@ -146,12 +151,10 @@ const prepareVaultDirector = (
const oldInvitation = baggage.has(shortfallInvitationKey)
? baggage.get(shortfallInvitationKey)
: undefined;
console.log('@@@@@ Old Invitation', oldInvitation);

const newInvitation = await directorParamManager.getInternalParamValue(
SHORTFALL_INVITATION_KEY,
);
console.log('@@@@@ New Invitation', newInvitation);

if (newInvitation === oldInvitation) {
shortfallReporter ||
Expand Down
5 changes: 4 additions & 1 deletion packages/inter-protocol/src/vaultFactory/vaultFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ harden(meta);
* storageNode: ERef<StorageNode>;
* marshaller: ERef<Marshaller>;
* auctioneerInstance: Instance;
* managerParams: Record<string, import('./params.js').VaultManagerParams>;
* }} privateArgs
* @param {import('@agoric/swingset-liveslots').Baggage} baggage
*/
Expand All @@ -81,6 +82,7 @@ export const start = async (zcf, privateArgs, baggage) => {
marshaller,
storageNode,
auctioneerInstance,
managerParams,
} = privateArgs;

trace('awaiting debtMint');
Expand All @@ -95,7 +97,7 @@ export const start = async (zcf, privateArgs, baggage) => {
const { timerService } = zcf.getTerms();

const zoe = zcf.getZoeService();
const auctioneerPublicFacet = await E(zoe).getPublicFacet(auctioneerInstance);
const auctioneerPublicFacet = E(zoe).getPublicFacet(auctioneerInstance);

const { makeRecorderKit, makeERecorderKit } = prepareRecorderKitMakers(
baggage,
Expand Down Expand Up @@ -138,6 +140,7 @@ export const start = async (zcf, privateArgs, baggage) => {
marshaller,
makeRecorderKit,
makeERecorderKit,
managerParams,
);

// cannot await because it would make remote calls during vat restart
Expand Down
3 changes: 1 addition & 2 deletions packages/inter-protocol/src/vaultFactory/vaultManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ import {
} from '@agoric/zoe/src/contractSupport/index.js';
import { PriceQuoteShape, SeatShape } from '@agoric/zoe/src/typeGuards.js';
import { E } from '@endo/eventual-send';
import { AuctionPFShape } from '../auction/auctioneer.js';
import {
checkDebtLimit,
makeNatAmountShape,
Expand Down Expand Up @@ -335,7 +334,7 @@ export const prepareVaultManagerKit = (
getCollateralQuote: M.call().returns(PriceQuoteShape),
getPublicFacet: M.call().returns(M.remotable('publicFacet')),
lockOraclePrices: M.call().returns(PriceQuoteShape),
liquidateVaults: M.call(AuctionPFShape).returns(M.promise()),
liquidateVaults: M.call(M.promise()).returns(M.promise()),
}),
},
initState,
Expand Down

0 comments on commit 4b43fed

Please sign in to comment.