Skip to content

Commit

Permalink
feat(cosmic-swingset): introduce GovernorExecutor #10725
Browse files Browse the repository at this point in the history
refs: #10725

Initial set of changes to get auctioneer governors terminated.
  • Loading branch information
siarhei-agoric committed Jan 8, 2025
1 parent f223be1 commit 5c0ac62
Show file tree
Hide file tree
Showing 3 changed files with 252 additions and 0 deletions.
20 changes: 20 additions & 0 deletions golang/cosmos/app/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,26 @@ func terminateGovernorCoreProposal(upgradeName string) (vm.CoreProposalStep, err
)
}

func upgradeGovernorExecutorCoreProposal(upgradeName string) (vm.CoreProposalStep, error) {
// targets is a slice of "$boardID:$instanceKitLabel" strings.
var targets []string
switch getVariantFromUpgradeName(upgradeName) {
case "EMERYNET":
targets = []string{"board04149:auctioneer"} // v38: governor for v39
//"boardXXXYYY:autioneer",
// fixme: need some targets here
default:
return nil, nil
}

return buildProposalStepWithArgs(
"@agoric/builders/scripts/vats/upgrade-governor-instance.js",
// Request `defaultProposalBuilder(powers, targets)`.
"defaultProposalBuilder",
[]any{targets},
)
}

// func upgradeMintHolderCoreProposal(upgradeName string) (vm.CoreProposalStep, error) {
// variant := getVariantFromUpgradeName(upgradeName)

Expand Down
206 changes: 206 additions & 0 deletions packages/builders/scripts/vats/upgrade-governor-instance.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
/**
* @file Upgrade price-feed governor instances such as emerynet v664.
* Functions as both an off-chain builder and an on-chain core-eval.
*/

/// <reference types="@agoric/vats/src/core/types-ambient"/>

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

const SELF = '@agoric/builders/scripts/vats/terminate-governor-instance.js';
const USAGE = `Usage: agoric run /path/to/terminate-governor-instance.js \\
<$governorInstanceHandleBoardID:$instanceKitLabel>...`;

const repr = val =>
typeof val === 'string' || (typeof val === 'object' && val !== null)
? JSON.stringify(val)
: String(val);
const defaultMakeError = (strings, ...subs) =>
Error(
strings.map((s, i) => `${i === 0 ? '' : repr(subs[i - 1])}${s}`).join(''),
);
const makeUsageError = (strings, ...subs) => {
const err = defaultMakeError(strings, ...subs);
console.error(err.message);
console.error(USAGE);
return err;
};

const rtarget = /^(?<boardID>board[0-9]+):(?<instanceKitLabel>.+)$/;
/**
* @param {string[]} args
* @param {(strings: TemplateStringsArray | string[], ...subs: unknown[]) => Error} [makeError]
* @returns {Array<{boardID: string, instanceKitLabel: string}>}
*/
const parseTargets = (args = [], makeError = defaultMakeError) => {
if (!Array.isArray(args)) throw makeError`invalid targets: ${args}`;
/** @type {Array<{boardID: string, instanceKitLabel: string}>} */
const targets = [];
const badTargets = [];
for (const arg of args) {
const m = typeof arg === 'string' && arg.match(rtarget);
if (!m) {
badTargets.push(arg);
} else {
// @ts-expect-error cast
targets.push(m.groups);
}
}
if (badTargets.length !== 0) {
throw makeError`malformed target(s): ${badTargets}`;
} else if (targets.length === 0) {
throw makeError`no target(s)`;
}
return targets;
};

/**
* @param {BootstrapPowers} powers
* @param {{ options: { governorExecutorBundle: any, targetSpecifiers: string[] } }} config
*/
export const upgradeGovernors = async (
{ consume: { board, governedContractKits } },
{ options: { governorExecutorBundle, targetSpecifiers } },
) => {
const { Fail, quote: q } = assert;
const targets = parseTargets(targetSpecifiers, Fail);
const doneP = Promise.allSettled(
targets.map(async ({ boardID, instanceKitLabel }) => {
const logLabel = [boardID, instanceKitLabel];
const contractInstanceHandle = await E(board).getValue(boardID);
const instanceKit = await E(governedContractKits).get(
// @ts-expect-error TS2345 Property '[tag]' is missing
contractInstanceHandle,
);
console.log(
`${q(logLabel)} alleged governor contract instance kit`,
instanceKit,
);
const { label, governorAdminFacet, adminFacet } = instanceKit;
label === instanceKitLabel ||
Fail`${q(logLabel)} unexpected instanceKit label, got ${label} but wanted ${q(instanceKitLabel)}`;
(adminFacet && adminFacet !== governorAdminFacet) ||
Fail`${q(logLabel)} instanceKit adminFacet should have been present and different from governorAdminFacet but was ${adminFacet}`;
const reason = harden(Error(`core-eval terminating ${label} governor`));
await E(governorAdminFacet).upgradeContract(governorExecutorBundle, undefined);
console.log(`${q(logLabel)} terminated governor`);
}),
);
const results = await doneP;
const problems = targets.flatMap(({ boardID, instanceKitLabel }, i) => {
if (results[i].status === 'fulfilled') return [];
return [[boardID, instanceKitLabel, results[i].reason]];
});
if (problems.length !== 0) {
console.error('governor termination(s) failed', problems);
Fail`governor termination(s) failed: ${problems}`;
}
};
harden(upgradeGovernors);

/*
export const getManifest = (_powers, targetSpecifiers) => {
parseTargets(targetSpecifiers);
return {
manifest: {
[upgradeGovernors.name]: {
consume: { board: true, governedContractKits: true },
},
},
// Provide `terminateGovernors` a second argument like
// `{ options: { targetSpecifiers } }`.
options: { targetSpecifiers },
};
};
*/

const uG = 'upgradeGovernors';
/**
* Return the manifest, installations, and options for upgrading Vaults.
*
* @param {object} utils
* @param {any} utils.restoreRef
* @param {any} vaultUpgradeOptions
*/
export const getManifest = async (
{ restoreRef },
{ bundleRef, targetSpecifiers },
) => {
return {
manifest: {
[upgradeGovernors.name]: {
consume: {
board: uG,
zoe: uG,
governedContractKits: uG,
},
},
},
installations: { governorExecutor: restoreRef(bundleRef) }, // do we need installations ???
options: { governorExecutorBundle: bundleRef, targetSpecifiers },
};
};

/* @type {import('@agoric/deploy-script-support/src/externalTypes.js').CoreEvalBuilder} */
/*
export const defaultProposalBuilder = async (_utils, targetSpecifiers) => {
parseTargets(targetSpecifiers);
return harden({
sourceSpec: SELF,
getManifestCall: ['getManifest', targetSpecifiers],
});
};*/

/* @type {import('@agoric/deploy-script-support/src/externalTypes.js').DeployScriptFunction} */
/*
export default async (homeP, endowments) => {
const { scriptArgs } = endowments;
parseTargets(scriptArgs, makeUsageError);
const dspModule = await import('@agoric/deploy-script-support');
const { makeHelpers } = dspModule;
const { writeCoreEval } = await makeHelpers(homeP, endowments);
await writeCoreEval(upgradeGovernors.name, utils =>
defaultProposalBuilder(utils, scriptArgs),
);
};
*/
// import { makeHelpers } from '@agoric/deploy-script-support';

/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').CoreEvalBuilder} */
export const defaultProposalBuilder = async ({ publishRef, install }, targetSpecifiers) => {
parseTargets(targetSpecifiers);
return harden({
sourceSpec: SELF,
getManifestCall: [
getManifest.name,
{
bundleRef: publishRef(
install(
'@agoric/governance/bundles/contractGovernorExecutor.js',
),
),
targetSpecifiers,
},
],
});
}

/* @type {import('@agoric/deploy-script-support/src/externalTypes.js').DeployScriptFunction} */
/*
export default async (homeP, endowments) => {
const { writeCoreEval } = await makeHelpers(homeP, endowments);
await writeCoreEval('upgrade-governor-instance', defaultProposalBuilder);
};
*/

/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').DeployScriptFunction} */
export default async (homeP, endowments) => {
const { scriptArgs } = endowments;
parseTargets(scriptArgs, makeUsageError);
const dspModule = await import('@agoric/deploy-script-support');
const { makeHelpers } = dspModule;
const { writeCoreEval } = await makeHelpers(homeP, endowments);
await writeCoreEval(upgradeGovernors.name, utils =>
defaultProposalBuilder(utils, scriptArgs),
);
};
26 changes: 26 additions & 0 deletions packages/governance/src/contractGovernorExecutor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { E } from '@endo/eventual-send';
import { makeTracer } from '@agoric/internal';

const trace = makeTracer('CGExec', false);

/** @type {ContractMeta} */
export const meta = {
upgradability: 'canUpgrade',
};
harden(meta);

/**
* Start an instance of a governor, governing a "governed" contract specified in terms.
*
* @param {ZCF<{}>} zcf
* @param {{}} _privateArgs
* @param {import('@agoric/vat-data').Baggage} baggage
*/
export const start = async (zcf, _privateArgs, baggage) => {
trace('start');
const contractInstanceAdminFacet = baggage.get('creatorFacet');
const terminationData = harden(Error(`termination of contract by executor governor`));
await E(contractInstanceAdminFacet).terminateContract(terminationData);
zcf.shutdown(`self-termination of executor governor`);
};
harden(start);

0 comments on commit 5c0ac62

Please sign in to comment.