diff --git a/packages/boot/test/bootstrapTests/price-feed-replace.test.ts b/packages/boot/test/bootstrapTests/price-feed-replace.test.ts index c137ba91017..4518bb519ef 100644 --- a/packages/boot/test/bootstrapTests/price-feed-replace.test.ts +++ b/packages/boot/test/bootstrapTests/price-feed-replace.test.ts @@ -81,6 +81,13 @@ test.serial('setupVaults; run updatePriceFeeds proposals', async t => { const SOME_GUI = 'someGUIHASH'; await updateVaultDirectorParams(t, gd, SOME_GUI); + const { EV } = t.context.runUtils; + const agoricNames = await EV.vat('bootstrap').consumeItem('agoricNames'); + const oldVaultInstallation = await EV(agoricNames).lookup( + 'installation', + 'VaultFactory', + ); + t.log('building all relevant CoreEvals'); const coreEvals = await Promise.all([ buildProposal(priceFeedBuilder, ['main']), @@ -99,6 +106,16 @@ test.serial('setupVaults; run updatePriceFeeds proposals', async t => { t.not(instancePre, instancePost); await priceFeedDrivers[collateralBrandKey].refreshInvitations(); + + const newVaultInstallation = await EV(agoricNames).lookup( + 'installation', + 'VaultFactory', + ); + + t.notDeepEqual( + newVaultInstallation.getKref(), + oldVaultInstallation.getKref(), + ); }); test.serial('1. place bid', async t => { diff --git a/packages/boot/test/bootstrapTests/updateUpgradedVaultParams.test.ts b/packages/boot/test/bootstrapTests/updateUpgradedVaultParams.test.ts new file mode 100644 index 00000000000..5229aa2e6f7 --- /dev/null +++ b/packages/boot/test/bootstrapTests/updateUpgradedVaultParams.test.ts @@ -0,0 +1,132 @@ +/** + * @file The goal of this test is to show that #9982 is fixed + * We change a parameter so that provideParamGovernance() is called once, and + * paramGoverance has been set. Then upgrade vaultFactory, so any ephemeral + * objects from the contract held by the governor are gone, then try to change + * param again, to show that the bug is fixedd. + */ +import { test as anyTest } from '@agoric/zoe/tools/prepare-test-env-ava.js'; + +import type { TestFn } from 'ava'; +import { makeAgoricNamesRemotesFromFakeStorage } from '@agoric/vats/tools/board-utils'; +import { Fail } from '@endo/errors'; + +import { makeSwingsetTestKit } from '../../tools/supports.js'; +import { + makeGovernanceDriver, + makeWalletFactoryDriver, +} from '../../tools/drivers.js'; +import { updateVaultManagerParams } from '../tools/changeVaultParams.js'; + +const makeDefaultTestContext = async t => { + console.time('DefaultTestContext'); + const swingsetTestKit = await makeSwingsetTestKit(t.log); + + const { runUtils, storage } = swingsetTestKit; + console.timeLog('DefaultTestContext', 'swingsetTestKit'); + const { EV } = runUtils; + + // Wait for ATOM to make it into agoricNames + await EV.vat('bootstrap').consumeItem('vaultFactoryKit'); + console.timeLog('DefaultTestContext', 'vaultFactoryKit'); + + // has to be late enough for agoricNames data to have been published + const agoricNamesRemotes = makeAgoricNamesRemotesFromFakeStorage( + swingsetTestKit.storage, + ); + agoricNamesRemotes.brand.ATOM || Fail`ATOM missing from agoricNames`; + console.timeLog('DefaultTestContext', 'agoricNamesRemotes'); + + const walletFactoryDriver = await makeWalletFactoryDriver( + runUtils, + storage, + agoricNamesRemotes, + ); + console.timeLog('DefaultTestContext', 'walletFactoryDriver'); + + console.timeEnd('DefaultTestContext'); + + const gd = await makeGovernanceDriver( + swingsetTestKit, + agoricNamesRemotes, + walletFactoryDriver, + [ + 'agoric1ldmtatp24qlllgxmrsjzcpe20fvlkp448zcuce', + 'agoric140dmkrz2e42ergjj7gyvejhzmjzurvqeq82ang', + 'agoric1w8wktaur4zf8qmmtn3n7x3r0jhsjkjntcm3u6h', + ], + ); + + return { ...swingsetTestKit, agoricNamesRemotes, gd }; +}; + +const test = anyTest as TestFn< + Awaited> +>; + +test.before(async t => { + t.context = await makeDefaultTestContext(t); +}); +test.after.always(t => { + return t.context.shutdown && t.context.shutdown(); +}); + +const outcome = { + bids: [{ payouts: { Bid: 0, Collateral: 1.800828 } }], +}; + +test('restart vaultFactory, change params', async t => { + const { runUtils, gd, agoricNamesRemotes } = t.context; + const { EV } = runUtils; + const vaultFactoryKit = + await EV.vat('bootstrap').consumeItem('vaultFactoryKit'); + + const { ATOM } = agoricNamesRemotes.brand; + ATOM || Fail`ATOM missing from agoricNames`; + + const reserveKit = await EV.vat('bootstrap').consumeItem('reserveKit'); + const bootstrapVat = EV.vat('bootstrap'); + const electorateCreatorFacet = await bootstrapVat.consumeItem( + 'economicCommitteeCreatorFacet', + ); + + const poserInvitation = await EV(electorateCreatorFacet).getPoserInvitation(); + const creatorFacet1 = await EV.get(reserveKit).creatorFacet; + const shortfallInvitation = + await EV(creatorFacet1).makeShortfallReportingInvitation(); + + const zoe: ZoeService = await EV.vat('bootstrap').consumeItem('zoe'); + const brands = await EV(zoe).getBrands(vaultFactoryKit.instance); + const getDebtLimitValue = async () => { + const params = await EV(vaultFactoryKit.publicFacet).getGovernedParams({ + collateralBrand: brands.ATOM, + }); + + // @ts-expect-error getGovernedParams doesn't declare these fields + return params.DebtLimit.value.value; + }; + + // Change the value of a param before the upgrade so paramGovernance is set + t.is(await getDebtLimitValue(), 1_000_000_000n); + await updateVaultManagerParams(t, gd, ATOM, 50_000_000n); + + t.is(await getDebtLimitValue(), 50_000_000n); + + const privateArgs = { + // @ts-expect-error cast XXX missing from type + ...vaultFactoryKit.privateArgs, + initialPoserInvitation: poserInvitation, + initialShortfallInvitation: shortfallInvitation, + }; + + const vfAdminFacet = await EV( + vaultFactoryKit.governorCreatorFacet, + ).getAdminFacet(); + + t.log('awaiting VaultFactory restartContract'); + const upgradeResult = await EV(vfAdminFacet).restartContract(privateArgs); + t.deepEqual(upgradeResult, { incarnationNumber: 1 }); + + await updateVaultManagerParams(t, gd, ATOM, 150_000_000n); + t.is(await getDebtLimitValue(), 150_000_000n); +}); diff --git a/packages/boot/test/bootstrapTests/vaults-upgrade.test.ts b/packages/boot/test/bootstrapTests/vaults-upgrade.test.ts index 4af94865058..b2de8652855 100644 --- a/packages/boot/test/bootstrapTests/vaults-upgrade.test.ts +++ b/packages/boot/test/bootstrapTests/vaults-upgrade.test.ts @@ -286,11 +286,16 @@ test.serial('restart vaultFactory', async t => { const vaultFactoryKit = await EV.vat('bootstrap').consumeItem('vaultFactoryKit'); - const reserveKit = await EV.vat('bootstrap').consumeItem('reserveKit'); const bootstrapVat = EV.vat('bootstrap'); + const reserveKit = await bootstrapVat.consumeItem('reserveKit'); const electorateCreatorFacet = await bootstrapVat.consumeItem( 'economicCommitteeCreatorFacet', ); + const agoricNames = await EV.vat('bootstrap').consumeItem('agoricNames'); + const oldVaultInstallation = await EV(agoricNames).lookup( + 'installation', + 'VaultFactory', + ); const poserInvitation = await EV(electorateCreatorFacet).getPoserInvitation(); const creatorFacet1 = await EV.get(reserveKit).creatorFacet; @@ -319,6 +324,13 @@ test.serial('restart vaultFactory', async t => { const upgradeResult = await EV(vfAdminFacet).restartContract(privateArgs); t.deepEqual(upgradeResult, { incarnationNumber: 1 }); t.like(readCollateralMetrics(0), keyMetrics); // unchanged + + const newVaultInstallation = await EV(agoricNames).lookup( + 'installation', + 'VaultFactory', + ); + + t.notDeepEqual(newVaultInstallation, oldVaultInstallation); }); test.serial('restart contractGovernor', async t => { diff --git a/packages/builders/scripts/vats/add-auction.js b/packages/builders/scripts/vats/add-auction.js index e98d10ef39e..034d95cab1b 100644 --- a/packages/builders/scripts/vats/add-auction.js +++ b/packages/builders/scripts/vats/add-auction.js @@ -7,12 +7,18 @@ export const defaultProposalBuilder = async ({ publishRef, install }) => { getManifestCall: [ 'getManifestForAddAuction', { - auctionsRef: publishRef( + auctioneerRef: publishRef( install( '@agoric/inter-protocol/src/auction/auctioneer.js', '../../inter-protocol/bundles/bundle-auctioneer.js', ), ), + contractGovernorRef: publishRef( + install( + '@agoric/governance/src/contractGovernor.js', + '../bundles/bundle-contractGovernor.js', + ), + ), }, ], }); diff --git a/packages/builders/scripts/vats/upgradeVaults.js b/packages/builders/scripts/vats/upgradeVaults.js index fb77d4ee5f6..406a5990796 100644 --- a/packages/builders/scripts/vats/upgradeVaults.js +++ b/packages/builders/scripts/vats/upgradeVaults.js @@ -7,12 +7,18 @@ export const defaultProposalBuilder = async ({ publishRef, install }) => getManifestCall: [ 'getManifestForUpgradeVaults', { - vaultsRef: publishRef( + VaultFactoryRef: publishRef( install( '@agoric/inter-protocol/src/vaultFactory/vaultFactory.js', '../bundles/bundle-vaultFactory.js', ), ), + contractGovernorRef: publishRef( + install( + '@agoric/governance/src/contractGovernor.js', + '../bundles/bundle-contractGovernor.js', + ), + ), }, ], }); diff --git a/packages/deploy-script-support/src/coreProposalBehavior.js b/packages/deploy-script-support/src/coreProposalBehavior.js index 52cb7641541..73272a4f8ca 100644 --- a/packages/deploy-script-support/src/coreProposalBehavior.js +++ b/packages/deploy-script-support/src/coreProposalBehavior.js @@ -167,6 +167,7 @@ export const makeCoreProposalBehavior = ({ const installAdmin = E(agoricNamesAdmin).lookupAdmin('installation'); await Promise.all( installationEntries.map(([key, value]) => { + produceInstallations[key].reset(); produceInstallations[key].resolve(value); return E(installAdmin).update(key, value); }), diff --git a/packages/governance/src/contractGovernance/governParam.js b/packages/governance/src/contractGovernance/governParam.js index 8ca1b9b7859..3e9a707d9b0 100644 --- a/packages/governance/src/contractGovernance/governParam.js +++ b/packages/governance/src/contractGovernance/governParam.js @@ -55,14 +55,14 @@ const assertBallotConcernsParam = (paramSpec, questionSpec) => { }; /** - * @param {ERef} paramManagerRetriever + * @param {() => ERef} paramManagerRetrieverAccessor * @param {Instance} contractInstance * @param {import('@agoric/time').TimerService} timer * @param {() => Promise} getUpdatedPoserFacet * @returns {ParamGovernor} */ const setupParamGovernance = ( - paramManagerRetriever, + paramManagerRetrieverAccessor, contractInstance, timer, getUpdatedPoserFacet, @@ -76,6 +76,7 @@ const setupParamGovernance = ( deadline, paramSpec, ) => { + const paramManagerRetriever = paramManagerRetrieverAccessor(); const paramMgr = await E(paramManagerRetriever).get(paramSpec.paramPath); /** @type {import('@endo/marshal').Passable} */ const changePs = {}; diff --git a/packages/governance/src/contractGovernorKit.js b/packages/governance/src/contractGovernorKit.js index 02016b36607..358c0d8fd3e 100644 --- a/packages/governance/src/contractGovernorKit.js +++ b/packages/governance/src/contractGovernorKit.js @@ -160,7 +160,7 @@ export const prepareContractGovernorKit = (baggage, powers) => { const { timer } = powers; const { creatorFacet, instance } = this.state; paramGovernance = setupParamGovernance( - E(creatorFacet).getParamMgrRetriever(), + () => E(creatorFacet).getParamMgrRetriever(), instance, timer, () => this.facets.helper.getUpdatedPoserFacet(), diff --git a/packages/inter-protocol/src/proposals/add-auction.js b/packages/inter-protocol/src/proposals/add-auction.js index 6bfad088ee6..29f32c4c477 100644 --- a/packages/inter-protocol/src/proposals/add-auction.js +++ b/packages/inter-protocol/src/proposals/add-auction.js @@ -15,61 +15,52 @@ const trace = makeTracer('NewAuction', true); /** * @param {import('./econ-behaviors.js').EconomyBootstrapPowers & * interlockPowers} powers - * @param {{ options: { auctionsRef: { bundleID: string } } }} options */ -export const addAuction = async ( - { +export const addAuction = async ({ + consume: { + agoricNamesAdmin, + auctioneerKit: legacyKitP, + board, + chainStorage, + chainTimerService, + economicCommitteeCreatorFacet: electorateCreatorFacet, + econCharterKit, + priceAuthority8400, + zoe, + }, + produce: { auctioneerKit: produceAuctioneerKit, auctionUpgradeNewInstance }, + instance: { + consume: { reserve: reserveInstance }, + produce: { auctioneer: auctionInstance }, + }, + installation: { consume: { - agoricNamesAdmin, - auctioneerKit: legacyKitP, - board, - chainStorage, - chainTimerService, - economicCommitteeCreatorFacet: electorateCreatorFacet, - econCharterKit, - priceAuthority8400, - zoe, - }, - produce: { auctioneerKit: produceAuctioneerKit, auctionUpgradeNewInstance }, - instance: { - consume: { reserve: reserveInstance }, - produce: { auctioneer: auctionInstance }, - }, - installation: { - consume: { contractGovernor: contractGovernorInstallation }, - produce: { auctioneer: produceInstallation }, - }, - issuer: { - consume: { [Stable.symbol]: stableIssuerP }, + auctioneer: auctioneerInstallationP, + contractGovernor: governorInstallationP, }, }, - { options }, -) => { - trace('addAuction start', options); + issuer: { + consume: { [Stable.symbol]: stableIssuerP }, + }, +}) => { + trace('addAuction start'); const STORAGE_PATH = 'auction'; - const { auctionsRef } = options; const poserInvitationP = E(electorateCreatorFacet).getPoserInvitation(); - const bundleID = auctionsRef.bundleID; - /** - * @type {Promise< - * Installation - * >} - */ - const installationP = E(zoe).installBundleID(bundleID); - produceInstallation.reset(); - produceInstallation.resolve(installationP); - const [ initialPoserInvitation, electorateInvitationAmount, stableIssuer, legacyKit, + auctioneerInstallation, + governorInstallation, ] = await Promise.all([ poserInvitationP, E(E(zoe).getInvitationIssuer()).getAmountOf(poserInvitationP), stableIssuerP, legacyKitP, + auctioneerInstallationP, + governorInstallationP, ]); // Each field has an extra layer of type + value: @@ -104,12 +95,10 @@ export const addAuction = async ( }, ); - const installation = await installationP; - const governorTerms = await deeplyFulfilledObject( harden({ timer: chainTimerService, - governedContractInstallation: installation, + governedContractInstallation: auctioneerInstallation, governed: { terms: auctionTerms, issuerKeywordRecord: { Bid: stableIssuer }, @@ -120,9 +109,9 @@ export const addAuction = async ( }), ); - /** @type {GovernorStartedInstallationKit} */ + /** @type {GovernorStartedInstallationKit} */ const governorStartResult = await E(zoe).startInstance( - contractGovernorInstallation, + governorInstallation, undefined, governorTerms, harden({ @@ -136,12 +125,17 @@ export const addAuction = async ( 'auctioneer.governor', ); - const [governedInstance, governedCreatorFacet, governedPublicFacet] = - await Promise.all([ - E(governorStartResult.creatorFacet).getInstance(), - E(governorStartResult.creatorFacet).getCreatorFacet(), - E(governorStartResult.creatorFacet).getPublicFacet(), - ]); + const [ + governedInstance, + governedCreatorFacet, + governedPublicFacet, + governedAdminFacet, + ] = await Promise.all([ + E(governorStartResult.creatorFacet).getInstance(), + E(governorStartResult.creatorFacet).getCreatorFacet(), + E(governorStartResult.creatorFacet).getPublicFacet(), + E(governorStartResult.creatorFacet).getAdminFacet(), + ]); const allIssuers = await E(zoe).getIssuers(legacyKit.instance); const { Bid: _istIssuer, ...auctionIssuers } = allIssuers; @@ -157,7 +151,7 @@ export const addAuction = async ( const kit = harden({ label: 'auctioneer', creatorFacet: governedCreatorFacet, - adminFacet: governorStartResult.adminFacet, + adminFacet: governedAdminFacet, publicFacet: governedPublicFacet, instance: governedInstance, @@ -209,10 +203,7 @@ export const ADD_AUCTION_MANIFEST = harden({ produce: { auctioneer: true }, }, installation: { - consume: { - contractGovernor: true, - }, - produce: { auctioneer: true }, + consume: { contractGovernor: true, auctioneer: true }, }, issuer: { consume: { [Stable.symbol]: true }, @@ -223,12 +214,20 @@ export const ADD_AUCTION_MANIFEST = harden({ /** * Add a new auction to a chain that already has one. * - * @param {object} _ign + * @param {object} utils + * @param {any} utils.restoreRef * @param {any} addAuctionOptions */ -export const getManifestForAddAuction = async (_ign, addAuctionOptions) => { +export const getManifestForAddAuction = async ( + { restoreRef }, + { auctioneerRef, contractGovernorRef }, +) => { return { manifest: ADD_AUCTION_MANIFEST, - options: addAuctionOptions, + options: { auctioneerRef, contractGovernorRef }, + installations: { + auctioneer: restoreRef(auctioneerRef), + contractGovernor: restoreRef(contractGovernorRef), + }, }; }; diff --git a/packages/inter-protocol/src/proposals/upgrade-vaults.js b/packages/inter-protocol/src/proposals/upgrade-vaults.js index 8befdd0c5a3..94885fdb743 100644 --- a/packages/inter-protocol/src/proposals/upgrade-vaults.js +++ b/packages/inter-protocol/src/proposals/upgrade-vaults.js @@ -16,7 +16,12 @@ const trace = makeTracer('upgrade Vaults proposal'); /** * @param {import('../../src/proposals/econ-behaviors').EconomyBootstrapPowers & * interlockPowers} powers - * @param {{ options: { vaultsRef: { bundleID: string } } }} options + * @param {{ + * options: { + * VaultFactoryBundle: { bundleID: string }; + * contractGovernorBundle: { bundleID: string }; + * }; + * }} options */ export const upgradeVaults = async ( { @@ -30,16 +35,12 @@ export const upgradeVaults = async ( priceAuthority8400, }, produce: { auctionUpgradeNewInstance: auctionUpgradeNewInstanceProducer }, - installation: { - produce: { VaultFactory: produceVaultInstallation }, - }, instance: { consume: { auctioneer: auctioneerInstanceP }, }, }, - { options }, + { options: { VaultFactoryBundle: vaultBundleRef, contractGovernorBundle } }, ) => { - const { vaultsRef } = options; const kit = await vaultFactoryKit; const { instance: directorInstance } = kit; const allBrands = await E(zoe).getBrands(directorInstance); @@ -47,17 +48,6 @@ export const upgradeVaults = async ( await priceAuthority8400; - const bundleID = vaultsRef.bundleID; - console.log(`upgradeVaults: bundleId`, bundleID); - /** - * @type {Promise< - * Installation - * >} - */ - const installationP = E(zoe).installBundleID(bundleID); - produceVaultInstallation.reset(); - produceVaultInstallation.resolve(installationP); - const [auctionOldInstance, auctionNewInstance] = await Promise.all([ auctioneerInstanceP, auctionUpgradeNewInstance, @@ -149,7 +139,7 @@ export const upgradeVaults = async ( }); const upgradeResult = await E(kit.adminFacet).upgradeContract( - bundleID, + vaultBundleRef.bundleID, newPrivateArgs, ); @@ -162,8 +152,9 @@ export const upgradeVaults = async ( trace('restarting governor'); const ecf = await electorateCreatorFacet; - // restart vaultFactory governor - await E(kit.governorAdminFacet).restartContract( + // upgrade vaultFactory governor. Won't be needed next time: see #10063 + await E(kit.governorAdminFacet).upgradeContract( + contractGovernorBundle.bundleID, harden({ electorateCreatorFacet: ecf, governed: vaultFactoryPrivateArgs, @@ -177,30 +168,37 @@ const uV = 'upgradeVaults'; /** * Return the manifest, installations, and options for upgrading Vaults. * - * @param {object} _ign + * @param {object} utils + * @param {any} utils.restoreRef * @param {any} vaultUpgradeOptions */ export const getManifestForUpgradeVaults = async ( - _ign, - vaultUpgradeOptions, -) => ({ - manifest: { - [upgradeVaults.name]: { - consume: { - priceAuthority8400: uV, - auctionUpgradeNewInstance: uV, - chainTimerService: uV, - economicCommitteeCreatorFacet: uV, - reserveKit: uV, - vaultFactoryKit: uV, - zoe: uV, - }, - produce: { auctionUpgradeNewInstance: uV }, - installation: { - produce: { VaultFactory: true }, + { restoreRef }, + { VaultFactoryRef, contractGovernorRef }, +) => { + return { + manifest: { + [upgradeVaults.name]: { + consume: { + priceAuthority8400: uV, + auctionUpgradeNewInstance: uV, + chainTimerService: uV, + economicCommitteeCreatorFacet: uV, + reserveKit: uV, + vaultFactoryKit: uV, + zoe: uV, + }, + produce: { auctionUpgradeNewInstance: uV }, + instance: { consume: { auctioneer: uV } }, }, - instance: { consume: { auctioneer: true } }, }, - }, - options: { ...vaultUpgradeOptions }, -}); + installations: { + VaultFactory: restoreRef(VaultFactoryRef), + contractGovernor: restoreRef(contractGovernorRef), + }, + options: { + VaultFactoryBundle: VaultFactoryRef, + contractGovernorBundle: contractGovernorRef, + }, + }; +};