Skip to content

Commit

Permalink
Merge pull request #8802 from Agoric/8453-release2-Zoe
Browse files Browse the repository at this point in the history
8453 release2 zoe
  • Loading branch information
mhofman authored Feb 8, 2024
2 parents 0a0580c + 167b884 commit c2ea6db
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 16 deletions.
8 changes: 6 additions & 2 deletions packages/zoe/src/contractFacet/zcfZygote.js
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ export const makeZCFZygote = async (
instanceRecHolder = makeInstanceRecord(instanceRecordFromZoe);
instantiateIssuerStorage(issuerStorageFromZoe);
zcfBaggage.init('instanceRecHolder', instanceRecHolder);
zcfBaggage.init('repairedContractCompletionWatcher', true);

const startFn = start || prepare;
if (privateArgsShape) {
Expand Down Expand Up @@ -423,8 +424,11 @@ export const makeZCFZygote = async (
instanceRecHolder = zcfBaggage.get('instanceRecHolder');
initSeatMgrAndMintKind();

if (privateArgsShape) {
mustMatch(privateArgs, privateArgsShape, 'privateArgs');
await null;
if (!zcfBaggage.has('repairedContractCompletionWatcher')) {
await E(zoeInstanceAdmin).repairContractCompletionWatcher();
console.log(`Repaired contract completion watcher`);
zcfBaggage.init('repairedContractCompletionWatcher', true);
}

// restart an upgradeable contract
Expand Down
1 change: 1 addition & 0 deletions packages/zoe/src/internal-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@
* @property {(strings: Array<string>) => void} setOfferFilter
* @property {() => Array<string>} getOfferFilter
* @property {(seatHandle: SeatHandle) => Subscriber<AmountKeywordRecord>} getExitSubscriber
* @property {() => void} repairContractCompletionWatcher
*/

/**
Expand Down
1 change: 1 addition & 0 deletions packages/zoe/src/typeGuards.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ export const InstanceAdminI = M.interface('InstanceAdmin', {
getOfferFilter: M.call().returns(M.arrayOf(M.string())),
getExitSubscriber: M.call(SeatShape).returns(SubscriberShape),
isBlocked: M.call(M.string()).returns(M.boolean()),
repairContractCompletionWatcher: M.call().returns(),
});

export const InstanceStorageManagerIKit = harden({
Expand Down
63 changes: 52 additions & 11 deletions packages/zoe/src/zoeService/startInstance.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@ import {
makeScalarBigMapStore,
provideDurableWeakMapStore,
prepareExoClass,
prepareExo,
watchPromise,
} from '@agoric/vat-data';
import { initEmpty } from '@agoric/store';
import { isUpgradeDisconnection } from '@agoric/internal/src/upgrade-api.js';

import { defineDurableHandle } from '../makeHandle.js';
import { makeInstanceAdminMaker } from './instanceAdminStorage.js';
import { AdminFacetI, InstanceAdminI } from '../typeGuards.js';
import {
AdminFacetI,
InstanceAdminI,
InstanceAdminShape,
} from '../typeGuards.js';

/** @typedef {import('@agoric/vat-data').Baggage} Baggage */
/** @typedef { import('@agoric/swingset-vat').BundleCap} BundleCap */
Expand Down Expand Up @@ -46,10 +53,46 @@ export const makeStartInstance = (
const InstanceAdminStateShape = harden({
instanceStorage: M.remotable('ZoeInstanceStorageManager'),
instanceAdmin: M.remotable('InstanceAdmin'),
seatHandleToSeatAdmin: M.remotable(),
seatHandleToSeatAdmin: M.remotable(), // seatHandleToSeatAdmin, but putting that string here is backwards-incompatible
adminNode: M.remotable('adminNode'),
});

/** @type {{ onFulfilled: (completion: any, instanceAdmin: InstanceAdmin) => void, onRejected: (reason: any, instanceAdmin: InstanceAdmin, adminNode: any) => void }} */
const watcher = prepareExo(
zoeBaggage,
'InstanceCompletionWatcher',
M.interface('InstanceCompletionWatcher', {
onFulfilled: M.call(
M.any(),
InstanceAdminShape,
M.remotable('adminNode'),
).returns(),
onRejected: M.call(
M.any(),
InstanceAdminShape,
M.remotable('adminNode'),
).returns(),
}),
{
onFulfilled: (completion, instanceAdmin) =>
instanceAdmin.exitAllSeats(completion),
onRejected: (/** @type {Error} */ reason, instanceAdmin, adminNode) => {
if (isUpgradeDisconnection(reason)) {
console.log(`resetting promise watcher after upgrade`, reason);
// eslint-disable-next-line no-use-before-define
watchForAdminNodeDone(adminNode, instanceAdmin);
} else {
instanceAdmin.failAllSeats(reason);
}
},
},
);

const watchForAdminNodeDone = (adminNode, instAdmin) => {
// @ts-expect-error XXX unknown type?
watchPromise(E(adminNode).done(), watcher, instAdmin, adminNode);
};

const makeZoeInstanceAdmin = prepareExoClass(
zoeBaggage,
'zoeInstanceAdmin',
Expand Down Expand Up @@ -124,10 +167,10 @@ export const makeStartInstance = (
replaceAllocations(seatHandleAllocations) {
const { state } = this;
try {
seatHandleAllocations.forEach(({ seatHandle, allocation }) => {
for (const { seatHandle, allocation } of seatHandleAllocations) {
const zoeSeatAdmin = state.seatHandleToSeatAdmin.get(seatHandle);
zoeSeatAdmin.replaceAllocation(allocation);
});
}
} catch (err) {
// nothing for Zoe to do if the termination fails
void E(state.adminNode).terminateWithFailure(err);
Expand All @@ -154,6 +197,10 @@ export const makeStartInstance = (
const { state } = this;
return state.instanceAdmin.isBlocked(string);
},
repairContractCompletionWatcher() {
const { state, self } = this;
void watchForAdminNodeDone(state.adminNode, self);
},
},
{
stateShape: InstanceAdminStateShape,
Expand Down Expand Up @@ -274,13 +321,7 @@ export const makeStartInstance = (
);
zoeInstanceStorageManager.initInstanceAdmin(instanceHandle, instanceAdmin);

E.when(
E(adminNode).done(),
completion => {
instanceAdmin.exitAllSeats(completion);
},
reason => instanceAdmin.failAllSeats(reason),
);
void watchForAdminNodeDone(adminNode, instanceAdmin);

/** @type {ZoeInstanceAdmin} */
const zoeInstanceAdminForZcf = makeZoeInstanceAdmin(
Expand Down
25 changes: 22 additions & 3 deletions packages/zoe/test/swingsetTests/zoe/test-zoe-upgrade.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import '@agoric/swingset-vat/tools/prepare-test-env.js';
import test from 'ava';

import bundleSource from '@endo/bundle-source';
import { buildVatController } from '@agoric/swingset-vat';
import { kunser } from '@agoric/swingset-vat/src/lib/kmarshal.js';

Expand Down Expand Up @@ -52,6 +54,18 @@ test('zoe vat upgrade trauma', async t => {
const messageObject = (presence, methodName, args) =>
run('messageVatObject', [{ presence, methodName, args }]);

const restartVatAdminVat = async controller => {
const vaBundle = await bundleSource(
new URL(
'../../../../SwingSet/src/vats/vat-admin/vat-vat-admin.js',
import.meta.url,
).pathname,
);
const bundleID = await controller.validateAndInstallBundle(vaBundle);
controller.upgradeStaticVat('vatAdmin', true, bundleID, {});
await controller.run();
};

/**
* @see {@link ../upgradeCoveredCall/bootstrap-coveredCall-service-upgrade.js}
*/
Expand Down Expand Up @@ -206,16 +220,21 @@ test('zoe vat upgrade trauma', async t => {
pausedFlows.push({ result, remainingSteps: flow.slice(i) });
}

// Null-upgrade vatAdmin.
await restartVatAdminVat(c);

// Null-upgrade Zoe.
const { incarnationNumber } = await run('upgradeVat', [zoeVatConfig]);
t.is(incarnationNumber, 1, 'Zoe vat must be upgraded');
const { incarnationNumber: zoeIncarnationNumber } = await run('upgradeVat', [
zoeVatConfig,
]);
t.is(zoeIncarnationNumber, 1, 'Zoe vat must be upgraded');

// Verify a complete run in the new Zoe.
await doSteps('post-upgrade', flow);

// Verify completion of each paused flow.
for (const { result, remainingSteps } of pausedFlows) {
const [beforeStepName] = remainingSteps[0];
await doSteps(`resumed-${beforeStepName}`, flow, result);
await doSteps(`resumed-${beforeStepName}`, remainingSteps, result);
}
});
49 changes: 49 additions & 0 deletions packages/zoe/test/unitTests/zcf/test-zcf.js
Original file line number Diff line number Diff line change
Expand Up @@ -1270,6 +1270,45 @@ test('numWantsSatisfied: no', async t => {

await zcfSeat.exit();
t.is(await E(userSeat).numWantsSatisfied(), 0);

t.deepEqual(await E(E(userSeat).getExitSubscriber()).getUpdateSince(), {
updateCount: undefined,
value: undefined,
});
});

test('numWantsSatisfied: fail', async t => {
const { zcf } = await setupZCFTest();
const doubloonMint = await zcf.makeZCFMint('Doubloons');
const yenMint = await zcf.makeZCFMint('Yen');
const { brand: doubloonBrand } = doubloonMint.getIssuerRecord();
const { brand: yenBrand } = yenMint.getIssuerRecord();
const yenAmount = AmountMath.make(yenBrand, 100n);
const proposal = harden({
give: { DownPayment: yenAmount },
want: { Bonus: AmountMath.make(doubloonBrand, 1_000_000n) },
});

const { zcfSeat: mintSeat, userSeat: payoutSeat } = zcf.makeEmptySeatKit();
yenMint.mintGains(harden({ Cost: yenAmount }), mintSeat);
mintSeat.exit();
const payout = await E(payoutSeat).getPayout('Cost');
const payment = { DownPayment: payout };

const { zcfSeat, userSeat } = await makeOffer(
zcf.getZoeService(),
zcf,
proposal,
payment,
);

void zcfSeat.fail(Error('whatever'));
t.is(await E(userSeat).numWantsSatisfied(), 0);

await t.throwsAsync(
() => E(E(userSeat).getExitSubscriber()).getUpdateSince(),
{ message: 'whatever' },
);
});

test('numWantsSatisfied: yes', async t => {
Expand All @@ -1290,6 +1329,11 @@ test('numWantsSatisfied: yes', async t => {

await zcfSeat.exit();
t.is(await E(userSeat).numWantsSatisfied(), 1);

t.deepEqual(await E(E(userSeat).getExitSubscriber()).getUpdateSince(), {
updateCount: undefined,
value: undefined,
});
});

test('numWantsSatisfied as promise', async t => {
Expand All @@ -1314,4 +1358,9 @@ test('numWantsSatisfied as promise', async t => {

await zcfSeat.exit();
await outcome;

t.deepEqual(await E(E(userSeat).getExitSubscriber()).getUpdateSince(), {
updateCount: undefined,
value: undefined,
});
});

0 comments on commit c2ea6db

Please sign in to comment.