Skip to content

Commit

Permalink
Durable ZCF (#5635)
Browse files Browse the repository at this point in the history
* refactor: make zcf durable

* chore: minor updates for tests and comments

* chore: more minor updates and integration

* refactor: rename MakeNoEscrowSeat to MakeNoEscrowSeatKit

* test: disable loopback in paramGovernance test.

* test: repair bootstrap test for new way to start contracts

* chore: cleanups from review

drop zcfSeat notifier
rename !didStart to firstTime

* chore: drop default to undefined

* refactor: simplify upgradeable contract interface to start/vivify

* fix: suggestions for #5770

* fix: added TODOs

* chore: minor cleanups: types, names, TODOs, etc.

* chore: clean up backquoted strings

* refactor: switch assertDeposit() back to depositPayout()

* chore: review clean-ups

* chore: types fixes

* refactor(zcfMint): default zcfSeat for mintGains

* chore: contract includes "upgraded" in strings conditionally

* chore: more reviews: comments, asserts, and strings

* chore: drop atomicity TODOs. discussed and resolved.

* chore: minor clean-ups

touching to re-re-trigger CI

Co-authored-by: Mark S. Miller <[email protected]>
Co-authored-by: Turadg Aleahmad <[email protected]>
  • Loading branch information
3 people authored Jul 19, 2022
1 parent 44c34cb commit 17ff31d
Show file tree
Hide file tree
Showing 46 changed files with 1,334 additions and 445 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {

import { AssetKind, makeDurableIssuerKit, vivifyIssuerKit } from '../../../src';

function vivifyErtpService(baggage, exitVatWithFailure) {
export const vivifyErtpService = (baggage, exitVatWithFailure) => {
const issuerBaggageSet = provideDurableSetStore(baggage, 'BaggageSet');
const ertpService = vivifySingleton(baggage, 'ERTPService', {
makeIssuerKit: (
Expand Down Expand Up @@ -38,7 +38,8 @@ function vivifyErtpService(baggage, exitVatWithFailure) {
}

return ertpService;
}
};
harden(vivifyErtpService);

export const buildRootObject = async (vatPowers, _vatParams, baggage) => {
const ertpService = vivifyErtpService(baggage, vatPowers.exitVatWithFailure);
Expand Down
2 changes: 1 addition & 1 deletion packages/SwingSet/src/supervisors/supervisor-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function makeSupervisorDispatch(dispatch) {
() => harden(['ok', null, null]),
err => {
// TODO react more thoughtfully, maybe terminate the vat
console.log(`error ${err} during vat dispatch() of ${delivery}`);
console.log(`error [${err}] during vat dispatch() of ${delivery}`);
return harden(['error', `${err}`, null]);
},
);
Expand Down
4 changes: 1 addition & 3 deletions packages/governance/test/unitTests/test-paramGovernance.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import bundleSource from '@endo/bundle-source';
import buildManualTimer from '@agoric/zoe/tools/manualTimer.js';
import { makeFakeVatAdmin } from '@agoric/zoe/tools/fakeVatAdmin.js';
import { E } from '@endo/eventual-send';
import { makeLoopback } from '@endo/captp';

import { resolve as importMetaResolve } from 'import-meta-resolve';
import { MALLEABLE_NUMBER } from '../swingsetTests/contractGovernor/governedContract.js';
Expand All @@ -34,7 +33,7 @@ const voteCounterBundleP = makeBundle(voteCounterRoot);
const governedBundleP = makeBundle(governedRoot);

const setUpZoeForTest = async setJig => {
const { makeFar } = makeLoopback('zoeTest');
const makeFar = o => o;

/**
* These properties will be assigned by `setJig` in the contract.
Expand All @@ -44,7 +43,6 @@ const setUpZoeForTest = async setJig => {
* @property {IssuerRecord} runIssuerRecord
* @property {IssuerRecord} govIssuerRecord
*/

const { zoeService, feeMintAccess: nonFarFeeMintAccess } = makeZoeKit(
makeFakeVatAdmin(setJig, o => makeFar(o)).admin,
);
Expand Down
42 changes: 37 additions & 5 deletions packages/vat-data/src/kind-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { provide } from '@agoric/store';
import { defineDurableKind, makeKindHandle } from './vat-data-bindings.js';

/** @template L,R @typedef {import('@endo/eventual-send').RemotableBrand<L, R>} RemotableBrand */
/** @typedef {import('@agoric/store').MapStore<string,unknown>} Baggage */
/** @template T @typedef {import('./types.js').DefineKindOptions<T>} DefineKindOptions */
/** @template T @typedef {import('./types.js').KindFacet<T>} KindFacet */
/** @typedef {import('./types.js').DurableKindHandle} DurableKindHandle */

const { entries, fromEntries } = Object;

Expand All @@ -12,6 +16,11 @@ export const dropContext =
// @ts-expect-error TODO statically recognize harden
harden(dropContext);

/**
* @param {Baggage} baggage
* @param {string} kindName
* @returns {DurableKindHandle}
*/
export const provideKindHandle = (baggage, kindName) =>
provide(baggage, `${kindName}_kindHandle`, () => makeKindHandle(kindName));
// @ts-expect-error TODO statically recognize harden
Expand Down Expand Up @@ -62,14 +71,37 @@ export const objectMap = (original, mapFn) => {
return /** @type {Record<K, U>} */ (harden(fromEntries(mapEnts)));
};

/** @typedef {import('@agoric/store').MapStore<string,unknown>} Baggage */
/**
* @template P,S,B
* @param {Baggage} baggage
* @param {string} kindName
* @param {(...args: P[]) => S} init
* @param {B} behavior
* @param {DefineKindOptions<unknown>} [options]
* @returns {(...args: P[]) => KindFacet<B>}
*/
export const vivifyKind = (
baggage,
kindName,
init,
behavior,
options = undefined,
) =>
defineDurableKind(
provideKindHandle(baggage, kindName),
init,
behavior,
options,
);
// @ts-expect-error TODO statically recognize harden
harden(vivifyKind);

/**
* @template T
* @param {Baggage} baggage
* @param {string} kindName
* @param {T} methods
* @param {import('./types.js').DefineKindOptions<unknown>} [options]
* @param {DefineKindOptions<unknown>} [options]
* @returns {T & RemotableBrand<{}, T>}
*/
export const vivifySingleton = (
Expand All @@ -78,10 +110,10 @@ export const vivifySingleton = (
methods,
options = undefined,
) => {
const kindHandle = provideKindHandle(baggage, kindName);
const behavior = objectMap(methods, dropContext);
const makeSingleton = defineDurableKind(
kindHandle,
const makeSingleton = vivifyKind(
baggage,
kindName,
() => ({}),
behavior,
options,
Expand Down
1 change: 0 additions & 1 deletion packages/vat-data/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ export type DurableKindHandle = DurableKindHandleClass;
type DefineKindOptions<C> = {
finish?: (context: C) => void;
durable?: boolean;
fakeDurable?: boolean;
};

export type VatData = {
Expand Down
7 changes: 6 additions & 1 deletion packages/vats/src/core/basic-behaviors.js
Original file line number Diff line number Diff line change
Expand Up @@ -281,9 +281,14 @@ export const installBootContracts = async ({
mintHolder,
walletFactory,
})) {
// This really wants to be E(vatAdminSvc).getBundleIDByName, but it's
// good enough to do D(vatAdmin).getBundleIDByName
const bundleCap = D(vatAdmin).getNamedBundleCap(name);

const bundle = D(bundleCap).getBundle();
producer.resolve(E(zoe).install(bundle));
// TODO (#4374) this should be E(zoe).installBundleID(bundleID);
const installation = E(zoe).install(bundle);
producer.resolve(installation);
}
};

Expand Down
9 changes: 6 additions & 3 deletions packages/vats/test/test-boot.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,19 +108,22 @@ const testRole = (ROLE, governanceActions) => {
fakeBundleCaps.set(bundleCap, name);
return bundleCap;
};
const createVat = bundleCap => {
const createVat = (bundleCap, options) => {
const name = fakeBundleCaps.get(bundleCap);
assert(name);
switch (name) {
case 'zcf':
return fakeVatAdmin.createVat(zcfBundleCap);
return fakeVatAdmin.createVat(zcfBundleCap, options);
default: {
const buildRoot = vatRoots[name];
if (!buildRoot) {
throw Error(`TODO: load vat ${name}`);
}
const vatParameters = {};
const vatParameters = { ...options?.vatParameters };
if (name === 'zoe') {
// basic-behaviors.js:buildZoe() provides hard-coded zcf BundleName
// and vat-zoe.js ignores vatParameters, but this would be the
// preferred way to pass the name.
vatParameters.zcfBundleName = 'zcf';
}
return { root: buildRoot({}, vatParameters), admin: {} };
Expand Down
5 changes: 3 additions & 2 deletions packages/wallet/api/test/attestationExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import '@agoric/zoe/exported.js';
import { AmountMath } from '@agoric/ertp';
import { E } from '@endo/eventual-send';
import { Far } from '@endo/marshal';

/** @param {ZCF} zcf */
export const start = async zcf => {
Expand Down Expand Up @@ -53,12 +54,12 @@ export const start = async zcf => {

const getCallHistory = () => callHistory;

const publicFacet = {
const publicFacet = Far('attestation public facet', {
mintAttestation,
makeWantAttInvitation,
makeReturnAttInvitation,
getCallHistory,
};
});

return harden({ publicFacet });
};
5 changes: 3 additions & 2 deletions packages/zoe/src/contractFacet/allocationMath.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// @ts-check
import { assert, details as X } from '@agoric/assert';
import { AmountMath } from '@agoric/ertp';

const { details: X } = assert;

/**
* @callback Operation
*
Expand Down Expand Up @@ -37,7 +38,7 @@ const doOperation = (allocation, amountKeywordRecord, operationFn) => {
})
.filter(([_keyword, result]) => result !== undefined);

return Object.fromEntries(entries);
return harden(Object.fromEntries(entries));
};

/** @type {Operation} */
Expand Down
2 changes: 1 addition & 1 deletion packages/zoe/src/contractFacet/internal-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@

/**
* @typedef ZCFZygote
* @property {(bundleOrBundleCap: SourceBundle | BundleCap) => void} evaluateContract
* @property {(instanceAdminFromZoe: ERef<ZoeInstanceAdmin>,
* instanceRecordFromZoe: InstanceRecord,
* issuerStorageFromZoe: IssuerRecords,
* privateArgs?: object,
* ) => Promise<ExecuteContractResult>} startContract
* @property {(privateArgs?: object) => void} restartContract
*/
49 changes: 41 additions & 8 deletions packages/zoe/src/contractFacet/offerHandlerStorage.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,56 @@

import { makeWeakStore } from '@agoric/store';
import { ToFarFunction } from '@endo/marshal';
import { canBeDurable, provideDurableWeakMapStore } from '@agoric/vat-data';

import { makeHandle } from '../makeHandle.js';
import { defineDurableHandle } from '../makeHandle.js';

export const makeOfferHandlerStorage = () => {
export const makeOfferHandlerStorage = zcfBaggage => {
const makeInvitationHandle = defineDurableHandle(zcfBaggage, 'Invitation');
/** @type {WeakStore<InvitationHandle, OfferHandler>} */
const invitationHandleToHandler = makeWeakStore('invitationHandle');

// ZCF needs to ephemerally hold on to ephemeral handlers, and durably hold
// onto handlers that are intended to be durable. We keep two stores and store
// each handler in the right one. When retrieving, we look for them in both.
// If Zoe restarts, the ephemeral ones will be lost, and the durable ones will
// survive.
const invitationHandleToEphemeralHandler = makeWeakStore(
'invitationHandleToEphemeralHandler',
);
/** @type {WeakStore<InvitationHandle, OfferHandler>} */
const invitationHandleToDurableHandler = provideDurableWeakMapStore(
zcfBaggage,
'invitationHandleToDurableHandler',
);

/** @type {(offerHandler: OfferHandler) => InvitationHandle} */
const storeOfferHandler = offerHandler => {
const farOfferHandler = ToFarFunction('offerHandler', offerHandler);
const invitationHandle = makeHandle('Invitation');
invitationHandleToHandler.init(invitationHandle, farOfferHandler);
if (typeof offerHandler === 'function') {
offerHandler = ToFarFunction('offerHandler', offerHandler);
}
const invitationHandleToHandler = canBeDurable(offerHandler)
? invitationHandleToDurableHandler
: invitationHandleToEphemeralHandler;

const invitationHandle = makeInvitationHandle();
invitationHandleToHandler.init(invitationHandle, offerHandler);
return invitationHandle;
};

/** @type {(invitationHandle: InvitationHandle) => OfferHandler} */
const takeOfferHandler = invitationHandle => {
/**
* @type {(invitationHandle: InvitationHandle, details?: Details) => OfferHandler}
*/
const takeOfferHandler = (
invitationHandle,
details = 'offerHandler may not have survived upgrade',
) => {
let invitationHandleToHandler;
if (invitationHandleToDurableHandler.has(invitationHandle)) {
invitationHandleToHandler = invitationHandleToDurableHandler;
} else {
assert(invitationHandleToEphemeralHandler.has(invitationHandle), details);
invitationHandleToHandler = invitationHandleToEphemeralHandler;
}
const offerHandler = invitationHandleToHandler.get(invitationHandle);
invitationHandleToHandler.delete(invitationHandle);
return offerHandler;
Expand Down
16 changes: 9 additions & 7 deletions packages/zoe/src/contractFacet/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
*/

/**
* @template {object} [OR=any] OR is OfferResult
* @template {object} [OR=unknown] OR is OfferResult
* @callback MakeInvitation
*
* Make a credible Zoe invitation for a particular smart contract
Expand Down Expand Up @@ -188,7 +188,7 @@
* @typedef {object} ZCFSeat
* @property {() => void} exit
* @property {ZCFSeatFail} fail
* @property {() => Notifier<Allocation>} getNotifier
* @property {() => Promise<Notifier<Allocation>>} getNotifier
* @property {() => boolean} hasExited
* @property {() => ProposalRecord} getProposal
* @property {ZCFGetAmountAllocated} getAmountAllocated
Expand All @@ -206,11 +206,13 @@
*/

/**
* @template {object} [OR=any]
* @callback OfferHandler
* @param {ZCFSeat} seat
* @param {object=} offerArgs
* @returns {OR}
* @template {object} OR Offer results
* @typedef {(seat: ZCFSeat, offerArgs?: object) => OR} HandleOffer
*/

/**
* @template {object} [OR=unknown] Offer results
* @typedef {HandleOffer<OR> | { handle: HandleOffer<OR> }} OfferHandler
*/

/**
Expand Down
Loading

0 comments on commit 17ff31d

Please sign in to comment.