agoric 0.21.2-other-dev-8f8782b.0 → 0.21.2-other-dev-3eb1a1d.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,11 @@
1
- /* eslint-disable @jessie.js/no-nested-await */
2
1
  // @ts-check
3
2
  /* eslint-disable func-names */
4
- /* global globalThis, process, setTimeout */
3
+ /* eslint-env node */
4
+ import { makeVstorageKit } from '@agoric/client-utils';
5
5
  import { execFileSync as execFileSyncAmbient } from 'child_process';
6
6
  import { Command, CommanderError } from 'commander';
7
7
  import { normalizeAddressWithOptions, pollBlocks } from '../lib/chain.js';
8
- import { getNetworkConfig, makeRpcUtils } from '../lib/rpc.js';
8
+ import { getNetworkConfig } from '../lib/network-config.js';
9
9
  import {
10
10
  findContinuingIds,
11
11
  getCurrent,
@@ -14,7 +14,19 @@ import {
14
14
  sendAction,
15
15
  } from '../lib/wallet.js';
16
16
 
17
- /** @typedef {import('@agoric/smart-wallet/src/offers.js').OfferSpec} OfferSpec */
17
+ /**
18
+ * @import {OfferSpec} from '@agoric/smart-wallet/src/offers.js'
19
+ * @import {QuestionDetails} from '@agoric/governance/src/types.js'
20
+ */
21
+
22
+ const collectValues = (val, memo) => {
23
+ memo.push(val);
24
+ return memo;
25
+ };
26
+
27
+ const defaultKeyring = process.env.AGORIC_KEYRING_BACKEND || 'test';
28
+
29
+ const networkConfig = await getNetworkConfig({ env: process.env, fetch });
18
30
 
19
31
  /**
20
32
  * @param {import('anylogger').Logger} _logger
@@ -27,23 +39,33 @@ import {
27
39
  * delay?: (ms: number) => Promise<void>,
28
40
  * }} [io]
29
41
  */
30
- export const makeEconomicCommiteeCommand = (_logger, io = {}) => {
42
+ export const makeGovCommand = (_logger, io = {}) => {
31
43
  const {
32
44
  // Allow caller to provide access explicitly, but
33
45
  // default to conventional ambient IO facilities.
34
- env = process.env,
35
46
  stdout = process.stdout,
36
47
  stderr = process.stderr,
37
- fetch = globalThis.fetch,
48
+ fetch = global.fetch,
38
49
  execFileSync = execFileSyncAmbient,
39
50
  delay = ms => new Promise(resolve => setTimeout(resolve, ms)),
40
51
  } = io;
41
52
 
42
- const ec = new Command('ec').description('Economic Committee commands');
53
+ const cmd = new Command('gov').description('Electoral governance commands');
54
+ // backwards compatibility with less general "ec" command. To make this work
55
+ // the new CLI options default to the values used for Economic Committee
56
+ cmd.alias('ec');
57
+ cmd.option(
58
+ '--keyring-backend <os|file|test>',
59
+ `keyring's backend (os|file|test) (default "${defaultKeyring}")`,
60
+ defaultKeyring,
61
+ );
43
62
 
44
63
  /** @param {string} literalOrName */
45
64
  const normalizeAddress = literalOrName =>
46
- normalizeAddressWithOptions(literalOrName, { keyringBackend: 'test' });
65
+ normalizeAddressWithOptions(literalOrName, {
66
+ // FIXME does not observe keyring-backend option, which isn't available during arg parsing
67
+ keyringBackend: defaultKeyring,
68
+ });
47
69
 
48
70
  /** @type {(info: unknown, indent?: unknown) => boolean } */
49
71
  const show = (info, indent) =>
@@ -53,6 +75,8 @@ export const makeEconomicCommiteeCommand = (_logger, io = {}) => {
53
75
  const done = found.filter(it => it.instanceName === instanceName);
54
76
  if (done.length > 0) {
55
77
  console.warn(`invitation to ${instanceName} already accepted`, done);
78
+ // CommanderError is a class constructor, and so
79
+ // must be invoked with `new`.
56
80
  throw new CommanderError(1, 'EALREADY', `already accepted`);
57
81
  }
58
82
  };
@@ -62,20 +86,25 @@ export const makeEconomicCommiteeCommand = (_logger, io = {}) => {
62
86
  * given a sendFrom address; else print it.
63
87
  *
64
88
  * @param {{
65
- * toOffer: (agoricNames: *, current: import('@agoric/smart-wallet/src/smartWallet').CurrentWalletRecord | undefined) => OfferSpec,
89
+ * toOffer: (agoricNames: *, current: import('@agoric/smart-wallet/src/smartWallet.js').CurrentWalletRecord | undefined) => OfferSpec,
66
90
  * sendFrom?: string | undefined,
91
+ * keyringBackend: string,
67
92
  * instanceName?: string,
68
93
  * }} detail
69
- * @param {Awaited<ReturnType<makeRpcUtils>>} [optUtils]
94
+ * @param {Awaited<ReturnType<makeVstorageKit>>} [optUtils]
70
95
  */
71
- const processOffer = async function ({ toOffer, sendFrom }, optUtils) {
72
- const networkConfig = await getNetworkConfig(env);
73
- const utils = await (optUtils || makeRpcUtils({ fetch }));
74
- const { agoricNames, readLatestHead } = utils;
96
+ const processOffer = async function (
97
+ { toOffer, sendFrom, keyringBackend },
98
+ optUtils,
99
+ ) {
100
+ const utils = await (optUtils || makeVstorageKit({ fetch }, networkConfig));
101
+ const { agoricNames, readPublished } = utils;
102
+
103
+ assert(keyringBackend, 'missing keyring-backend option');
75
104
 
76
105
  let current;
77
106
  if (sendFrom) {
78
- current = await getCurrent(sendFrom, { readLatestHead });
107
+ current = await getCurrent(sendFrom, { readPublished });
79
108
  }
80
109
 
81
110
  const offer = toOffer(agoricNames, current);
@@ -90,7 +119,7 @@ export const makeEconomicCommiteeCommand = (_logger, io = {}) => {
90
119
  const result = await sendAction(
91
120
  { method: 'executeOffer', offer },
92
121
  {
93
- keyring: { backend: 'test' }, // XXX
122
+ keyring: { backend: keyringBackend },
94
123
  from: sendFrom,
95
124
  verbose: false,
96
125
  ...networkConfig,
@@ -105,9 +134,9 @@ export const makeEconomicCommiteeCommand = (_logger, io = {}) => {
105
134
  show({ timestamp, height, offerId: offer.id, txhash });
106
135
  const checkInWallet = async blockInfo => {
107
136
  const [state, update] = await Promise.all([
108
- getCurrent(sendFrom, { readLatestHead }),
109
- getLastUpdate(sendFrom, { readLatestHead }),
110
- readLatestHead(`published.wallet.${sendFrom}`),
137
+ getCurrent(sendFrom, { readPublished }),
138
+ getLastUpdate(sendFrom, { readPublished }),
139
+ readPublished(`wallet.${sendFrom}`),
111
140
  ]);
112
141
  if (update.updated === 'offerStatus' && update.status.id === offer.id) {
113
142
  return blockInfo;
@@ -127,29 +156,38 @@ export const makeEconomicCommiteeCommand = (_logger, io = {}) => {
127
156
  show(blockInfo);
128
157
  };
129
158
 
130
- ec.command('committee')
131
- .description('accept invitation to join the economic committee')
159
+ cmd
160
+ .command('committee')
161
+ .description('accept invitation to join a committee')
162
+ .requiredOption(
163
+ '--name <string>',
164
+ 'Committee instance name',
165
+ String,
166
+ 'economicCommittee',
167
+ )
132
168
  .option('--voter <number>', 'Voter number', Number, 0)
133
169
  .option(
134
170
  '--offerId <string>',
135
171
  'Offer id',
136
172
  String,
137
- `ecCommittee-${Date.now()}`,
173
+ `gov-committee-${Date.now()}`,
138
174
  )
139
175
  .option(
140
176
  '--send-from <name-or-address>',
141
177
  'Send from address',
142
178
  normalizeAddress,
143
179
  )
144
- .action(async function (opts) {
180
+ .action(async function (opts, options) {
181
+ const { name: instanceName } = opts;
182
+
145
183
  /** @type {Parameters<typeof processOffer>[0]['toOffer']} */
146
184
  const toOffer = (agoricNames, current) => {
147
- const instance = agoricNames.instance.economicCommittee;
148
- assert(instance, `missing economicCommittee`);
185
+ const instance = agoricNames.instance[instanceName];
186
+ assert(instance, `missing ${instanceName}`);
149
187
 
150
188
  if (current) {
151
189
  const found = findContinuingIds(current, agoricNames);
152
- abortIfSeen('economicCommittee', found);
190
+ abortIfSeen(instanceName, found);
153
191
  }
154
192
 
155
193
  return {
@@ -165,28 +203,37 @@ export const makeEconomicCommiteeCommand = (_logger, io = {}) => {
165
203
 
166
204
  await processOffer({
167
205
  toOffer,
168
- instanceName: 'economicCommittee',
169
- ...opts,
206
+ instanceName,
207
+ sendFrom: opts.sendFrom,
208
+ keyringBackend: options.optsWithGlobals().keyringBackend,
170
209
  });
171
210
  });
172
211
 
173
- ec.command('charter')
212
+ cmd
213
+ .command('charter')
174
214
  .description('accept the charter invitation')
175
- .option('--offerId <string>', 'Offer id', String, `ecCharter-${Date.now()}`)
215
+ .requiredOption(
216
+ '--name <string>',
217
+ 'Charter instance name',
218
+ 'economicCommitteeCharter',
219
+ )
220
+ .option('--offerId <string>', 'Offer id', String, `charter-${Date.now()}`)
176
221
  .option(
177
222
  '--send-from <name-or-address>',
178
223
  'Send from address',
179
224
  normalizeAddress,
180
225
  )
181
- .action(async function (opts) {
226
+ .action(async function (opts, options) {
227
+ const { name: instanceName } = opts;
228
+
182
229
  /** @type {Parameters<typeof processOffer>[0]['toOffer']} */
183
230
  const toOffer = (agoricNames, current) => {
184
- const instance = agoricNames.instance.econCommitteeCharter;
185
- assert(instance, `missing econCommitteeCharter`);
231
+ const instance = agoricNames.instance[instanceName];
232
+ assert(instance, `missing ${instanceName}`);
186
233
 
187
234
  if (current) {
188
235
  const found = findContinuingIds(current, agoricNames);
189
- abortIfSeen('econCommitteeCharter', found);
236
+ abortIfSeen(instanceName, found);
190
237
  }
191
238
 
192
239
  return {
@@ -202,12 +249,14 @@ export const makeEconomicCommiteeCommand = (_logger, io = {}) => {
202
249
 
203
250
  await processOffer({
204
251
  toOffer,
205
- instanceName: 'econCommitteeCharter',
206
- ...opts,
252
+ instanceName,
253
+ sendFrom: opts.sendFrom,
254
+ keyringBackend: options.optsWithGlobals().keyringBackend,
207
255
  });
208
256
  });
209
257
 
210
- ec.command('find-continuing-id')
258
+ cmd
259
+ .command('find-continuing-id')
211
260
  .description('print id of specified voting continuing invitation')
212
261
  .requiredOption(
213
262
  '--from <name-or-address>',
@@ -216,8 +265,11 @@ export const makeEconomicCommiteeCommand = (_logger, io = {}) => {
216
265
  )
217
266
  .requiredOption('--for <string>', 'description of the invitation')
218
267
  .action(async opts => {
219
- const { agoricNames, readLatestHead } = await makeRpcUtils({ fetch });
220
- const current = await getCurrent(opts.from, { readLatestHead });
268
+ const { agoricNames, readPublished } = await makeVstorageKit(
269
+ { fetch },
270
+ networkConfig,
271
+ );
272
+ const current = await getCurrent(opts.from, { readPublished });
221
273
 
222
274
  const known = findContinuingIds(current, agoricNames);
223
275
  if (!known) {
@@ -233,7 +285,8 @@ export const makeEconomicCommiteeCommand = (_logger, io = {}) => {
233
285
  console.log(match.offerId);
234
286
  });
235
287
 
236
- ec.command('find-continuing-ids')
288
+ cmd
289
+ .command('find-continuing-ids')
237
290
  .description('print records of voting continuing invitations')
238
291
  .requiredOption(
239
292
  '--from <name-or-address>',
@@ -241,16 +294,34 @@ export const makeEconomicCommiteeCommand = (_logger, io = {}) => {
241
294
  normalizeAddress,
242
295
  )
243
296
  .action(async opts => {
244
- const { agoricNames, readLatestHead } = await makeRpcUtils({ fetch });
245
- const current = await getCurrent(opts.from, { readLatestHead });
297
+ const { agoricNames, readPublished } = await makeVstorageKit(
298
+ { fetch },
299
+ networkConfig,
300
+ );
301
+ const current = await getCurrent(opts.from, { readPublished });
246
302
 
247
303
  const found = findContinuingIds(current, agoricNames);
248
- found.forEach(it => show({ ...it, address: opts.from }));
304
+ for (const it of found) {
305
+ show({ ...it, address: opts.from });
306
+ }
249
307
  });
250
308
 
251
- ec.command('vote')
252
- .description('vote on a question (hard-coded for now))')
253
- .option('--offerId <number>', 'Offer id', String, `ecVote-${Date.now()}`)
309
+ cmd
310
+ .command('vote')
311
+ .description('vote on latest question')
312
+ .requiredOption(
313
+ '--instance <string>',
314
+ 'Committee name under agoricNames.instances',
315
+ String,
316
+ 'economicCommittee',
317
+ )
318
+ .requiredOption(
319
+ '--pathname <string>',
320
+ 'Committee name under published.committees',
321
+ String,
322
+ 'Economic_Committee',
323
+ )
324
+ .option('--offerId <number>', 'Offer id', String, `gov-vote-${Date.now()}`)
254
325
  .requiredOption(
255
326
  '--forPosition <number>',
256
327
  'index of one position to vote for (within the question description.positions); ',
@@ -261,17 +332,17 @@ export const makeEconomicCommiteeCommand = (_logger, io = {}) => {
261
332
  'Send from address',
262
333
  normalizeAddress,
263
334
  )
264
- .action(async function (opts) {
265
- const utils = await makeRpcUtils({ fetch });
266
- const { readLatestHead } = utils;
335
+ .action(async function (opts, options) {
336
+ const utils = await makeVstorageKit({ fetch }, networkConfig);
337
+ const { readPublished } = utils;
267
338
 
268
- const info = await readLatestHead(
269
- 'published.committees.Economic_Committee.latestQuestion',
339
+ const questionDesc = await readPublished(
340
+ `committees.${opts.pathname}.latestQuestion`,
270
341
  ).catch(err => {
342
+ // CommanderError is a class constructor, and so
343
+ // must be invoked with `new`.
271
344
  throw new CommanderError(1, 'VSTORAGE_FAILURE', err.message);
272
345
  });
273
- // XXX runtime shape-check
274
- const questionDesc = /** @type {any} */ (info);
275
346
 
276
347
  // TODO support multiple position arguments
277
348
  const chosenPositions = [questionDesc.positions[opts.forPosition]];
@@ -280,11 +351,11 @@ export const makeEconomicCommiteeCommand = (_logger, io = {}) => {
280
351
  /** @type {Parameters<typeof processOffer>[0]['toOffer']} */
281
352
  const toOffer = (agoricNames, current) => {
282
353
  const cont = current ? findContinuingIds(current, agoricNames) : [];
283
- const votingRight = cont.find(
284
- it => it.instance === agoricNames.instance.economicCommittee,
285
- );
354
+ const votingRight = cont.find(it => it.instanceName === opts.instance);
286
355
  if (!votingRight) {
287
356
  console.debug('continuing ids', cont, 'for', current);
357
+ // CommanderError is a class constructor, and so
358
+ // must be invoked with `new`.
288
359
  throw new CommanderError(
289
360
  1,
290
361
  'NO_INVITATION',
@@ -307,8 +378,89 @@ export const makeEconomicCommiteeCommand = (_logger, io = {}) => {
307
378
  };
308
379
  };
309
380
 
310
- await processOffer({ toOffer, sendFrom: opts.sendFrom }, utils);
381
+ await processOffer(
382
+ {
383
+ toOffer,
384
+ sendFrom: opts.sendFrom,
385
+ keyringBackend: options.optsWithGlobals().keyringBackend,
386
+ },
387
+ utils,
388
+ );
389
+ });
390
+
391
+ cmd
392
+ .command('proposePauseOffers')
393
+ .description('propose a vote to pause offers')
394
+ .option(
395
+ '--send-from <name-or-address>',
396
+ 'Send from address',
397
+ normalizeAddress,
398
+ )
399
+ .option(
400
+ '--offerId <string>',
401
+ 'Offer id',
402
+ String,
403
+ `proposePauseOffers-${Date.now()}`,
404
+ )
405
+ .requiredOption(
406
+ '--instance <string>',
407
+ 'name of governed instance in agoricNames',
408
+ )
409
+ .requiredOption(
410
+ '--substring <string>',
411
+ 'an offer string to pause (can be repeated)',
412
+ collectValues,
413
+ [],
414
+ )
415
+ .option(
416
+ '--deadline <minutes>',
417
+ 'minutes from now to close the vote',
418
+ Number,
419
+ 1,
420
+ )
421
+ .action(async function (opts, options) {
422
+ const { instance: instanceName } = opts;
423
+
424
+ /** @type {Parameters<typeof processOffer>[0]['toOffer']} */
425
+ const toOffer = (agoricNames, current) => {
426
+ const instance = agoricNames.instance[instanceName];
427
+ assert(instance, `missing ${instanceName}`);
428
+ assert(current, 'missing current wallet');
429
+
430
+ const known = findContinuingIds(current, agoricNames);
431
+
432
+ assert(known, 'could not find committee acceptance offer id');
433
+
434
+ // TODO magic string
435
+ const match = known.find(
436
+ r => r.description === 'charter member invitation',
437
+ );
438
+ assert(match, 'no offer found for charter member invitation');
439
+
440
+ return {
441
+ id: opts.offerId,
442
+ invitationSpec: {
443
+ source: 'continuing',
444
+ previousOffer: match.offerId,
445
+ invitationMakerName: 'VoteOnPauseOffers',
446
+ // ( instance, strings list, timer deadline seconds )
447
+ invitationArgs: harden([
448
+ instance,
449
+ opts.substring,
450
+ BigInt(opts.deadline * 60 + Math.round(Date.now() / 1000)),
451
+ ]),
452
+ },
453
+ proposal: {},
454
+ };
455
+ };
456
+
457
+ await processOffer({
458
+ toOffer,
459
+ instanceName,
460
+ sendFrom: opts.sendFrom,
461
+ keyringBackend: options.optsWithGlobals().keyringBackend,
462
+ });
311
463
  });
312
464
 
313
- return ec;
465
+ return cmd;
314
466
  };