diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 3c6f9098ddb..31dab462f0f 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -39,6 +39,7 @@ module.exports = { './packages/*/tsconfig.json', './packages/*/tsconfig.json', './packages/wallet/*/tsconfig.json', + './a3p-integration/proposals/*/tsconfig.json', './tsconfig.json', ], tsconfigRootDir: __dirname, diff --git a/a3p-integration/.gitignore b/a3p-integration/.gitignore index 24b7a18f8bb..2027ea67059 100644 --- a/a3p-integration/.gitignore +++ b/a3p-integration/.gitignore @@ -2,6 +2,7 @@ Dockerfile docker-bake.* upgrade-test-scripts +*submission/ # Yarn (https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored) .pnp.* @@ -14,8 +15,8 @@ upgrade-test-scripts # same for each proposal, an independent project proposals/*/.pnp.* proposals/*/.yarn/* -proposals/*/!.yarn/patches -proposals/*/!.yarn/plugins -proposals/*/!.yarn/releases -proposals/*/!.yarn/sdks -proposals/*/!.yarn/versions +!proposals/*/.yarn/patches +!proposals/*/.yarn/plugins +!proposals/*/.yarn/releases +!proposals/*/.yarn/sdks +!proposals/*/.yarn/versions diff --git a/a3p-integration/proposals/a:upgrade-next/package.json b/a3p-integration/proposals/a:upgrade-next/package.json index 50991879ead..28a9ca2ad9f 100644 --- a/a3p-integration/proposals/a:upgrade-next/package.json +++ b/a3p-integration/proposals/a:upgrade-next/package.json @@ -17,6 +17,7 @@ "ava": { "concurrency": 1, "serial": true, + "timeout": "1m", "files": [ "!submission" ] diff --git a/a3p-integration/proposals/a:upgrade-next/probeZcfBundleCap.test.js b/a3p-integration/proposals/a:upgrade-next/probeZcfBundleCap.test.js new file mode 100644 index 00000000000..88ecf5d0929 --- /dev/null +++ b/a3p-integration/proposals/a:upgrade-next/probeZcfBundleCap.test.js @@ -0,0 +1,36 @@ +import test from 'ava'; + +import { + evalBundles, + getIncarnation, + getVatDetails, +} from '@agoric/synthetic-chain'; + +const SUBMISSION_DIR = 'probe-submission'; + +test('upgrade Zoe to verify ZcfBundleCap endures', async t => { + await null; + t.assert((await getIncarnation('zoe')) === 1, 'zoe incarnation must be one'); + + // Before the test, the Wallet Factory should be using the legacy ZCF + const detailsBefore = await getVatDetails('walletFactory'); + t.true(detailsBefore.incarnation >= 2, 'wf incarnation must be >= 2'); + + await evalBundles(SUBMISSION_DIR); + + const detailsAfter = await getVatDetails('walletFactory'); + t.is( + detailsAfter.incarnation, + detailsBefore.incarnation + 2, + 'wf incarnation must increase by 2', + ); + + // The test restarts the WalletFactory, so it'll use the recently assigned + // ZCF bundle. It then restarts Zoe, so it'll revert to whichever ZCF bundle + // made it to persistent store. We then restart the Wallet Factory and see if + // it's gone back to the ZCF that Zoe initially knew about. If we could get the + // ZCF bundleID here from the probe, we'd explicitly check for that. Instead, + // we have to be content that it did indeed use the newly assigned bundle in + // manual tests. + t.not(detailsAfter.bundleID, detailsBefore.bundleID); +}); diff --git a/a3p-integration/proposals/a:upgrade-next/test.sh b/a3p-integration/proposals/a:upgrade-next/test.sh index dd653d58e64..a2e9eec7c6a 100755 --- a/a3p-integration/proposals/a:upgrade-next/test.sh +++ b/a3p-integration/proposals/a:upgrade-next/test.sh @@ -3,4 +3,6 @@ # Place here any test that should be executed using the executed proposal. # The effects of this step are not persisted in further proposal layers. -yarn ava +yarn ava post.test.js +GLOBIGNORE=post.test.js +yarn ava *.test.js diff --git a/packages/builders/scripts/vats/probe-zcf-bundle.js b/packages/builders/scripts/vats/probe-zcf-bundle.js new file mode 100644 index 00000000000..9fdf0c69d23 --- /dev/null +++ b/packages/builders/scripts/vats/probe-zcf-bundle.js @@ -0,0 +1,22 @@ +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/probeZcfBundle.js', + getManifestCall: [ + 'getManifestForProbeZcfBundleCap', + { + zoeRef: publishRef(install('@agoric/vats/src/vat-zoe.js')), + zcfRef: publishRef(install('@agoric/zoe/src/contractFacet/vatRoot.js')), + walletRef: publishRef( + install('@agoric/smart-wallet/src/walletFactory.js'), + ), + }, + ], + }); + +export default async (homeP, endowments) => { + const { writeCoreProposal } = await makeHelpers(homeP, endowments); + await writeCoreProposal('probeZcfBundle', defaultProposalBuilder); +}; diff --git a/packages/vats/src/proposals/probeZcfBundle.js b/packages/vats/src/proposals/probeZcfBundle.js new file mode 100644 index 00000000000..ee44affb2c6 --- /dev/null +++ b/packages/vats/src/proposals/probeZcfBundle.js @@ -0,0 +1,72 @@ +import { E } from '@endo/far'; +import { makeStorageNodeChild } from '@agoric/internal/src/lib-chainStorage.js'; + +// verify that Zoe remembers the zcfBundleCap across upgrades +export const probeZcfBundleCap = async ( + { + consume: { + vatAdminSvc, + walletFactoryStartResult, + provisionPoolStartResult, + chainStorage, + walletBridgeManager: walletBridgeManagerP, + vatStore, + }, + }, + options, +) => { + const { zoeRef, zcfRef, walletRef } = options.options; + + const { adminNode, root: zoeRoot } = await E(vatStore).get('zoe'); + const zoeConfigFacet = await E(zoeRoot).getZoeConfigFacet(); + + // STEP 1: restart WF; it'll use the newest ZCF known to Zoe //////////////// + + const WALLET_STORAGE_PATH_SEGMENT = 'wallet'; + const [walletBridgeManager, walletStorageNode, ppFacets] = await Promise.all([ + walletBridgeManagerP, + makeStorageNodeChild(chainStorage, WALLET_STORAGE_PATH_SEGMENT), + provisionPoolStartResult, + ]); + const walletReviver = await E(ppFacets.creatorFacet).getWalletReviver(); + const privateArgs = { + storageNode: walletStorageNode, + walletBridgeManager, + walletReviver, + }; + + const { adminFacet: walletAdminFacet } = await walletFactoryStartResult; + await E(walletAdminFacet).upgradeContract(walletRef.bundleID, privateArgs); + + // STEP 2: Set the ZCF bundle //////////////////////// + await E(zoeConfigFacet).updateZcfBundleId(zcfRef.bundleID); + + // STEP 3: Upgrade Zoe again //////////////////////// + // ////// See if Zoe forgets ZcfBundleCap ////////// + + const zoeBundleCap = await E(vatAdminSvc).getBundleCap(zoeRef.bundleID); + await E(adminNode).upgrade(zoeBundleCap, {}); + + // STEP 4: restart WF //////////////////////// + await E(walletAdminFacet).restartContract(privateArgs); + + // ////// See which zcf bundle was used ////////// +}; +harden(probeZcfBundleCap); + +export const getManifestForProbeZcfBundleCap = (_powers, options) => ({ + manifest: { + [probeZcfBundleCap.name]: { + consume: { + vatAdminSvc: true, + vatStore: true, + walletBridgeManager: true, + walletFactoryStartResult: true, + provisionPoolStartResult: true, + chainStorage: true, + }, + }, + }, + options, +}); +harden(getManifestForProbeZcfBundleCap); diff --git a/packages/zoe/src/contractFacet/zcfZygote.js b/packages/zoe/src/contractFacet/zcfZygote.js index ed15761a015..b4f3d1643e6 100644 --- a/packages/zoe/src/contractFacet/zcfZygote.js +++ b/packages/zoe/src/contractFacet/zcfZygote.js @@ -469,8 +469,12 @@ export const makeZCFZygote = async ( await null; if (!zcfBaggage.has('repairedContractCompletionWatcher')) { - await E(zoeInstanceAdmin).repairContractCompletionWatcher(); - console.log(`Repaired contract completion watcher`); + // We don't wait because it's a cross-vat call (to Zoe) that can't be + // completed during this vat's start-up + E(zoeInstanceAdmin) + .repairContractCompletionWatcher() + .catch(() => {}); + zcfBaggage.init('repairedContractCompletionWatcher', true); } diff --git a/packages/zoe/src/zoeService/startInstance.js b/packages/zoe/src/zoeService/startInstance.js index d3eb173abde..6ec668115ff 100644 --- a/packages/zoe/src/zoeService/startInstance.js +++ b/packages/zoe/src/zoeService/startInstance.js @@ -266,6 +266,7 @@ export const makeStartInstance = ( contractBundleCap: newContractBundleCap, privateArgs: newPrivateArgs, }; + state.contractBundleCap = newContractBundleCap; return E.when(getFreshZcfBundleCap(), bCap => E(state.adminNode).upgrade(bCap, { vatParameters }), ); diff --git a/scripts/generate-a3p-submission.sh b/scripts/generate-a3p-submission.sh index ad4b8f5aabb..1abfd40e732 100755 --- a/scripts/generate-a3p-submission.sh +++ b/scripts/generate-a3p-submission.sh @@ -8,13 +8,17 @@ cd "$sdkroot" buildSubmission() { proposalName=$1 a3pProposal=$2 + output=${3:-$proposalName} + submissionName=${4:-submission} yarn agoric run "packages/builders/scripts/vats/$proposalName.js" - submissionDir="a3p-integration/proposals/$a3pProposal/submission" + + submissionDir="a3p-integration/proposals/$a3pProposal/$submissionName" mkdir -p "$submissionDir" - cp $(grep -oh '/.*b1-.*.json' "$proposalName"*) "$submissionDir" - mv "$proposalName"* "$submissionDir" + cp $(grep -oh '/.*b1-.*.json' "$output"*) "$submissionDir" + mv "$output"* "$submissionDir" } +buildSubmission probe-zcf-bundle "a:upgrade-next" probeZcfBundle probe-submission buildSubmission test-localchain "b:localchain"