UNPKG

@agoric/zoe

Version:

Zoe: the Smart Contract Framework for Offer Enforcement

299 lines (265 loc) • 9.21 kB
// @jessie-check /** * Zoe uses ERTP, the Electronic Rights Transfer Protocol * * A note about ERTP AssetKinds: Within Zoe, the assetKind of * validated amounts must be consistent with the brand's assetKind. * This is stricter than the validation provided by AmountMath * currently. When the brand has an assetKind itself, AmountMath will * validate that. */ /// <reference types="@agoric/internal/exported" /> /// <reference types="@agoric/notifier/exported.js" /> /// <reference path="../internal-types.js" /> import { Fail } from '@endo/errors'; import { E } from '@endo/eventual-send'; import { Far } from '@endo/marshal'; import { M } from '@endo/patterns'; import { makeScalarBigMapStore, prepareExo } from '@agoric/vat-data'; import { makeZoeStorageManager } from './zoeStorageManager.js'; import { makeStartInstance } from './startInstance.js'; import { makeOfferMethod } from './offer/offer.js'; import { makeInvitationQueryFns } from './invitationQueries.js'; import { getZcfBundleCap } from './createZCFVat.js'; import { defaultFeeIssuerConfig, prepareFeeMint } from './feeMint.js'; import { ZoeServiceI } from '../typeGuards.js'; /** * @import {VatAdminSvc, ShutdownWithFailure} from '@agoric/swingset-vat'; * @import {Baggage} from '@agoric/vat-data'; * @import {FeeIssuerConfig, FeeMintAccess, ZCFSpec, ZoeService} from './types.js'; */ /** * Create a durable instance of Zoe. * * @param {object} options * @param {Baggage} options.zoeBaggage - the baggage for Zoe durability. Must be provided by caller * @param {Promise<VatAdminSvc> | VatAdminSvc} [options.vatAdminSvc] - The vatAdmin Service, which carries the * power to create a new vat. If it's not available when makeZoe() is called, it * must be provided later using setVatAdminService(). * @param {ShutdownWithFailure} [options.shutdownZoeVat] - a function to * shutdown the Zoe Vat. This function needs to use the vatPowers * available to a vat. * @param {FeeIssuerConfig} [options.feeIssuerConfig] * @param {ZCFSpec} [options.zcfSpec] - Pointer to the contract facet bundle. */ const makeDurableZoeKit = ({ zoeBaggage, vatAdminSvc, shutdownZoeVat = () => {}, feeIssuerConfig = defaultFeeIssuerConfig, zcfSpec = { name: 'zcf' }, }) => { /** @type {BundleCap} */ let zcfBundleCap; const saveBundleCap = () => { void E.when( Promise.all([vatAdminSvc, getZcfBundleCap(zcfSpec, vatAdminSvc)]), ([vatAdminService, bundleCap]) => { zcfBundleCap = bundleCap; if (!zoeBaggage.has('vatAdminSvc')) { zoeBaggage.init('vatAdminSvc', vatAdminService); zoeBaggage.init('zcfBundleCap', zcfBundleCap); } else { zoeBaggage.set('vatAdminSvc', vatAdminService); zoeBaggage.set('zcfBundleCap', zcfBundleCap); } }, ); }; const setVatAdminService = Far('setVatAdminService', lateVatAdminSvc => { vatAdminSvc = lateVatAdminSvc; saveBundleCap(); }); if (vatAdminSvc) { saveBundleCap(); } else if (zoeBaggage.has('zcfBundleCap')) { zcfBundleCap = zoeBaggage.get('zcfBundleCap'); // in this case, it'll be known to have been resolved, but that's fine vatAdminSvc = zoeBaggage.get('vatAdminSvc'); } const feeMintKit = prepareFeeMint( zoeBaggage, feeIssuerConfig, shutdownZoeVat, ); // guarantee that vatAdminSvcP has been defined. const getActualVatAdminSvcP = () => { if (!vatAdminSvc) { throw Fail`createZCFVat did not get bundleCap`; } return vatAdminSvc; }; /** @type {GetBundleCapForID} */ const getBundleCapForID = bundleID => { return E(getActualVatAdminSvcP()).waitForBundleCap(bundleID); }; const getBundleCapByIdNow = id => { return E(getActualVatAdminSvcP()).getBundleCap(id); }; // This method contains the power to create a new ZCF Vat, and must // be closely held. vatAdminSvc is even more powerful - any vat can // be created. We severely restrict access to vatAdminSvc for this reason. const createZCFVat = (contractBundleCap, contractLabel) => { zcfBundleCap || Fail`createZCFVat did not get bundleCap`; const name = contractLabel && typeof contractLabel === 'string' && contractLabel.length > 0 ? `zcf-${contractLabel}` : 'zcf'; return E(getActualVatAdminSvcP()).createVat( zcfBundleCap, harden({ name, vatParameters: { contractBundleCap, zoeService, invitationIssuer: invitationIssuerAccess.getInvitationIssuer(), }, }), ); }; // The ZoeStorageManager composes and consolidates capabilities // needed by Zoe according to POLA. const { zoeServiceDataAccess: dataAccess, makeOfferAccess, startInstanceAccess, invitationIssuerAccess, } = makeZoeStorageManager( createZCFVat, getBundleCapForID, shutdownZoeVat, feeMintKit.feeMint, zoeBaggage, ); // Pass the capabilities necessary to create E(zoe).startInstance const startInstance = makeStartInstance( startInstanceAccess, () => zcfBundleCap, getBundleCapByIdNow, zoeBaggage, ); // Pass the capabilities necessary to create E(zoe).offer const offer = makeOfferMethod(makeOfferAccess); // Make the methods that allow users to easily and credibly get // information about their invitations. const invitationIssuer = invitationIssuerAccess.getInvitationIssuer(); const { getInstance, getInstallation, getInvitationDetails } = makeInvitationQueryFns(invitationIssuer); const getConfiguration = () => { return harden({ feeIssuerConfig, }); }; const ZoeConfigI = M.interface('ZoeConfigFacet', { updateZcfBundleId: M.call(M.string()).returns(), }); const zoeConfigFacet = prepareExo(zoeBaggage, 'ZoeConfigFacet', ZoeConfigI, { updateZcfBundleId(bundleId) { void E.when( getZcfBundleCap({ id: bundleId }, vatAdminSvc), bundleCap => { zcfBundleCap = bundleCap; zoeBaggage.set('zcfBundleCap', zcfBundleCap); }, e => { console.error('🚨 unable to update ZCF Bundle: ', e); throw e; }, ); }, }); const zoeService = prepareExo( zoeBaggage, 'ZoeService', ZoeServiceI, /** @type {ZoeService} */ ({ install(bundleId, bundleLabel) { return dataAccess.installBundle(bundleId, bundleLabel); }, installBundleID(bundleId, bundleLabel) { return dataAccess.installBundleID(bundleId, bundleLabel); }, startInstance, offer, // The functions below are getters only and have no impact on // state within Zoe getOfferFilter(instance) { return dataAccess.getOfferFilter(instance); }, async getInvitationIssuer() { return dataAccess.getInvitationIssuer(); }, async getFeeIssuer() { return feeMintKit.feeMint.getFeeIssuer(); }, getBrands(instance) { return dataAccess.getBrands(instance); }, getIssuers(instance) { return dataAccess.getIssuers(instance); }, getPublicFacet(instance) { return dataAccess.getPublicFacet(instance); }, getTerms(instance) { return dataAccess.getTerms(instance); }, getInstallationForInstance(instance) { return dataAccess.getInstallation(instance); }, getBundleIDFromInstallation(installation) { return dataAccess.getBundleIDFromInstallation(installation); }, getInstallation, getInstance(invitation) { return getInstance(invitation); }, getConfiguration, getInvitationDetails, getProposalShapeForInvitation(invitation) { return dataAccess.getProposalShapeForInvitation(invitation); }, }), ); return harden({ zoeService, zoeConfigFacet, /** @type {FeeMintAccess} */ // @ts-expect-error cast feeMintAccess: feeMintKit.feeMintAccess, setVatAdminService, }); }; /** * @deprecated * Create an instance of Zoe. * * This will fail upgrades and should only be used in tests through the helper * `makeZoeKitForTest`. * * @param {Promise<VatAdminSvc> | VatAdminSvc} [vatAdminSvc] - The vatAdmin Service, which carries the * power to create a new vat. If it's not available when makeZoe() is called, it * must be provided later using setVatAdminService(). * @param {ShutdownWithFailure} [shutdownZoeVat] - a function to * shutdown the Zoe Vat. This function needs to use the vatPowers * available to a vat. * @param {FeeIssuerConfig} [feeIssuerConfig] * @param {ZCFSpec} [zcfSpec] - Pointer to the contract facet bundle. */ const makeZoeKit = (vatAdminSvc, shutdownZoeVat, feeIssuerConfig, zcfSpec) => makeDurableZoeKit({ vatAdminSvc, shutdownZoeVat, feeIssuerConfig, zcfSpec, zoeBaggage: makeScalarBigMapStore('fake zoe baggage', { durable: true }), }); harden(makeZoeKit); export { makeDurableZoeKit, makeZoeKit }; /** * @typedef {ReturnType<typeof makeDurableZoeKit>} ZoeKit */ // eslint-disable-next-line import/export -- no named value exports; only types export * from '../types-index.js';