diff --git a/a3p-integration/proposals/z:acceptance/.gitignore b/a3p-integration/proposals/z:acceptance/.gitignore index 3d143254692..7f5da265d56 100644 --- a/a3p-integration/proposals/z:acceptance/.gitignore +++ b/a3p-integration/proposals/z:acceptance/.gitignore @@ -2,3 +2,4 @@ restart-valueVow start-valueVow localchaintest-submission +recorded-instances-submission diff --git a/a3p-integration/proposals/z:acceptance/package.json b/a3p-integration/proposals/z:acceptance/package.json index 8a906d701b8..fd4b89562ae 100644 --- a/a3p-integration/proposals/z:acceptance/package.json +++ b/a3p-integration/proposals/z:acceptance/package.json @@ -3,6 +3,7 @@ "type": "/agoric.swingset.CoreEvalProposal", "sdk-generate": [ "testing/start-valueVow.js start-valueVow", + "testing/recorded-retired-instances.js recorded-instances-submission", "vats/test-localchain.js localchaintest-submission", "testing/restart-valueVow.js restart-valueVow" ] diff --git a/a3p-integration/proposals/z:acceptance/recorded-retired.test.js b/a3p-integration/proposals/z:acceptance/recorded-retired.test.js new file mode 100644 index 00000000000..a5a00d01107 --- /dev/null +++ b/a3p-integration/proposals/z:acceptance/recorded-retired.test.js @@ -0,0 +1,11 @@ +import test from 'ava'; + +import { evalBundles } from '@agoric/synthetic-chain'; + +const SUBMISSION_DIR = 'recorded-instances-submission'; + +test(`recorded instances in u18`, async t => { + const result = await evalBundles(SUBMISSION_DIR); + console.log('recorded retired instance result:', result); + t.pass('checked names'); +}); diff --git a/a3p-integration/proposals/z:acceptance/test.sh b/a3p-integration/proposals/z:acceptance/test.sh index a029b8d6e8a..e45cced45d1 100755 --- a/a3p-integration/proposals/z:acceptance/test.sh +++ b/a3p-integration/proposals/z:acceptance/test.sh @@ -12,6 +12,9 @@ yarn ava core-eval.test.js scripts/test-vaults.ts +echo ACCEPTANCE TESTING recorded instances +yarn ava recorded-retired.test.js + echo ACCEPTANCE TESTING kread yarn ava kread.test.js diff --git a/packages/boot/test/bootstrapTests/price-feed-replace.test.ts b/packages/boot/test/bootstrapTests/price-feed-replace.test.ts index c9a6178f0fd..d2cd27af919 100644 --- a/packages/boot/test/bootstrapTests/price-feed-replace.test.ts +++ b/packages/boot/test/bootstrapTests/price-feed-replace.test.ts @@ -105,7 +105,7 @@ test.serial('setupVaults; run updatePriceFeeds proposals', async t => { t.log('building all relevant CoreEvals'); const coreEvals = await Promise.all([ - buildProposal(priceFeedBuilder, ['MAINNET']), + buildProposal(priceFeedBuilder, ['BOOT_TEST']), buildProposal('@agoric/builders/scripts/vats/upgradeVaults.js'), buildProposal('@agoric/builders/scripts/vats/add-auction.js'), ]); diff --git a/packages/boot/test/bootstrapTests/updateGovernedParams.test.ts b/packages/boot/test/bootstrapTests/updateGovernedParams.test.ts index 5410fcc59d7..07c4e711289 100644 --- a/packages/boot/test/bootstrapTests/updateGovernedParams.test.ts +++ b/packages/boot/test/bootstrapTests/updateGovernedParams.test.ts @@ -134,7 +134,7 @@ test('modify manager & director params; update vats, check', async t => { const priceFeedBuilder = '@agoric/builders/scripts/inter-protocol/updatePriceFeeds.js'; const coreEvals = await Promise.all([ - buildProposal(priceFeedBuilder, ['MAINNET']), + buildProposal(priceFeedBuilder, ['BOOT_TEST']), buildProposal('@agoric/builders/scripts/vats/upgradeVaults.js'), buildProposal('@agoric/builders/scripts/vats/add-auction.js'), ]); diff --git a/packages/builders/scripts/inter-protocol/updatePriceFeeds.js b/packages/builders/scripts/inter-protocol/updatePriceFeeds.js index f5e71a6c137..f99db1caa59 100644 --- a/packages/builders/scripts/inter-protocol/updatePriceFeeds.js +++ b/packages/builders/scripts/inter-protocol/updatePriceFeeds.js @@ -41,6 +41,16 @@ const configurations = { ], inBrandNames: ['ATOM', 'stATOM', 'stOSMO', 'stTIA', 'stkATOM'], }, + BOOT_TEST: { + oracleAddresses: [ + 'agoric144rrhh4m09mh7aaffhm6xy223ym76gve2x7y78', // DSRV + 'agoric19d6gnr9fyp6hev4tlrg87zjrzsd5gzr5qlfq2p', // Stakin + 'agoric19uscwxdac6cf6z7d5e26e0jm0lgwstc47cpll8', // 01node + 'agoric1krunjcqfrf7la48zrvdfeeqtls5r00ep68mzkr', // Simply Staking + 'agoric1n4fcxsnkxe4gj6e24naec99hzmc4pjfdccy5nj', // P2P + ], + inBrandNames: ['ATOM'], + }, }; const { keys } = Object; diff --git a/packages/builders/scripts/testing/recorded-retired-instances.js b/packages/builders/scripts/testing/recorded-retired-instances.js new file mode 100644 index 00000000000..58bc79c969e --- /dev/null +++ b/packages/builders/scripts/testing/recorded-retired-instances.js @@ -0,0 +1,73 @@ +import { makeTracer } from '@agoric/internal'; +import { E } from '@endo/far'; + +const trace = makeTracer('RecordedRetired', true); + +/** + * @param {BootstrapPowers & + * PromiseSpaceOf<{ retiredContractInstances: MapStore; + * }> + * } powers + */ +export const testRecordedRetiredInstances = async ({ + consume: { + contractKits, + // governedContractKits, + retiredContractInstances: retiredContractInstancesP, + }, +}) => { + trace('Start'); + const retiredContractInstances = await retiredContractInstancesP; + + const auctionIDs = Array.from(retiredContractInstances.keys()).filter(k => + k.startsWith('auction'), + ); + assert(auctionIDs); + assert(auctionIDs.length === 1); + const auctionInstance = retiredContractInstances.get(auctionIDs[0]); + trace({ auctionInstance }); + // I don't know why it's neither in governedContractKits nor contractKits + // assert(await E(governedContractKits).get(auctionInstance)); + + const committeeIDs = Array.from(retiredContractInstances.keys()).filter(k => + k.startsWith('economicCommittee'), + ); + assert(committeeIDs); + assert(committeeIDs.length === 1); + const committeeInstance = retiredContractInstances.get(committeeIDs[0]); + assert(await E(contractKits).get(committeeInstance)); + + trace('done'); +}; +harden(testRecordedRetiredInstances); + +export const getManifestForRecordedRetiredInstances = () => { + return { + manifest: { + [testRecordedRetiredInstances.name]: { + consume: { + contractKits: true, + // governedContractKits: true, + retiredContractInstances: true, + }, + }, + }, + }; +}; + +/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').CoreEvalBuilder} */ +export const defaultProposalBuilder = async () => + harden({ + sourceSpec: + '@agoric/builders/scripts/testing/recorded-retired-instances.js', + getManifestCall: ['getManifestForRecordedRetiredInstances', {}], + }); + +/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').DeployScriptFunction} */ +export default async (homeP, endowments) => { + // import dynamically so the module can work in CoreEval environment + const dspModule = await import('@agoric/deploy-script-support'); + const { makeHelpers } = dspModule; + const { writeCoreEval } = await makeHelpers(homeP, endowments); + await writeCoreEval('recorded-retired', defaultProposalBuilder); +}; diff --git a/packages/inter-protocol/src/proposals/add-auction.js b/packages/inter-protocol/src/proposals/add-auction.js index 6814fbdcc87..76dd57a0c6b 100644 --- a/packages/inter-protocol/src/proposals/add-auction.js +++ b/packages/inter-protocol/src/proposals/add-auction.js @@ -1,8 +1,9 @@ import { deeplyFulfilledObject, makeTracer } from '@agoric/internal'; import { makeStorageNodeChild } from '@agoric/internal/src/lib-chainStorage.js'; -import { E } from '@endo/far'; import { Stable } from '@agoric/internal/src/tokens.js'; +import { E } from '@endo/far'; import { makeGovernedTerms as makeGovernedATerms } from '../auction/params.js'; +import { provideRetiredInstances } from './utils.js'; const trace = makeTracer('NewAuction', true); @@ -11,6 +12,7 @@ const trace = makeTracer('NewAuction', true); * auctionUpgradeNewInstance: Instance; * auctionUpgradeNewGovCreator: any; * newContractGovBundleId: string; + * retiredContractInstances: MapStore; * }>} interlockPowers */ @@ -35,6 +37,7 @@ export const addAuction = async ( economicCommitteeCreatorFacet: electorateCreatorFacet, governedContractKits: governedContractKitsP, priceAuthority8400, + retiredContractInstances: retiredContractInstancesP, zoe, }, produce: { @@ -42,6 +45,7 @@ export const addAuction = async ( auctionUpgradeNewInstance, auctionUpgradeNewGovCreator, newContractGovBundleId, + retiredContractInstances: produceRetiredInstances, }, instance: { consume: { reserve: reserveInstance }, @@ -79,6 +83,16 @@ export const addAuction = async ( auctioneerInstallationP, ]); + const retiredInstances = await provideRetiredInstances( + retiredContractInstancesP, + produceRetiredInstances, + ); + + // save the auctioneer instance so we can manage it later + const boardID = await E(board).getId(legacyKit.instance); + const identifier = `auctioneer-${boardID}`; + retiredInstances.init(identifier, legacyKit.instance); + // Each field has an extra layer of type + value: // AuctionStartDelay: { type: 'relativeTime', value: { relValue: 2n, timerBrand: Object [Alleged: timerBrand] {} } } /** @type {any} */ @@ -210,6 +224,7 @@ export const ADD_AUCTION_MANIFEST = harden({ economicCommitteeCreatorFacet: true, governedContractKits: true, priceAuthority8400: true, + retiredContractInstances: true, zoe: true, }, produce: { @@ -217,6 +232,7 @@ export const ADD_AUCTION_MANIFEST = harden({ auctionUpgradeNewInstance: true, auctionUpgradeNewGovCreator: true, newContractGovBundleId: true, + retiredContractInstances: true, }, instance: { consume: { reserve: true }, diff --git a/packages/inter-protocol/src/proposals/deploy-price-feeds.js b/packages/inter-protocol/src/proposals/deploy-price-feeds.js index f98f62223f2..0ecbea49098 100644 --- a/packages/inter-protocol/src/proposals/deploy-price-feeds.js +++ b/packages/inter-protocol/src/proposals/deploy-price-feeds.js @@ -5,6 +5,7 @@ import { E } from '@endo/far'; import { unitAmount } from '@agoric/zoe/src/contractSupport/priceQuote.js'; import { oracleBrandFeedName, + provideRetiredInstances, reserveThenDeposit, sanitizePathSegment, } from './utils.js'; @@ -84,7 +85,8 @@ export const ensureOracleBrand = async ( }; /** - * @param {EconomyBootstrapPowers} powers + * @param {EconomyBootstrapPowers & + * PromiseSpaceOf<{ retiredContractInstances: MapStore }>} powers * @param {{ * AGORIC_INSTANCE_NAME: string; * contractTerms: import('@agoric/inter-protocol/src/price/fluxAggregatorKit.js').ChainlinkConfig; @@ -96,6 +98,7 @@ export const ensureOracleBrand = async ( const startPriceAggregatorInstance = async ( { consume: { + agoricNames, board, chainStorage, chainTimerService, @@ -103,8 +106,10 @@ const startPriceAggregatorInstance = async ( highPrioritySendersManager, namesByAddressAdmin, startGovernedUpgradable, + retiredContractInstances: retiredContractInstancesP, }, instance: { produce: produceInstance }, + produce: { retiredContractInstances: produceRetiredInstances }, }, { AGORIC_INSTANCE_NAME, contractTerms, brandIn, brandOut }, installation, @@ -139,6 +144,22 @@ const startPriceAggregatorInstance = async ( // @ts-expect-error GovernableStartFn vs. fluxAggregatorContract.js start installation, }); + const retiredContractInstances = await provideRetiredInstances( + retiredContractInstancesP, + produceRetiredInstances, + ); + + // save the instance so we can manage it later + const retiringInstance = await E(agoricNames).lookup( + 'instance', + AGORIC_INSTANCE_NAME, + ); + const boardID = await E(board).getId(retiringInstance); + retiredContractInstances.init( + `priceFeed-${AGORIC_INSTANCE_NAME}-${boardID}`, + retiringInstance, + ); + produceInstance[AGORIC_INSTANCE_NAME].reset(); produceInstance[AGORIC_INSTANCE_NAME].resolve(governedKit.instance); trace( @@ -191,7 +212,9 @@ const distributeInvitations = async ( }; /** - * @param {EconomyBootstrapPowers & NamedVatPowers} powers + * @param {EconomyBootstrapPowers & + * NamedVatPowers & + * PromiseSpaceOf<{ retiredContractInstances: MapStore }>} powers * @param {{ * options: PriceFeedConfig & { * priceAggregatorRef: { bundleID: string }; @@ -300,6 +323,7 @@ export const getManifestForPriceFeeds = async ( namesByAddressAdmin: t, priceAuthority: t, priceAuthorityAdmin: t, + retiredContractInstances: t, startGovernedUpgradable: t, startUpgradable: t, zoe: t, @@ -307,9 +331,10 @@ export const getManifestForPriceFeeds = async ( installation: { produce: { priceAggregator: t } }, instance: { produce: t, + consume: t, }, oracleBrand: { produce: t }, - produce: { priceAuthority8400: t }, + produce: { priceAuthority8400: t, retiredContractInstances: t }, }, }, options: { ...priceFeedOptions }, diff --git a/packages/inter-protocol/src/proposals/replaceElectorate.js b/packages/inter-protocol/src/proposals/replaceElectorate.js index cb862be5a67..adde9adf747 100644 --- a/packages/inter-protocol/src/proposals/replaceElectorate.js +++ b/packages/inter-protocol/src/proposals/replaceElectorate.js @@ -15,7 +15,7 @@ import { assertPathSegment, makeStorageNodeChild, } from '@agoric/internal/src/lib-chainStorage.js'; -import { reserveThenDeposit } from './utils.js'; +import { provideRetiredInstances, reserveThenDeposit } from './utils.js'; /** @import {EconomyBootstrapPowers} from './econ-behaviors.js' */ /** @import {EconCharterStartResult} from './econ-behaviors.js' */ @@ -181,8 +181,10 @@ const inviteToEconCharter = async ( * Starts a new Economic Committee (EC) by creating an instance with the * provided committee specifications. * - * @param {EconomyBootstrapPowers} powers - The resources and capabilities - * required to start the committee. + * @param {EconomyBootstrapPowers & + * PromiseSpaceOf<{ retiredContractInstances: MapStore }>} powers + * - The resources and capabilities required to start the committee. + * * @param {{ * options: { * committeeName: string; @@ -196,12 +198,22 @@ const inviteToEconCharter = async ( */ const startNewEconomicCommittee = async ( { - consume: { board, chainStorage, startUpgradable }, - produce: { economicCommitteeKit, economicCommitteeCreatorFacet }, + consume: { + board, + chainStorage, + startUpgradable, + retiredContractInstances: retiredInstancesP, + }, + produce: { + economicCommitteeKit, + economicCommitteeCreatorFacet, + retiredContractInstances: produceRetiredInstances, + }, installation: { consume: { committee }, }, instance: { + consume: { economicCommittee: economicCommitteeOriginalP }, produce: { economicCommittee }, }, }, @@ -214,6 +226,19 @@ const startNewEconomicCommittee = async ( trace(`committeeName ${committeeName}`); trace(`committeeSize ${committeeSize}`); + const retiredInstances = await provideRetiredInstances( + retiredInstancesP, + produceRetiredInstances, + ); + + // Record the retired electorate instance so we can manage it later. + const economicCommitteeOriginal = await economicCommitteeOriginalP; + const boardID = await E(board).getId(economicCommitteeOriginal); + retiredInstances.init( + `economicCommittee-${boardID}`, + economicCommitteeOriginal, + ); + const committeesNode = await makeStorageNodeChild( chainStorage, COMMITTEES_ROOT, @@ -309,6 +334,7 @@ const startNewEconCharter = async ({ * @typedef {PromiseSpaceOf<{ * auctionUpgradeNewInstance: Instance; * auctionUpgradeNewGovCreator: any; + * retiredContractInstances: MapStore; * }>} interlockPowers */ @@ -485,6 +511,7 @@ export const getManifestForReplaceAllElectorates = async ( manifest: { [replaceAllElectorates.name]: { consume: { + agoricNames: true, auctionUpgradeNewGovCreator: true, auctionUpgradeNewInstance: true, psmKit: true, @@ -492,6 +519,7 @@ export const getManifestForReplaceAllElectorates = async ( chainStorage: true, highPrioritySendersManager: true, namesByAddressAdmin: true, + retiredContractInstances: true, // Rest of these are designed to be widely shared board: true, startUpgradable: true, @@ -501,6 +529,7 @@ export const getManifestForReplaceAllElectorates = async ( economicCommitteeKit: true, economicCommitteeCreatorFacet: true, auctionUpgradeNewGovCreator: true, + retiredContractInstances: true, }, installation: { consume: { @@ -514,6 +543,7 @@ export const getManifestForReplaceAllElectorates = async ( economicCommittee: true, econCommitteeCharter: true, }, + consume: { economicCommittee: true }, }, }, }, diff --git a/packages/inter-protocol/src/proposals/utils.js b/packages/inter-protocol/src/proposals/utils.js index 42894a27148..165731e4838 100644 --- a/packages/inter-protocol/src/proposals/utils.js +++ b/packages/inter-protocol/src/proposals/utils.js @@ -3,6 +3,7 @@ import { E } from '@endo/far'; import { WalletName } from '@agoric/internal'; import { getCopyMapEntries, makeCopyMap } from '@agoric/store'; import { assertPathSegment } from '@agoric/internal/src/lib-chainStorage.js'; +import { makeScalarBigMapStore } from '@agoric/vat-data'; /** @import {CopyMap} from '@endo/patterns'; */ @@ -171,3 +172,22 @@ export const sanitizePathSegment = name => { assertPathSegment(candidate); return candidate; }; + +/** + * Idempotently provide an empty MapStore for the `retiredContractInstances` + * value in promise space + * + * @param {Promise} consume + * @param {Producer} produce + * @returns {Promise} + */ +export const provideRetiredInstances = async (consume, produce) => { + // Promise space has no way to look for an existing value other than awaiting a promise, + // but it does allow extra production so it's safe to do this redundantly. + produce.resolve( + makeScalarBigMapStore('retiredContractInstances', { + durable: true, + }), + ); + return consume; +};