Skip to content

Commit

Permalink
test: a test framework for verifying upgrade of Zoe and ZCF
Browse files Browse the repository at this point in the history
It starts from the currently installed version of Zoe and ZCF. First
it verifies that reallocation via staging and via the helper work and
that the version internal to ZCF is not present. It then upgrades Zoe
and ZCF as necessary to introduce the new behavior. Finally, it
re-runs the initial verification to show that the ZCF internal version
works.

This currently fails on the first step since it runs in a state in
which the new Zoe/ZCF code has already replaced the old.
  • Loading branch information
Chris-Hibbert committed Jul 19, 2023
1 parent 6f740a4 commit 1e6d52e
Show file tree
Hide file tree
Showing 10 changed files with 510 additions and 47 deletions.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/vats/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"build:boot-viz-sim-gov": "node src/authorityViz.js --sim-chain --gov >docs/boot-sim-gov.dot && dot -Tsvg docs/boot-sim-gov.dot >docs/boot-sim-gov.dot.svg",
"build:restart-vats-proposal": "agoric run scripts/restart-vats.js",
"build:add-STARS-proposal": "agoric run scripts/add-STARS.js",
"build:zcf-proposal": "agoric run scripts/replace-zoe.js",
"prepack": "tsc --build jsconfig.build.json",
"postpack": "git clean -f '*.d.ts*'",
"test": "ava",
Expand Down
25 changes: 25 additions & 0 deletions packages/vats/scripts/replace-zoe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { makeHelpers } from '@agoric/deploy-script-support';

/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').ProposalBuilder} */
export const defaultProposalBuilder = async () =>
harden({
sourceSpec: '../src/proposals/zcf-proposal.js',
getManifestCall: [
'getManifestForZoe',
{
zcfRef: {
bundleID:
'b1-8674abc9a8de561c4a33fb475b87be75708cd901c37931fd5ac1f40d3ee99937a459a6ca7b4a8b7907512626caf98c125f22c15384826e37dfc899dc0bf2a63a',
},
zoeRef: {
bundleID:
'b1-68963663488ee6d178293b559b9d902cea1857dddac257f08540cb9748647d0218a991ce02cf9f61e1e49cca3979b20473103a7fee509cf808de43e323afab54',
},
},
],
});

export default async (homeP, endowments) => {
const { writeCoreProposal } = await makeHelpers(homeP, endowments);
await writeCoreProposal('replace-zcf', defaultProposalBuilder);
};
44 changes: 44 additions & 0 deletions packages/vats/src/proposals/zcf-proposal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { E } from '@endo/far';

/**
* @param { BootstrapPowers & {
* consume: {
* vatAdminSvc: VatAdminSve,
* vatStore: MapStore<string, CreateVatResults>,
* }
* }} powers
*
* @param {object} options
* @param {{zoeRef: VatSourceRef, zcfRef: VatSourceRef}} options.options
*/
export const upgradeZcf = async (
{ consume: { vatAdminSvc, vatStore } },
options,
) => {
const { zoeRef, zcfRef } = options.options;

const zoeBundleCap = await E(vatAdminSvc).getBundleCap(zoeRef.bundleID);

const { adminNode, root: zoeRoot } = await E(vatStore).get('zoe');

await E(adminNode).upgrade(zoeBundleCap, {});

const zoeConfigFacet = await E(zoeRoot).getZoeConfigFacet();
await E(zoeConfigFacet).updateZcfBundleId(zcfRef.bundleID);
};

export const getManifestForZoe = (_powers, { zoeRef, zcfRef }) => ({
manifest: {
[upgradeZcf.name]: {
consume: {
vatAdminSvc: 'vatAdminSvc',
vatStore: 'vatStore',
},
produce: {},
},
},
options: {
zoeRef,
zcfRef,
},
});
81 changes: 81 additions & 0 deletions packages/vats/test/bootstrapTests/drivers.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { SECONDS_PER_MINUTE } from '@agoric/inter-protocol/src/proposals/econ-be
import { unmarshalFromVstorage } from '@agoric/internal/src/marshal.js';
import { slotToRemotable } from '@agoric/internal/src/storage-test-utils.js';
import { instanceNameFor } from '@agoric/inter-protocol/src/proposals/price-feed-proposal.js';

import { boardSlottingMarshaller } from '../../tools/board-utils.js';

/**
Expand Down Expand Up @@ -340,3 +341,83 @@ export const makeGovernanceDriver = async (
},
};
};

/**
* @param {import('./supports.js').SwingsetTestKit} testKit
*/
export const makeZoeDriver = async testKit => {
const { EV } = testKit.runUtils;
const zoe = await EV.vat('bootstrap').consumeItem('zoe');
let creatorFacet;
let adminFacet;
let brand;
const sub = (a, v) => {
return { brand: a.brand, value: a.value - v };
};

return {
async instantiateProbeContract(probeContractBundle) {
const installation = await EV(zoe).install(probeContractBundle);
const startResults = await EV(zoe).startInstance(installation);
({ creatorFacet, adminFacet } = startResults);

const issuers = await EV(zoe).getIssuers(startResults.instance);
const brands = await EV(zoe).getBrands(startResults.instance);
brand = brands.Ducats;
return { creatorFacet, issuer: issuers.Ducats, brand };
},
async upgradeProbe(probeContractBundle) {
const fabricateBundleId = bundle => {
return `b1-${bundle.endoZipBase64Sha512}`;
};

await EV(adminFacet).upgradeContract(
fabricateBundleId(probeContractBundle),
);
},

verifyRealloc() {
const alloc = EV(creatorFacet).getAllocation();
return alloc;
},
async probeReallocation(value, payment) {
const stagingInv = await EV(creatorFacet).makeProbeStagingInvitation();

const stagingSeat = await EV(zoe).offer(
stagingInv,
{ give: { Ducats: value } },
{ Ducats: payment },
);
const helperPayments = await EV(stagingSeat).getPayouts();

const helperInv = await EV(creatorFacet).makeProbeHelperInvitation();
const helperSeat = await EV(zoe).offer(
helperInv,
{ give: { Ducats: sub(value, 1n) } },
{ Ducats: helperPayments.Ducats },
);
const internalPayments = await EV(helperSeat).getPayouts();

const internalInv = await EV(creatorFacet).makeProbeInternalInvitation();
const internalSeat = await EV(zoe).offer(
internalInv,
{ give: { Ducats: sub(value, 2n) } },
{ Ducats: internalPayments.Ducats },
);
const leftoverPayments = await EV(internalSeat).getPayouts();

return {
stagingResult: await EV(stagingSeat).getOfferResult(),
helperResult: await EV(helperSeat).getOfferResult(),
internalResult: await EV(internalSeat).getOfferResult(),
leftoverPayments,
};
},
async faucet() {
const faucetInv = await EV(creatorFacet).makeFaucetInvitation();
const seat = await EV(zoe).offer(faucetInv);

return EV(seat).getPayout('Ducats');
},
};
};
102 changes: 102 additions & 0 deletions packages/vats/test/bootstrapTests/supports.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// @ts-check

/* global process */
import * as fsAmbient from 'fs';

import { resolve as importMetaResolve } from 'import-meta-resolve';
import { basename } from 'path';
import { inspect } from 'util';
Expand All @@ -17,14 +19,22 @@ import { loadSwingsetConfigFile } from '@agoric/swingset-vat';
import { E } from '@endo/eventual-send';
import { makeQueue } from '@endo/stream';
import { TimeMath } from '@agoric/time';
import { eventLoopIteration } from '@agoric/internal/src/testing-utils.js';

import * as processAmbient from 'child_process';
import {
boardSlottingMarshaller,
makeAgoricNamesRemotesFromFakeStorage,
slotToBoardRemote,
} from '../../tools/board-utils.js';
import { makeWalletFactoryDriver, makeZoeDriver } from './drivers.js';

// to retain for ESlint, used by typedef
E;

// main/production config doesn't have initialPrice, upon which 'open vaults' depends
const PLATFORM_CONFIG = '@agoric/vats/decentral-itest-vaults-config.json';

const sink = () => {};

const trace = makeTracer('BSTSupport', false);
Expand Down Expand Up @@ -488,4 +498,96 @@ export const makeSwingsetTestKit = async (
timer,
};
};

/** @typedef {Awaited<ReturnType<typeof makeSwingsetTestKit>>} SwingsetTestKit */

export const makeTestContext = async t => {
console.time('DefaultTestContext');
/** @type {SwingsetTestKit} */
const swingsetTestKit = await makeSwingsetTestKit(t, 'bundles/vaults', {
configSpecifier: PLATFORM_CONFIG,
});

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');

await eventLoopIteration();

// has to be late enough for agoricNames data to have been published
const agoricNamesRemotes = makeAgoricNamesRemotesFromFakeStorage(
swingsetTestKit.storage,
);
agoricNamesRemotes.brand.ATOM || Fail`ATOM brand not yet defined`;
console.timeLog('DefaultTestContext', 'agoricNamesRemotes');

const walletFactoryDriver = await makeWalletFactoryDriver(
runUtils,
storage,
agoricNamesRemotes,
);
console.timeLog('DefaultTestContext', 'walletFactoryDriver');

console.timeEnd('DefaultTestContext');

const buildProposal = makeProposalExtractor({
childProcess: processAmbient,
fs: fsAmbient.promises,
});

return {
...swingsetTestKit,
agoricNamesRemotes,
walletFactoryDriver,
buildProposal,
};
};

export const makeZoeTestContext = async t => {
console.time('DefaultTestContext');
/** @type {SwingsetTestKit} */
const swingsetTestKit = await makeSwingsetTestKit(t, 'bundles/zoe', {
// configSpecifier: PLATFORM_CONFIG,
// configSpecifier: '@agoric/vats/decentral-core-config.json',
configSpecifier: '@agoric/vats/decentral-demo-config.json',
});

const { controller, runUtils } = swingsetTestKit;
console.timeLog('DefaultTestContext', 'swingsetTestKit');
const { EV } = runUtils;

await eventLoopIteration();

// We don't need vaults, but this gets the brand, which is checked somewhere
// 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,
);
console.timeLog('DefaultTestContext', 'agoricNamesRemotes');

const zoeDriver = await makeZoeDriver(swingsetTestKit);
console.timeLog('DefaultTestContext', 'walletFactoryDriver');

console.timeEnd('DefaultTestContext');

const buildProposal = makeProposalExtractor({
childProcess: processAmbient,
fs: fsAmbient.promises,
});

return {
...swingsetTestKit,
controller,
agoricNamesRemotes,
zoeDriver,
buildProposal,
};
};
49 changes: 2 additions & 47 deletions packages/vats/test/bootstrapTests/test-vats-restart.js
Original file line number Diff line number Diff line change
@@ -1,66 +1,21 @@
// @ts-check

/**
* @file Bootstrap test of restarting (almost) all vats
*/
import { test as anyTest } from '@agoric/zoe/tools/prepare-test-env-ava.js';

import { Fail } from '@agoric/assert';
import { Offers } from '@agoric/inter-protocol/src/clientSupport.js';
import { eventLoopIteration } from '@agoric/internal/src/testing-utils.js';
import { makeAgoricNamesRemotesFromFakeStorage } from '../../tools/board-utils.js';
import { makeWalletFactoryDriver } from './drivers.js';
import { makeSwingsetTestKit } from './supports.js';
import { makeTestContext } from './supports.js';

/**
* @type {import('ava').TestFn<Awaited<ReturnType<typeof makeTestContext>>>}
*/
const test = anyTest;

// main/production config doesn't have initialPrice, upon which 'open vaults' depends
const PLATFORM_CONFIG = '@agoric/vats/decentral-itest-vaults-config.json';

// presently all these tests use one collateral manager
const collateralBrandKey = 'ATOM';

const makeTestContext = async t => {
console.time('DefaultTestContext');
const swingsetTestKit = await makeSwingsetTestKit(t, 'bundles/vaults', {
configSpecifier: PLATFORM_CONFIG,
});

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');

await eventLoopIteration();

// has to be late enough for agoricNames data to have been published
const agoricNamesRemotes = makeAgoricNamesRemotesFromFakeStorage(
swingsetTestKit.storage,
);
agoricNamesRemotes.brand.ATOM || Fail`ATOM brand not yet defined`;
console.timeLog('DefaultTestContext', 'agoricNamesRemotes');

const walletFactoryDriver = await makeWalletFactoryDriver(
runUtils,
storage,
agoricNamesRemotes,
);
console.timeLog('DefaultTestContext', 'walletFactoryDriver');

console.timeEnd('DefaultTestContext');

return {
...swingsetTestKit,
agoricNamesRemotes,
walletFactoryDriver,
};
};

test.before(async t => {
t.context = await makeTestContext(t);
});
Expand Down
Loading

0 comments on commit 1e6d52e

Please sign in to comment.