diff --git a/a3p-integration/proposals/a:upgrade-next/agd-tools.js b/a3p-integration/proposals/a:upgrade-next/agd-tools.js index 90cf71587abc..f9da630c7851 100644 --- a/a3p-integration/proposals/a:upgrade-next/agd-tools.js +++ b/a3p-integration/proposals/a:upgrade-next/agd-tools.js @@ -2,14 +2,14 @@ import { agd, agops, agopsLocation, + CHAINID, executeCommand, - VALIDATORADDR, executeOffer, GOV1ADDR, GOV2ADDR, GOV3ADDR, newOfferId, - CHAINID, + VALIDATORADDR, } from '@agoric/synthetic-chain'; const ORACLE_ADDRESSES = [GOV1ADDR, GOV2ADDR, GOV3ADDR]; @@ -137,3 +137,8 @@ export const bankSend = (addr, wanted) => { return agd.tx('bank', 'send', VALIDATORADDR, addr, wanted, ...noise); }; + +export const getProvisionPoolMetrics = async () => { + const path = `published.provisionPool.metrics`; + return getQuoteBody(path); +}; diff --git a/a3p-integration/proposals/a:upgrade-next/package.json b/a3p-integration/proposals/a:upgrade-next/package.json index d2a3aca4d901..169fc2c5604d 100644 --- a/a3p-integration/proposals/a:upgrade-next/package.json +++ b/a3p-integration/proposals/a:upgrade-next/package.json @@ -7,8 +7,10 @@ "coreProposals": [] }, "sdk-generate": [ - "vats/probe-zcf-bundle.js probe-submission", - "vats/test-localchain.js localchaintest-submission" + "vats/upgrade-bank.js upgrade-bank", + "vats/upgrade-provisionPool.js upgrade-provisionPool", + "vats/add-STARS.js add-STARS", + "vats/add-STARS2.js add-STARS2" ], "type": "Software Upgrade Proposal" }, diff --git a/a3p-integration/proposals/a:upgrade-next/provisionPool.test.js b/a3p-integration/proposals/a:upgrade-next/provisionPool.test.js new file mode 100644 index 000000000000..210f4f48aa68 --- /dev/null +++ b/a3p-integration/proposals/a:upgrade-next/provisionPool.test.js @@ -0,0 +1,101 @@ +// @ts-check + +import test from 'ava'; + +import { + evalBundles, + getIncarnation, + waitForBlock, +} from '@agoric/synthetic-chain'; + +import { bankSend, getProvisionPoolMetrics } from './agd-tools.js'; + +const NULL_UPGRADE_BANK_DIR = 'upgrade-bank'; +const UPGRADE_PP_DIR = 'upgrade-provisionPool'; +const ADD_STARS_DIR = 'add-STARS'; +const ADD_STARS2_DIR = 'add-STARS2'; + +const USDC_DENOM = + 'ibc/295548A78785A1007F232DE286149A6FF512F180AF5657780FC89C009E2C348F'; +const PROVISIONING_POOL_ADDR = 'agoric1megzytg65cyrgzs6fvzxgrcqvwwl7ugpt62346'; + +/** + * @file + * The problems to be addressed are that provisionPool won't correctly handle + * (#8722) deposit of assets after it (provisionPool) is upgraded, or (#8724) + * new asset kinds after vat-bank is upgraded. + * + * To test this, we will + * + * 1. See that we can add USDC. + * + * 2. Null upgrade vat-bank, and see that we can add a new collateal. + * + * 2a. if we had been able to null-upgrade provisionPool at this point, we + * wouldn't have been able to productively add USDC, but the null upgrade fails. + * + * 3. Do a full upgrade of provisionPool; then deposit USDC, and see IST + * incremented in totalMintedConverted. + * + * 4. Null upgrade vat-bank again, and then see (in logs) that adding a new + * asset type gives the ability to make deposits. + */ + +const contributeToPool = async (t, asset, expectedToGrow) => { + const metricsBefore = await getProvisionPoolMetrics(); + console.log('PPT pre', metricsBefore); + + await bankSend(PROVISIONING_POOL_ADDR, asset); + + const metricsAfter = await getProvisionPoolMetrics(); + console.log('PPT post', metricsAfter); + t.is( + metricsAfter.totalMintedConverted.brand, + metricsBefore.totalMintedConverted.brand, + 'brands match', + ); + if (expectedToGrow) { + // I couldn't import AmountMath. dunno why. + t.true( + metricsAfter.totalMintedConverted.value > + metricsBefore.totalMintedConverted.value, + 'brands match', + ); + } else { + t.equal( + metricsAfter.totalMintedConverted.value, + metricsBefore.totalMintedConverted.value, + ); + } +}; + +test.serial('add assets before', async t => { + await contributeToPool(t, `10000${USDC_DENOM}`, true); +}); + +test.serial(`upgrade Bank`, async t => { + await evalBundles(NULL_UPGRADE_BANK_DIR); + + const incarnation = await getIncarnation('bank'); + t.is(incarnation, 1); + + await evalBundles(ADD_STARS_DIR); +}); + +test.serial('full upgrade ProvisionPool', async t => { + await evalBundles(UPGRADE_PP_DIR); + const ppIncarnation = await getIncarnation('db93f-provisionPool'); + t.is(ppIncarnation, 1); + + await contributeToPool(t, `30000${USDC_DENOM}`, true); +}); + +test.serial(`Add assets after bank upgrade`, async t => { + await evalBundles(NULL_UPGRADE_BANK_DIR); + await waitForBlock(2); + + const incarnation = await getIncarnation('bank'); + t.is(incarnation, 2); + + await evalBundles(ADD_STARS2_DIR); +}); diff --git a/packages/builders/scripts/vats/add-STARS.js b/packages/builders/scripts/vats/add-STARS.js new file mode 100644 index 000000000000..4cd72bb6d7f3 --- /dev/null +++ b/packages/builders/scripts/vats/add-STARS.js @@ -0,0 +1,22 @@ +import { makeHelpers } from '@agoric/deploy-script-support'; +import { defaultProposalBuilder as vaultProposalBuilder } from '../inter-protocol/add-collateral-core.js'; + +/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').ProposalBuilder} */ +export const starsVaultProposalBuilder = async powers => { + return vaultProposalBuilder(powers, { + interchainAssetOptions: { + // Values for the Stargaze token on Osmosis + denom: + 'ibc/987C17B11ABC2B20019178ACE62929FE9840202CE79498E29FE8E5CB02B7C0A4', + decimalPlaces: 6, + keyword: 'STARS', + oracleBrand: 'STARS', + proposedName: 'STARS', + }, + }); +}; + +export default async (homeP, endowments) => { + const { writeCoreProposal } = await makeHelpers(homeP, endowments); + await writeCoreProposal('add-STARS-collateral', starsVaultProposalBuilder); +}; diff --git a/packages/builders/scripts/vats/add-STARS2.js b/packages/builders/scripts/vats/add-STARS2.js new file mode 100644 index 000000000000..ccf55b148569 --- /dev/null +++ b/packages/builders/scripts/vats/add-STARS2.js @@ -0,0 +1,22 @@ +import { makeHelpers } from '@agoric/deploy-script-support'; +import { defaultProposalBuilder as vaultProposalBuilder } from '../inter-protocol/add-collateral-core.js'; + +/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').ProposalBuilder} */ +export const stars2VaultProposalBuilder = async powers => { + return vaultProposalBuilder(powers, { + interchainAssetOptions: { + // Values for the Stargaze token on Osmosis - modified to have suffix B's + denom: + 'ibc/987C17B11ABC2B20019178ACE62929FE9840202CE79498E29FE8E5CB02BBBBBB', + decimalPlaces: 6, + keyword: 'STARS2', + oracleBrand: 'STARS2', + proposedName: 'STARS2', + }, + }); +}; + +export default async (homeP, endowments) => { + const { writeCoreProposal } = await makeHelpers(homeP, endowments); + await writeCoreProposal('add-STARS2-collateral', stars2VaultProposalBuilder); +}; diff --git a/packages/builders/scripts/vats/upgrade-bank.js b/packages/builders/scripts/vats/upgrade-bank.js new file mode 100644 index 000000000000..c0f79b442ced --- /dev/null +++ b/packages/builders/scripts/vats/upgrade-bank.js @@ -0,0 +1,18 @@ +import { makeHelpers } from '@agoric/deploy-script-support'; + +/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').ProposalBuilder} */ +export const defaultProposalBuilder = async ({ publishRef, install }) => + harden({ + sourceSpec: '@agoric/vats/src/proposals/upgrade-bank-proposal.js', + getManifestCall: [ + 'getManifestForUpgradingBank', + { + bankRef: publishRef(install('@agoric/vats/src/vat-bank.js')), + }, + ], + }); + +export default async (homeP, endowments) => { + const { writeCoreProposal } = await makeHelpers(homeP, endowments); + await writeCoreProposal('upgrade-bank', defaultProposalBuilder); +}; diff --git a/packages/builders/scripts/vats/upgrade-provisionPool.js b/packages/builders/scripts/vats/upgrade-provisionPool.js new file mode 100644 index 000000000000..8ba5045ca826 --- /dev/null +++ b/packages/builders/scripts/vats/upgrade-provisionPool.js @@ -0,0 +1,20 @@ +import { makeHelpers } from '@agoric/deploy-script-support'; + +/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').ProposalBuilder} */ +export const defaultProposalBuilder = async ({ publishRef, install }) => + harden({ + sourceSpec: '@agoric/vats/src/proposals/upgrade-provisionPool-proposal.js', + getManifestCall: [ + 'getManifestForUpgradingProvisionPool', + { + provisionPoolRef: publishRef( + install('@agoric/inter-protocol/src/provisionPool.js'), + ), + }, + ], + }); + +export default async (homeP, endowments) => { + const { writeCoreProposal } = await makeHelpers(homeP, endowments); + await writeCoreProposal('upgrade-provision-pool', defaultProposalBuilder); +}; diff --git a/packages/vats/src/proposals/upgrade-bank-proposal.js b/packages/vats/src/proposals/upgrade-bank-proposal.js new file mode 100644 index 000000000000..46467475c9e4 --- /dev/null +++ b/packages/vats/src/proposals/upgrade-bank-proposal.js @@ -0,0 +1,42 @@ +import { E } from '@endo/far'; + +/** + * @param {BootstrapPowers & { + * consume: { + * vatAdminSvc: VatAdminSvc; + * vatStore: MapStore< + * string, + * import('@agoric/swingset-vat').CreateVatResults + * >; + * }; + * }} powers + * @param {object} options + * @param {{ bankRef: VatSourceRef }} options.options + */ +export const upgradeBank = async ( + { consume: { vatAdminSvc, vatStore } }, + options, +) => { + const { bankRef } = options.options; + + assert(bankRef.bundleID); + const bankBundleCap = await E(vatAdminSvc).getBundleCap(bankRef.bundleID); + console.log(`BANK BUNDLE ID: `, bankRef.bundleID); + + const { adminNode } = await E(vatStore).get('bank'); + + await E(adminNode).upgrade(bankBundleCap, {}); +}; + +export const getManifestForUpgradingBank = (_powers, { bankRef }) => ({ + manifest: { + [upgradeBank.name]: { + consume: { + vatAdminSvc: 'vatAdminSvc', + vatStore: 'vatStore', + }, + produce: {}, + }, + }, + options: { bankRef }, +}); diff --git a/packages/vats/src/proposals/upgrade-provisionPool-proposal.js b/packages/vats/src/proposals/upgrade-provisionPool-proposal.js new file mode 100644 index 000000000000..766a590de2ef --- /dev/null +++ b/packages/vats/src/proposals/upgrade-provisionPool-proposal.js @@ -0,0 +1,68 @@ +import { E } from '@endo/far'; +import { deeplyFulfilled } from '@endo/marshal'; + +/** + * @param {BootstrapPowers & { + * consume: { + * economicCommitteeCreatorFacet: any; + * }; + * }} powers + * @param {object} options + * @param {{ provisionPoolRef: VatSourceRef }} options.options + */ +export const upgradeProvisionPool = async ( + { + consume: { + economicCommitteeCreatorFacet: electorateCreatorFacet, + instancePrivateArgs: instancePrivateArgsP, + provisionPoolStartResult: provisionPoolStartResultP, + }, + }, + options, +) => { + const { provisionPoolRef } = options.options; + + assert(provisionPoolRef.bundleID); + console.log(`PROVISION POOL BUNDLE ID: `, provisionPoolRef.bundleID); + + const [provisionPoolStartResult, instancePrivateArgs] = await Promise.all([ + provisionPoolStartResultP, + instancePrivateArgsP, + ]); + const { adminFacet, instance } = provisionPoolStartResult; + // const privateArgs = await deeplyFulfilled(instancePrivateArgs.get(instance)); + + const [originalPrivateArgs, poserInvitation] = await Promise.all([ + deeplyFulfilled(instancePrivateArgs.get(instance)), + E(electorateCreatorFacet).getPoserInvitation(), + ]); + + const newPrivateArgs = harden({ + ...originalPrivateArgs, + initialPoserInvitation: poserInvitation, + }); + + const upgradeResult = await E(adminFacet).upgradeContract( + provisionPoolRef.bundleID, + newPrivateArgs, + ); + + console.log('ProvisionPool upgraded: ', upgradeResult); +}; + +export const getManifestForUpgradingProvisionPool = ( + _powers, + { provisionPoolRef }, +) => ({ + manifest: { + [upgradeProvisionPool.name]: { + consume: { + economicCommitteeCreatorFacet: true, + instancePrivateArgs: true, + provisionPoolStartResult: true, + }, + produce: {}, + }, + }, + options: { provisionPoolRef }, +});