diff --git a/packages/boot/test/bootstrapTests/ec-membership-update.test.ts b/packages/boot/test/bootstrapTests/ec-membership-update.test.ts index b48948acba7..b4ec16deb10 100644 --- a/packages/boot/test/bootstrapTests/ec-membership-update.test.ts +++ b/packages/boot/test/bootstrapTests/ec-membership-update.test.ts @@ -631,7 +631,7 @@ test.serial('EC can govern reserve parameter', async t => { const { reserve } = agoricNamesRemotes.instance; const { IST } = agoricNamesRemotes.brand; - t.log('Proposing question using new charter invitation for reserve'); + t.log('Proposing question using new charter invitation for reserve', reserve); await governanceDriver.proposeApiCall( reserve, 'burnFeesToReduceShortfall', diff --git a/packages/boot/test/bootstrapTests/upgradeAPI.test.ts b/packages/boot/test/bootstrapTests/upgradeAPI.test.ts index 27ad2c73ab1..a187dbef976 100644 --- a/packages/boot/test/bootstrapTests/upgradeAPI.test.ts +++ b/packages/boot/test/bootstrapTests/upgradeAPI.test.ts @@ -4,13 +4,28 @@ import path from 'path'; import bundleSource from '@endo/bundle-source'; import { CONTRACT_ELECTORATE, ParamTypes } from '@agoric/governance'; import { MALLEABLE_NUMBER } from '@agoric/governance/test/swingsetTests/contractGovernor/governedContract.js'; +import { makeAgoricNamesRemotesFromFakeStorage } from '@agoric/vats/tools/board-utils.js'; + import { makeSwingsetTestKit } from '../../tools/supports.js'; +import { + makeGovernanceDriver, + makeWalletFactoryDriver, +} from '../../tools/drivers.js'; const dirname = path.dirname(new URL(import.meta.url).pathname); const GOVERNED_CONTRACT_SRC = './governedContract.js'; const GOVERNED_CONTRACT2_SRC = './governedContract2.js'; +const wallets = [ + 'agoric1gx9uu7y6c90rqruhesae2t7c2vlw4uyyxlqxrx', + 'agoric1d4228cvelf8tj65f4h7n2td90sscavln2283h5', + 'agoric14543m33dr28x7qhwc558hzlj9szwhzwzpcmw6a', + 'agoric13p9adwk0na5npfq64g22l6xucvqdmu3xqe70wq', + 'agoric1el6zqs8ggctj5vwyukyk4fh50wcpdpwgugd5l5', + 'agoric1zayxg4e9vd0es9c9jlpt36qtth255txjp6a8yc', +]; + const setUpGovernedContract = async (zoe, timer, EV, controller) => { const installBundle = contractBundle => EV(zoe).install(contractBundle); const installBundleToVatAdmin = contractBundle => @@ -88,7 +103,7 @@ const setUpGovernedContract = async (zoe, timer, EV, controller) => { }; // A more minimal set would be better. We need governance, but not econ vats. -const PLATFORM_CONFIG = '@agoric/vm-config/decentral-test-vaults-config.json'; +const PLATFORM_CONFIG = '@agoric/vm-config/decentral-main-vaults-config.json'; const makeDefaultTestContext = async t => { console.time('DefaultTestContext'); @@ -102,9 +117,25 @@ const makeDefaultTestContext = async t => { const zoe: ZoeService = await EV.vat('bootstrap').consumeItem('zoe'); const timer = await EV.vat('bootstrap').consumeItem('chainTimerService'); + // has to be late enough for agoricNames data to have been published + const agoricNamesRemotes = makeAgoricNamesRemotesFromFakeStorage(storage); + + const walletFactoryDriver = await makeWalletFactoryDriver( + runUtils, + storage, + agoricNamesRemotes, + ); + + const governanceDriver = await makeGovernanceDriver( + swingsetTestKit, + agoricNamesRemotes, + walletFactoryDriver, + wallets, + ); + const facets = await setUpGovernedContract(zoe, timer, EV, controller); - return { ...swingsetTestKit, facets }; + return { ...swingsetTestKit, facets, governanceDriver }; }; const test = anyTest as TestFn< @@ -119,7 +150,7 @@ test.after.always(t => { return t.context.shutdown && t.context.shutdown(); }); -test(`start contract; verify`, async t => { +test.serial(`start contract; verify`, async t => { const { runUtils, facets } = t.context; const { governorFacets: { creatorFacet }, @@ -132,39 +163,95 @@ test(`start contract; verify`, async t => { t.is(avogadro, 602214090000000000000000n); }); -test(`verify API governance`, async t => { - const { runUtils, facets } = t.context; +const getQuestionId = id => `propose-question-${id}`; +const getVoteId = id => `vote-${id}`; + +const offerIds = { + invite: { charter: 'ch', committee: 'ctt' }, + add1: { outgoing: 'add1' }, + add2: { outgoing: 'add2' }, +}; + +let governedContract; +test.serial(`verify API governance`, async t => { + const { runUtils, facets, governanceDriver, storage, advanceTimeBy } = + t.context; const { - governorFacets: { creatorFacet }, + governorFacets: { creatorFacet, instance }, voteCounterInstallation: vci, } = facets; const { EV } = runUtils; + const contractPublicFacet = await EV(creatorFacet).getPublicFacet(); + + const committee = governanceDriver.ecMembers; - const question = await EV(creatorFacet).voteOnApiInvocation( + const agoricNamesAdmin = + await EV.vat('bootstrap').consumeItem('agoricNamesAdmin'); + const instanceAdmin = await EV(agoricNamesAdmin).lookupAdmin('instance'); + await EV(instanceAdmin).update('governedContract', instance); + const agoricNamesRemotes = makeAgoricNamesRemotesFromFakeStorage(storage); + ({ governedContract } = agoricNamesRemotes.instance); + + await null; + for (const member of committee) { + await member.acceptOutstandingCharterInvitation(offerIds.invite.charter); + await member.acceptOutstandingCommitteeInvitation( + offerIds.invite.committee, + ); + } + const econCharterKit = + await EV.vat('bootstrap').consumeItem('econCharterKit'); + + const charterCreatorFacet = await EV.get(econCharterKit).creatorFacet; + await EV(charterCreatorFacet).addInstance( + instance, + creatorFacet, + 'governedContract', + ); + + await governanceDriver.proposeApiCall( + governedContract, 'add1', [], - vci, - 37n, + committee[0], + getQuestionId(1), + offerIds.invite.charter, ); - t.truthy(question.instance); - await t.throwsAsync( - () => EV(creatorFacet).voteOnApiInvocation('add2', [], vci, 37n), - { - message: /"add2" is not a governed API./, - }, + t.like(committee[0].getLatestUpdateRecord(), { + status: { id: getQuestionId(1), numWantsSatisfied: 1 }, + }); + + await governanceDriver.enactLatestProposal( + committee, + getVoteId(1), + offerIds.invite.committee, ); + + for (const w of committee.slice(0, 2)) { + t.like(w.getLatestUpdateRecord(), { + status: { id: getVoteId(1), numWantsSatisfied: 1 }, + }); + } + await advanceTimeBy(1, 'minutes'); + + const lastOutcome = await governanceDriver.getLatestOutcome(); + t.is(lastOutcome.outcome, 'win'); + + const calls = await EV(contractPublicFacet).getApiCalled(); + t.is(calls, 1); }); -test(`upgrade; verify enhanced API governance`, async t => { - const { runUtils, facets } = t.context; +test.serial(`upgrade`, async t => { + const { runUtils, facets, governanceDriver, advanceTimeBy } = t.context; const { governorFacets: { creatorFacet }, voteCounterInstallation: vci, contract2SHA, poserInvitation2, } = facets; + const committee = governanceDriver.ecMembers; const { EV } = runUtils; const af = await EV(creatorFacet).getAdminFacet(); @@ -173,11 +260,52 @@ test(`upgrade; verify enhanced API governance`, async t => { initialPoserInvitation: poserInvitation2, }); - const question2 = await EV(creatorFacet).voteOnApiInvocation( + const contractPublicFacet = await EV(creatorFacet).getPublicFacet(); + const calls = await EV(contractPublicFacet).getApiCalled(); + t.is(calls, 1); +}); + +test.serial(`verify API governance post-upgrade`, async t => { + const { runUtils, facets, governanceDriver, advanceTimeBy } = t.context; + const { + governorFacets: { creatorFacet, instance }, + voteCounterInstallation: vci, + } = facets; + + const { EV } = runUtils; + + const committee = governanceDriver.ecMembers; + + await governanceDriver.proposeApiCall( + governedContract, 'add2', [], - vci, - 37n, + committee[0], + getQuestionId(2), + offerIds.invite.charter, ); - t.truthy(question2.instance); + + t.like(committee[0].getLatestUpdateRecord(), { + status: { id: getQuestionId(2), numWantsSatisfied: 1 }, + }); + + await governanceDriver.enactLatestProposal( + committee, + getVoteId(2), + offerIds.invite.committee, + ); + + for (const w of committee.slice(0, 2)) { + t.like(w.getLatestUpdateRecord(), { + status: { id: getVoteId(2), numWantsSatisfied: 1 }, + }); + } + await advanceTimeBy(1, 'minutes'); + + const lastOutcome = await governanceDriver.getLatestOutcome(); + t.is(lastOutcome.outcome, 'win'); + + const contractPublicFacet = await EV(creatorFacet).getPublicFacet(); + const calls = await EV(contractPublicFacet).getApiCalled(); + t.is(calls, 3); }); diff --git a/packages/boot/tools/drivers.ts b/packages/boot/tools/drivers.ts index 850cf668d04..342d4637c93 100644 --- a/packages/boot/tools/drivers.ts +++ b/packages/boot/tools/drivers.ts @@ -248,6 +248,7 @@ export const makeGovernanceDriver = async ( charterOfferId = charterMembershipId, instance = agoricNamesRemotes.instance.econCommitteeCharter, ) => { + console.log('ECM accept', charterOfferId); if (!findInvitation(w, 'charter member invitation')) { console.log('No charter member invitation found'); return; @@ -320,6 +321,7 @@ export const makeGovernanceDriver = async ( })); const ensureInvitationsAccepted = async () => { + console.log('DRIV ensure', invitationsAccepted); if (invitationsAccepted) { return; } @@ -400,6 +402,7 @@ export const makeGovernanceDriver = async ( enactLatestProposal, getLatestOutcome, async changeParams(instance: Instance, params: Object, path?: object) { + console.log('DRIV changePs'); instance || Fail`missing instance`; await ensureInvitationsAccepted(); await proposeParams(instance, params, path);