Skip to content

Commit

Permalink
Merge pull request #8385 from Agoric/8384-gov-cmd
Browse files Browse the repository at this point in the history
generalize `ec` cmd to `gov` any committee
  • Loading branch information
mergify[bot] authored Sep 29, 2023
2 parents af6e3fd + cc61e93 commit ae21a7e
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 38 deletions.
2 changes: 2 additions & 0 deletions packages/agoric-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"@agoric/casting": "^0.4.2",
"@agoric/cosmic-proto": "^0.3.0",
"@agoric/ertp": "^0.16.2",
"@agoric/governance": "^0.10.3",
"@agoric/inter-protocol": "^0.16.1",
"@agoric/internal": "^0.3.2",
"@agoric/network": "^0.1.0",
Expand All @@ -63,6 +64,7 @@
"@endo/init": "^0.5.59",
"@endo/marshal": "^0.8.8",
"@endo/nat": "^4.1.30",
"@endo/patterns": "^0.2.6",
"@endo/promise-kit": "^0.2.59",
"@iarna/toml": "^2.2.3",
"anylogger": "^0.21.0",
Expand Down
4 changes: 2 additions & 2 deletions packages/agoric-cli/src/bin-agops.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import process from 'process';
import anylogger from 'anylogger';
import { Command, CommanderError, createCommand } from 'commander';
import { makeOracleCommand } from './commands/oracle.js';
import { makeEconomicCommiteeCommand } from './commands/ec.js';
import { makeGovCommand } from './commands/gov.js';
import { makePsmCommand } from './commands/psm.js';
import { makeReserveCommand } from './commands/reserve.js';
import { makeVaultsCommand } from './commands/vaults.js';
Expand All @@ -34,7 +34,7 @@ const program = new Command();
program.name(progname).version('unversioned');

program.addCommand(makeOracleCommand(logger));
program.addCommand(makeEconomicCommiteeCommand(logger));
program.addCommand(makeGovCommand(logger));
program.addCommand(makePerfCommand(logger));
program.addCommand(makePsmCommand(logger));
program.addCommand(makeVaultsCommand(logger));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ import {

/** @typedef {import('@agoric/smart-wallet/src/offers.js').OfferSpec} OfferSpec */

const collectValues = (val, memo) => {
memo.push(val);
return memo;
};

const defaultKeyring = process.env.AGORIC_KEYRING_BACKEND || 'test';

/**
* @param {import('anylogger').Logger} _logger
* @param {{
Expand All @@ -26,7 +33,7 @@ import {
* delay?: (ms: number) => Promise<void>,
* }} [io]
*/
export const makeEconomicCommiteeCommand = (_logger, io = {}) => {
export const makeGovCommand = (_logger, io = {}) => {
const {
// Allow caller to provide access explicitly, but
// default to conventional ambient IO facilities.
Expand All @@ -38,11 +45,22 @@ export const makeEconomicCommiteeCommand = (_logger, io = {}) => {
delay = ms => new Promise(resolve => setTimeout(resolve, ms)),
} = io;

const ec = new Command('ec').description('Economic Committee commands');
const cmd = new Command('gov').description('Electoral governance commands');
// backwards compatibility with less general "ec" command. To make this work
// the new CLI options default to the values used for Economic Committee
cmd.alias('ec');
cmd.option(
'--keyring-backend <os|file|test>',
`keyring's backend (os|file|test) (default "${defaultKeyring}")`,
defaultKeyring,
);

/** @param {string} literalOrName */
const normalizeAddress = literalOrName =>
normalizeAddressWithOptions(literalOrName, { keyringBackend: 'test' });
normalizeAddressWithOptions(literalOrName, {
// FIXME does not observe keyring-backend option, which isn't available during arg parsing
keyringBackend: defaultKeyring,
});

/** @type {(info: unknown, indent?: unknown) => boolean } */
const show = (info, indent) =>
Expand All @@ -63,15 +81,21 @@ export const makeEconomicCommiteeCommand = (_logger, io = {}) => {
* @param {{
* toOffer: (agoricNames: *, current: import('@agoric/smart-wallet/src/smartWallet').CurrentWalletRecord | undefined) => OfferSpec,
* sendFrom?: string | undefined,
* keyringBackend: string,
* instanceName?: string,
* }} detail
* @param {Awaited<ReturnType<makeRpcUtils>>} [optUtils]
*/
const processOffer = async function ({ toOffer, sendFrom }, optUtils) {
const processOffer = async function (
{ toOffer, sendFrom, keyringBackend },
optUtils,
) {
const networkConfig = await getNetworkConfig(env);
const utils = await (optUtils || makeRpcUtils({ fetch }));
const { agoricNames, readLatestHead } = utils;

assert(keyringBackend, 'missing keyring-backend option');

let current;
if (sendFrom) {
current = await getCurrent(sendFrom, { readLatestHead });
Expand All @@ -89,7 +113,7 @@ export const makeEconomicCommiteeCommand = (_logger, io = {}) => {
const result = await sendAction(
{ method: 'executeOffer', offer },
{
keyring: { backend: 'test' }, // XXX
keyring: { backend: keyringBackend },
from: sendFrom,
verbose: false,
...networkConfig,
Expand Down Expand Up @@ -126,29 +150,38 @@ export const makeEconomicCommiteeCommand = (_logger, io = {}) => {
show(blockInfo);
};

ec.command('committee')
.description('accept invitation to join the economic committee')
cmd
.command('committee')
.description('accept invitation to join a committee')
.requiredOption(
'--name <string>',
'Committee instance name',
String,
'economicCommittee',
)
.option('--voter <number>', 'Voter number', Number, 0)
.option(
'--offerId <string>',
'Offer id',
String,
`ecCommittee-${Date.now()}`,
`gov-committee-${Date.now()}`,
)
.option(
'--send-from <name-or-address>',
'Send from address',
normalizeAddress,
)
.action(async function (opts) {
.action(async function (opts, options) {
const { name: instanceName } = opts;

/** @type {Parameters<typeof processOffer>[0]['toOffer']} */
const toOffer = (agoricNames, current) => {
const instance = agoricNames.instance.economicCommittee;
assert(instance, `missing economicCommittee`);
const instance = agoricNames.instance[instanceName];
assert(instance, `missing ${instanceName}`);

if (current) {
const found = findContinuingIds(current, agoricNames);
abortIfSeen('economicCommittee', found);
abortIfSeen(instanceName, found);
}

return {
Expand All @@ -164,28 +197,37 @@ export const makeEconomicCommiteeCommand = (_logger, io = {}) => {

await processOffer({
toOffer,
instanceName: 'economicCommittee',
...opts,
instanceName,
sendFrom: opts.sendFrom,
keyringBackend: options.optsWithGlobals().keyringBackend,
});
});

ec.command('charter')
cmd
.command('charter')
.description('accept the charter invitation')
.option('--offerId <string>', 'Offer id', String, `ecCharter-${Date.now()}`)
.requiredOption(
'--name <string>',
'Charter instance name',
'economicCommitteeCharter',
)
.option('--offerId <string>', 'Offer id', String, `charter-${Date.now()}`)
.option(
'--send-from <name-or-address>',
'Send from address',
normalizeAddress,
)
.action(async function (opts) {
.action(async function (opts, options) {
const { name: instanceName } = opts;

/** @type {Parameters<typeof processOffer>[0]['toOffer']} */
const toOffer = (agoricNames, current) => {
const instance = agoricNames.instance.econCommitteeCharter;
assert(instance, `missing econCommitteeCharter`);
const instance = agoricNames.instance[instanceName];
assert(instance, `missing ${instanceName}`);

if (current) {
const found = findContinuingIds(current, agoricNames);
abortIfSeen('econCommitteeCharter', found);
abortIfSeen(instanceName, found);
}

return {
Expand All @@ -201,12 +243,14 @@ export const makeEconomicCommiteeCommand = (_logger, io = {}) => {

await processOffer({
toOffer,
instanceName: 'econCommitteeCharter',
...opts,
instanceName,
sendFrom: opts.sendFrom,
keyringBackend: options.optsWithGlobals().keyringBackend,
});
});

ec.command('find-continuing-id')
cmd
.command('find-continuing-id')
.description('print id of specified voting continuing invitation')
.requiredOption(
'--from <name-or-address>',
Expand All @@ -232,7 +276,8 @@ export const makeEconomicCommiteeCommand = (_logger, io = {}) => {
console.log(match.offerId);
});

ec.command('find-continuing-ids')
cmd
.command('find-continuing-ids')
.description('print records of voting continuing invitations')
.requiredOption(
'--from <name-or-address>',
Expand All @@ -244,12 +289,27 @@ export const makeEconomicCommiteeCommand = (_logger, io = {}) => {
const current = await getCurrent(opts.from, { readLatestHead });

const found = findContinuingIds(current, agoricNames);
found.forEach(it => show({ ...it, address: opts.from }));
for (const it of found) {
show({ ...it, address: opts.from });
}
});

ec.command('vote')
.description('vote on a question (hard-coded for now))')
.option('--offerId <number>', 'Offer id', String, `ecVote-${Date.now()}`)
cmd
.command('vote')
.description('vote on latest question')
.requiredOption(
'--instance <string>',
'Committee name under agoricNames.instances',
String,
'economicCommittee',
)
.requiredOption(
'--pathname <string>',
'Committee name under published.committees',
String,
'Economic_Committee',
)
.option('--offerId <number>', 'Offer id', String, `gov-vote-${Date.now()}`)
.requiredOption(
'--forPosition <number>',
'index of one position to vote for (within the question description.positions); ',
Expand All @@ -260,17 +320,18 @@ export const makeEconomicCommiteeCommand = (_logger, io = {}) => {
'Send from address',
normalizeAddress,
)
.action(async function (opts) {
.action(async function (opts, options) {
const utils = await makeRpcUtils({ fetch });
const { readLatestHead } = utils;

const info = await readLatestHead(
'published.committees.Economic_Committee.latestQuestion',
`published.committees.${opts.pathname}.latestQuestion`,
).catch(err => {
throw new CommanderError(1, 'VSTORAGE_FAILURE', err.message);
});

// XXX runtime shape-check
const questionDesc = /** @type {any} */ (info);
const questionDesc = /** @type {QuestionDetails} */ (info);

// TODO support multiple position arguments
const chosenPositions = [questionDesc.positions[opts.forPosition]];
Expand All @@ -279,9 +340,7 @@ export const makeEconomicCommiteeCommand = (_logger, io = {}) => {
/** @type {Parameters<typeof processOffer>[0]['toOffer']} */
const toOffer = (agoricNames, current) => {
const cont = current ? findContinuingIds(current, agoricNames) : [];
const votingRight = cont.find(
it => it.instance === agoricNames.instance.economicCommittee,
);
const votingRight = cont.find(it => it.instanceName === opts.instance);
if (!votingRight) {
console.debug('continuing ids', cont, 'for', current);
throw new CommanderError(
Expand All @@ -306,8 +365,89 @@ export const makeEconomicCommiteeCommand = (_logger, io = {}) => {
};
};

await processOffer({ toOffer, sendFrom: opts.sendFrom }, utils);
await processOffer(
{
toOffer,
sendFrom: opts.sendFrom,
keyringBackend: options.optsWithGlobals().keyringBackend,
},
utils,
);
});

cmd
.command('proposePauseOffers')
.description('propose a vote to pause offers')
.option(
'--send-from <name-or-address>',
'Send from address',
normalizeAddress,
)
.option(
'--offerId <string>',
'Offer id',
String,
`proposePauseOffers-${Date.now()}`,
)
.requiredOption(
'--instance <string>',
'name of governed instance in agoricNames',
)
.requiredOption(
'--substring <string>',
'an offer string to pause (can be repeated)',
collectValues,
[],
)
.option(
'--deadline <minutes>',
'minutes from now to close the vote',
Number,
1,
)
.action(async function (opts, options) {
const { instance: instanceName } = opts;

/** @type {Parameters<typeof processOffer>[0]['toOffer']} */
const toOffer = (agoricNames, current) => {
const instance = agoricNames.instance[instanceName];
assert(instance, `missing ${instanceName}`);
assert(current, 'missing current wallet');

const known = findContinuingIds(current, agoricNames);

assert(known, 'could not find committee acceptance offer id');

// TODO magic string
const match = known.find(
r => r.description === 'charter member invitation',
);
assert(match, 'no offer found for charter member invitation');

return {
id: opts.offerId,
invitationSpec: {
source: 'continuing',
previousOffer: match.offerId,
invitationMakerName: 'VoteOnPauseOffers',
// ( instance, strings list, timer deadline seconds )
invitationArgs: harden([
instance,
opts.substring,
BigInt(opts.deadline * 60 + Math.round(Date.now() / 1000)),
]),
},
proposal: {},
};
};

await processOffer({
toOffer,
instanceName,
sendFrom: opts.sendFrom,
keyringBackend: options.optsWithGlobals().keyringBackend,
});
});

return ec;
return cmd;
};

0 comments on commit ae21a7e

Please sign in to comment.