From db0cd3769e9431ad0bbf80736de2ba69488c9f0d Mon Sep 17 00:00:00 2001 From: Jorge-Lopes Date: Mon, 9 Dec 2024 20:15:57 +0000 Subject: [PATCH] chore(governance):Refactor makeGovernanceDriver methods to be contract agnostic --- .../proposals/z:acceptance/governance.test.js | 68 +++--------- .../z:acceptance/test-lib/governance.js | 102 ++++++++++++++++-- .../z:acceptance/test-lib/psm-lib.js | 2 +- 3 files changed, 105 insertions(+), 67 deletions(-) diff --git a/a3p-integration/proposals/z:acceptance/governance.test.js b/a3p-integration/proposals/z:acceptance/governance.test.js index 353590370eb..24fc7b24796 100644 --- a/a3p-integration/proposals/z:acceptance/governance.test.js +++ b/a3p-integration/proposals/z:acceptance/governance.test.js @@ -1,53 +1,36 @@ -/* global fetch setTimeout */ +/* global fetch */ import test from 'ava'; -import { makeWalletUtils } from '@agoric/client-utils'; import { GOV1ADDR, GOV2ADDR } from '@agoric/synthetic-chain'; import { makeGovernanceDriver } from './test-lib/governance.js'; -import { networkConfig } from './test-lib/index.js'; -import { makeTimerUtils } from './test-lib/utils.js'; +import { networkConfig, walletUtils } from './test-lib/index.js'; const GOV4ADDR = 'agoric1c9gyu460lu70rtcdp95vummd6032psmpdx7wdy'; const governanceAddresses = [GOV4ADDR, GOV2ADDR, GOV1ADDR]; -// TODO test-lib export `walletUtils` with this ambient authority like s:stake-bld has -/** @param {number} ms */ -const delay = ms => - new Promise(resolve => setTimeout(() => resolve(undefined), ms)); +const { getLastUpdate } = walletUtils; +const governanceDriver = await makeGovernanceDriver(fetch, networkConfig); test.serial( 'economic committee can make governance proposal and vote on it', async t => { - const { waitUntil } = makeTimerUtils({ setTimeout }); - const { readLatestHead, getLastUpdate, getCurrentWalletRecord } = - await makeWalletUtils({ delay, fetch }, networkConfig); - const governanceDriver = await makeGovernanceDriver(fetch, networkConfig); - - /** @type {any} */ - const instance = await readLatestHead(`published.agoricNames.instance`); - const instances = Object.fromEntries(instance); - - const wallet = await getCurrentWalletRecord(governanceAddresses[0]); - const usedInvitations = wallet.offerToUsedInvitation; - - const charterInvitation = usedInvitations.find( - v => - v[1].value[0].instance.getBoardId() === - instances.econCommitteeCharter.getBoardId(), + const charterInvitation = await governanceDriver.getCharterInvitation( + governanceAddresses[0], ); - assert(charterInvitation, 'missing charter invitation'); const params = { ChargingPeriod: 400n, }; const path = { paramPath: { key: 'governedParams' } }; t.log('Proposing param change', { params, path }); + const instanceName = 'VaultFactory'; - await governanceDriver.proposeVaultDirectorParamChange( + await governanceDriver.proposeParamChange( governanceAddresses[0], params, path, + instanceName, charterInvitation[0], ); @@ -59,22 +42,9 @@ test.serial( t.log('Voting on param change'); for (const address of governanceAddresses) { - const voteWallet = - /** @type {import('@agoric/smart-wallet/src/smartWallet.js').CurrentWalletRecord} */ ( - await readLatestHead(`published.wallet.${address}.current`) - ); - - const usedInvitationsForVoter = voteWallet.offerToUsedInvitation; + const committeeInvitationForVoter = + await governanceDriver.getCommitteeInvitation(address); - const committeeInvitationForVoter = usedInvitationsForVoter.find( - v => - v[1].value[0].instance.getBoardId() === - instances.economicCommittee.getBoardId(), - ); - assert( - committeeInvitationForVoter, - `${address} must have committee invitation`, - ); await governanceDriver.voteOnProposedChanges( address, committeeInvitationForVoter[0], @@ -87,21 +57,7 @@ test.serial( }); } - const latestQuestion = - /** @type {import('@agoric/governance/src/types.js').QuestionSpec} */ ( - await readLatestHead( - 'published.committees.Economic_Committee.latestQuestion', - ) - ); - t.log('Waiting for deadline', latestQuestion); - /** @type {bigint} */ - // @ts-expect-error assume POSIX seconds since epoch - const deadline = latestQuestion.closingRule.deadline; - await waitUntil(deadline); - - const latestOutcome = await readLatestHead( - 'published.committees.Economic_Committee.latestOutcome', - ); + const { latestOutcome } = await governanceDriver.getLatestQuestion(); t.log('Verifying latest outcome', latestOutcome); t.like(latestOutcome, { outcome: 'win' }); }, diff --git a/a3p-integration/proposals/z:acceptance/test-lib/governance.js b/a3p-integration/proposals/z:acceptance/test-lib/governance.js index 27c881c0ed2..6e9304a23bd 100644 --- a/a3p-integration/proposals/z:acceptance/test-lib/governance.js +++ b/a3p-integration/proposals/z:acceptance/test-lib/governance.js @@ -1,5 +1,18 @@ +/* global fetch setTimeout */ + import { agops, agoric, executeOffer } from '@agoric/synthetic-chain'; -import { makeVstorageKit } from '@agoric/client-utils'; +import { + boardSlottingMarshaller, + makeFromBoard, + makeVstorageKit, + retryUntilCondition, +} from '@agoric/client-utils'; +import { makeVStorage } from './rpc.js'; +import { walletUtils } from './index.js'; +import { + checkCommitteeElectionResult, + fetchLatestEcQuestion, +} from './psm-lib.js'; /** * @param {typeof window.fetch} fetch @@ -11,6 +24,8 @@ export const makeGovernanceDriver = async (fetch, networkConfig) => { networkConfig, ); + let deadline; + /** @param {string} previousOfferId */ const generateVoteOffer = async previousOfferId => { const latestQuestionRecord = @@ -63,7 +78,7 @@ export const makeGovernanceDriver = async (fetch, networkConfig) => { }; /** - * Generates a vault director parameter change proposal as a `executeOffer` message + * Generates a parameter change proposal as a `executeOffer` message * body. * * @param {string} previousOfferId - the `id` of the offer that this proposal is @@ -72,13 +87,15 @@ export const makeGovernanceDriver = async (fetch, networkConfig) => { * be open for (in seconds) * @param {any} params * @param {{ paramPath: any; }} paramsPath + * @param {string} instanceName * @returns {Promise} - the `executeOffer` message body as a JSON string */ - const generateVaultDirectorParamChange = async ( + const generateParamChange = async ( previousOfferId, voteDur, params, paramsPath, + instanceName, ) => { const instancesRaw = await agoric.follow( '-lF', @@ -89,12 +106,12 @@ export const makeGovernanceDriver = async (fetch, networkConfig) => { const instances = Object.fromEntries( marshaller.fromCapData(JSON.parse(instancesRaw)), ); - const { VaultFactory } = instances; - assert(VaultFactory); + const instance = instances[instanceName]; + assert(instance); const msSinceEpoch = Date.now(); const id = `propose-${msSinceEpoch}`; - const deadline = BigInt(Math.ceil(msSinceEpoch / 1000)) + BigInt(voteDur); + deadline = BigInt(Math.ceil(msSinceEpoch / 1000)) + BigInt(voteDur); const body = { method: 'executeOffer', offer: { @@ -106,7 +123,7 @@ export const makeGovernanceDriver = async (fetch, networkConfig) => { }, offerArgs: { deadline, - instance: VaultFactory, + instance, params, path: paramsPath, }, @@ -123,12 +140,14 @@ export const makeGovernanceDriver = async (fetch, networkConfig) => { * @param {string} address * @param {any} params * @param {{paramPath: any}} path + * @param {string} instanceName * @param {string} charterAcceptOfferId */ - const proposeVaultDirectorParamChange = async ( + const proposeParamChange = async ( address, params, path, + instanceName, charterAcceptOfferId, ) => { await null; @@ -144,12 +163,75 @@ export const makeGovernanceDriver = async (fetch, networkConfig) => { return executeOffer( address, - generateVaultDirectorParamChange(offerId, 10, params, path), + generateParamChange(offerId, 30, params, path, instanceName), + ); + }; + + const getCharterInvitation = async address => { + const { getCurrentWalletRecord } = walletUtils; + + /** @type {any} */ + const instance = await readLatestHead(`published.agoricNames.instance`); + const instances = Object.fromEntries(instance); + + const wallet = await getCurrentWalletRecord(address); + const usedInvitations = wallet.offerToUsedInvitation; + + const charterInvitation = usedInvitations.find( + v => + v[1].value[0].instance.getBoardId() === + instances.econCommitteeCharter.getBoardId(), ); + assert(charterInvitation, 'missing charter invitation'); + + return charterInvitation; + }; + + const getCommitteeInvitation = async address => { + /** @type {any} */ + const instance = await readLatestHead(`published.agoricNames.instance`); + const instances = Object.fromEntries(instance); + + const voteWallet = + /** @type {import('@agoric/smart-wallet/src/smartWallet.js').CurrentWalletRecord} */ ( + await readLatestHead(`published.wallet.${address}.current`) + ); + + const usedInvitationsForVoter = voteWallet.offerToUsedInvitation; + + const committeeInvitationForVoter = usedInvitationsForVoter.find( + v => + v[1].value[0].instance.getBoardId() === + instances.economicCommittee.getBoardId(), + ); + assert( + committeeInvitationForVoter, + `${address} must have committee invitation`, + ); + + return committeeInvitationForVoter; + }; + + const getLatestQuestion = async () => { + const { latestOutcome, latestQuestion } = await await retryUntilCondition( + () => fetchLatestEcQuestion({ follow: agoric.follow }), + electionResult => + checkCommitteeElectionResult(electionResult, { + outcome: 'win', + deadline, + }), + 'Governed param change election failed', + { setTimeout, retryIntervalMs: 5000, maxRetries: 15 }, + ); + + return { latestOutcome, latestQuestion }; }; return { voteOnProposedChanges, - proposeVaultDirectorParamChange, + proposeParamChange, + getCharterInvitation, + getCommitteeInvitation, + getLatestQuestion, }; }; diff --git a/a3p-integration/proposals/z:acceptance/test-lib/psm-lib.js b/a3p-integration/proposals/z:acceptance/test-lib/psm-lib.js index 7b5beceb1d1..884d7e27c03 100644 --- a/a3p-integration/proposals/z:acceptance/test-lib/psm-lib.js +++ b/a3p-integration/proposals/z:acceptance/test-lib/psm-lib.js @@ -218,7 +218,7 @@ export const fetchLatestEcQuestion = async io => { return { latestOutcome, latestQuestion }; }; -const checkCommitteeElectionResult = ( +export const checkCommitteeElectionResult = ( /** @type {{ latestOutcome: { outcome: any; question: any; }; latestQuestion: { closingRule: { deadline: any; }; questionHandle: any; }; }} */ electionResult, /** @type {{ outcome: any; deadline: any; }} */ expectedResult, ) => {