From b4727738568fffffc086220c241a979db77444ef Mon Sep 17 00:00:00 2001 From: Chris Hibbert Date: Thu, 22 Jun 2023 09:29:33 -0700 Subject: [PATCH] test: a test framework for verifying upgrade of Zoe and ZCF 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. Variants of this test will run on master without this PR to verify that upgrading to the new Zoe/ZCF works, and in deployment/upgrade-test to show that the upgrade to the new zoe & zcf will succeed. --- packages/vats/package.json | 1 + packages/vats/scripts/replace-zoe.js | 19 +++ packages/vats/src/proposals/zcf-proposal.js | 46 ++++++ packages/vats/test/bootstrapTests/drivers.js | 88 +++++++++++ packages/vats/test/bootstrapTests/supports.js | 107 +++++++++++++- .../test/bootstrapTests/test-vats-restart.js | 50 +------ .../test/bootstrapTests/test-zcf-upgrade.js | 107 ++++++++++++++ packages/vats/test/bootstrapTests/zcfProbe.js | 138 ++++++++++++++++++ 8 files changed, 505 insertions(+), 51 deletions(-) create mode 100644 packages/vats/scripts/replace-zoe.js create mode 100644 packages/vats/src/proposals/zcf-proposal.js create mode 100644 packages/vats/test/bootstrapTests/test-zcf-upgrade.js create mode 100644 packages/vats/test/bootstrapTests/zcfProbe.js diff --git a/packages/vats/package.json b/packages/vats/package.json index b38054b2d2b..38f233e962e 100644 --- a/packages/vats/package.json +++ b/packages/vats/package.json @@ -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", diff --git a/packages/vats/scripts/replace-zoe.js b/packages/vats/scripts/replace-zoe.js new file mode 100644 index 00000000000..00fc1878bc6 --- /dev/null +++ b/packages/vats/scripts/replace-zoe.js @@ -0,0 +1,19 @@ +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: '../src/proposals/zcf-proposal.js', + getManifestCall: [ + 'getManifestForZoe', + { + zoeRef: publishRef(install('../src/vat-zoe.js')), + zcfRef: publishRef(install('../../zoe/src/contractFacet/vatRoot.js')), + }, + ], + }); + +export default async (homeP, endowments) => { + const { writeCoreProposal } = await makeHelpers(homeP, endowments); + await writeCoreProposal('replace-zcf', defaultProposalBuilder); +}; diff --git a/packages/vats/src/proposals/zcf-proposal.js b/packages/vats/src/proposals/zcf-proposal.js new file mode 100644 index 00000000000..1111551fc16 --- /dev/null +++ b/packages/vats/src/proposals/zcf-proposal.js @@ -0,0 +1,46 @@ +import { E } from '@endo/far'; + +/** + * @param { BootstrapPowers & { + * consume: { + * vatAdminSvc: VatAdminSve, + * vatStore: MapStore, + * } + * }} 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); + console.log(`ZOE BUNDLE ID: `, 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); + console.log(`ZCF BUNDLE ID: `, zcfRef.bundleID); +}; + +export const getManifestForZoe = (_powers, { zoeRef, zcfRef }) => ({ + manifest: { + [upgradeZcf.name]: { + consume: { + vatAdminSvc: 'vatAdminSvc', + vatStore: 'vatStore', + }, + produce: {}, + }, + }, + options: { + zoeRef, + zcfRef, + }, +}); diff --git a/packages/vats/test/bootstrapTests/drivers.js b/packages/vats/test/bootstrapTests/drivers.js index d5ffe41bac6..748c6eb22dd 100644 --- a/packages/vats/test/bootstrapTests/drivers.js +++ b/packages/vats/test/bootstrapTests/drivers.js @@ -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'; /** @@ -337,3 +338,90 @@ 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'); + const chainStorage = await EV.vat('bootstrap').consumeItem('chainStorage'); + const storageNode = await EV(chainStorage).makeChildNode('prober-asid9a'); + 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, + undefined, + undefined, + { storageNode }, + 'probe', + ); + ({ 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() { + return EV(creatorFacet).getAllocation(); + }, + 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'); + }, + }; +}; diff --git a/packages/vats/test/bootstrapTests/supports.js b/packages/vats/test/bootstrapTests/supports.js index b01549585d5..ee9dc71df36 100644 --- a/packages/vats/test/bootstrapTests/supports.js +++ b/packages/vats/test/bootstrapTests/supports.js @@ -1,6 +1,7 @@ // @ts-check /* global process */ -import * as fsAmbient from 'fs'; + +import { promises as fsAmbientPromises } from 'fs'; import { resolve as importMetaResolve } from 'import-meta-resolve'; import { basename } from 'path'; import { inspect } from 'util'; @@ -17,14 +18,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); @@ -199,7 +208,7 @@ export const getNodeTestVaultsConfig = async ( config.defaultManagerType = 'local'; // speed up build (60s down to 10s in testing) config.bundleCachePath = bundleDir; - await fsAmbient.promises.mkdir(bundleDir, { recursive: true }); + await fsAmbientPromises.mkdir(bundleDir, { recursive: true }); if (config.coreProposals) { // remove Pegasus because it relies on IBC to Golang that isn't running @@ -209,7 +218,7 @@ export const getNodeTestVaultsConfig = async ( } const testConfigPath = `${bundleDir}/${basename(specifier)}`; - await fsAmbient.promises.writeFile( + await fsAmbientPromises.writeFile( testConfigPath, JSON.stringify(config), 'utf-8', @@ -434,7 +443,7 @@ export const makeSwingsetTestKit = async ( const buildProposal = makeProposalExtractor({ childProcess: childProcessAmbient, - fs: fsAmbient.promises, + fs: fsAmbientPromises, }); console.timeEnd('makeSwingsetTestKit'); @@ -493,4 +502,94 @@ export const makeSwingsetTestKit = async ( timer, }; }; + /** @typedef {Awaited>} 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: fsAmbientPromises, + }); + + return { + ...swingsetTestKit, + agoricNamesRemotes, + walletFactoryDriver, + buildProposal, + }; +}; + +export const makeZoeTestContext = async t => { + console.time('DefaultTestContext'); + /** @type {SwingsetTestKit} */ + const swingsetTestKit = await makeSwingsetTestKit(t, 'bundles/zoe', { + 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: fsAmbientPromises, + }); + + return { + ...swingsetTestKit, + controller, + agoricNamesRemotes, + zoeDriver, + buildProposal, + }; +}; diff --git a/packages/vats/test/bootstrapTests/test-vats-restart.js b/packages/vats/test/bootstrapTests/test-vats-restart.js index d575a041df0..93431478f9f 100644 --- a/packages/vats/test/bootstrapTests/test-vats-restart.js +++ b/packages/vats/test/bootstrapTests/test-vats-restart.js @@ -2,12 +2,10 @@ /** @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'; + +/** @file Bootstrap test of restarting (almost) all vats */ /** * @type {import('ava').TestFn< @@ -16,51 +14,9 @@ import { makeSwingsetTestKit } from './supports.js'; */ 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); }); diff --git a/packages/vats/test/bootstrapTests/test-zcf-upgrade.js b/packages/vats/test/bootstrapTests/test-zcf-upgrade.js new file mode 100644 index 00000000000..9ef41fd6930 --- /dev/null +++ b/packages/vats/test/bootstrapTests/test-zcf-upgrade.js @@ -0,0 +1,107 @@ +// @ts-check + +import { test as anyTest } from '@agoric/zoe/tools/prepare-test-env-ava.js'; +import bundleSource from '@endo/bundle-source'; + +import path from 'path'; +import { makeZoeTestContext } from './supports.js'; + +const filename = new URL(import.meta.url).pathname; +const dirname = path.dirname(filename); + +const ZCF_PROBE_SRC = './zcfProbe.js'; + +/** + * @file Bootstrap test of upgrading ZCF to support atomicRearrange internally. + * + * The goal is to tell Zoe about a new version of ZCF that it should use + * when starting new contracts. Zoe wasn't previously configurable for that, so + * a prerequisite was to upgrade Zoe to a version that could have its ZCF + * updated. To test that we install a contract that can detect the variation + * among zcf versions, and run it before, in the middle and after the upgrades. + * + * 0. add a contract that can report on the state of ZCF's support for + * different versions of reallocation: staging, helper, and internal. + * 1. put new Zoe & ZCF bundles on chain + * 2. upgrade Zoe; return a new facet that supports ZCF update + * 3. tell Zoe to use new ZCF + * 4. restart the new contract; verify that the behavior is unchanged. + * 5. null upgrade the contract; verify that zcf supports internal rearrange. + * 6. [optional] fully upgrade the contract; verify that it works + */ + +/** @type {import('ava').TestFn>>} */ +const test = anyTest; + +test.before(async t => { + t.context = await makeZoeTestContext(t); +}); +test.after.always(t => t.context.shutdown?.()); + +test('run restart-vats proposal', async t => { + const { controller, buildProposal, zoeDriver } = t.context; + const { EV } = t.context.runUtils; + + const buildAndExecuteProposal = async packageSpec => { + const proposal = await buildProposal(packageSpec); + + for await (const bundle of proposal.bundles) { + await controller.validateAndInstallBundle(bundle); + } + + t.log('installed', proposal.bundles.length, 'bundles'); + + t.log('launching proposal'); + const bridgeMessage = { + type: 'CORE_EVAL', + evals: proposal.evals, + }; + + t.log({ bridgeMessage }); + /** @type {ERef} */ + const coreEvalBridgeHandler = await EV.vat('bootstrap').consumeItem( + 'coreEvalBridgeHandler', + ); + await EV(coreEvalBridgeHandler).fromBridge(bridgeMessage); + }; + const source = `${dirname}/${ZCF_PROBE_SRC}`; + + const zcfProbeBundle = await bundleSource(source); + // uncomment and add `import fs from "fs";` to generate a bundle of the prober contract + // fs.writeFileSync('bundles/prober-contract-bundle.json', JSON.stringify(zcfProbeBundle)); + + const brandRecord = await zoeDriver.instantiateProbeContract(zcfProbeBundle); + const { brand, issuer } = brandRecord; + const ducatAmountRecord = v => ({ Ducats: { brand, value: v } }); + + t.deepEqual(await zoeDriver.verifyRealloc(), {}); + + const ducats = await zoeDriver.faucet(); + const initialAmount = await EV(issuer).getAmountOf(ducats); + + const beforeResult = await zoeDriver.probeReallocation(initialAmount, ducats); + t.true(beforeResult.stagingResult); + t.true(beforeResult.helperResult); + // In this version of the test, we're upgrading from new ZCF to new ZCF + t.true(beforeResult.internalResult); + t.deepEqual(await zoeDriver.verifyRealloc(), ducatAmountRecord(3n)); + + t.log('building proposal'); + // /////// Upgrading //////////////////////////////// + const zcfPackageSpec = { + package: 'vats', + packageScriptName: 'build:zcf-proposal', + }; + await buildAndExecuteProposal(zcfPackageSpec); + + t.log('upgrade zoe&zcf proposal executed'); + zoeDriver.upgradeProbe(zcfProbeBundle); + const nextDucats = beforeResult.leftoverPayments.Ducats; + const nextAmount = await EV(issuer).getAmountOf(nextDucats); + + const afterResult = await zoeDriver.probeReallocation(nextAmount, nextDucats); + t.true(afterResult.stagingResult); + t.true(afterResult.helperResult); + t.true(afterResult.internalResult); + t.deepEqual(await zoeDriver.verifyRealloc(), ducatAmountRecord(6n)); +}); diff --git a/packages/vats/test/bootstrapTests/zcfProbe.js b/packages/vats/test/bootstrapTests/zcfProbe.js new file mode 100644 index 00000000000..11c6dd5d6a0 --- /dev/null +++ b/packages/vats/test/bootstrapTests/zcfProbe.js @@ -0,0 +1,138 @@ +import { makeTracer } from '@agoric/internal'; +import { E } from '@endo/far'; +import { + atomicRearrange, + provideAll, +} from '@agoric/zoe/src/contractSupport/index.js'; +import { M, prepareExoClass, provide } from '@agoric/vat-data'; +import { AmountMath } from '@agoric/ertp'; + +const trace = makeTracer('ZCF Probe'); + +const ZcfProbeI = M.interface('ZCF Probe', { + makeProbeHelperInvitation: M.call().returns(M.promise()), + makeProbeInternalInvitation: M.call().returns(M.promise()), + makeProbeStagingInvitation: M.call().returns(M.promise()), + getAllocation: M.call().returns(M.any()), + makeFaucetInvitation: M.call().returns(M.promise()), +}); + +/** + * @param {ZCF} zcf + * @param {{storageNode: StorageNode}} privateArgs + * @param {import('@agoric/vat-data').Baggage} baggage + */ +export const prepare = async (zcf, privateArgs, baggage) => { + const { probeMint } = await provideAll(baggage, { + probeMint: () => zcf.makeZCFMint('Ducats'), + }); + + const { storageNode } = privateArgs; + const makeZcfProbe = await prepareExoClass( + baggage, + 'zcfProbe', + ZcfProbeI, + () => ({ + stashSeat: zcf.makeEmptySeatKit().zcfSeat, + probeMint: null, + }), + { + makeProbeHelperInvitation() { + const { stashSeat } = this.state; + + const probeHelper = seat => { + trace('ProbeHelper'); + const originalAlloc = seat.getCurrentAllocation().Ducats; + const one = AmountMath.make(originalAlloc.brand, 1n); + let result; + try { + atomicRearrange(zcf, harden([[seat, stashSeat, { Ducats: one }]])); + result = true; + } catch (e) { + result = false; + } + + seat.exit(); + return result; + }; + + return zcf.makeInvitation(probeHelper, 'probe helper'); + }, + makeProbeInternalInvitation() { + const { stashSeat } = this.state; + const probeInternal = seat => { + trace('ProbeIntrinsics'); + const originalAlloc = seat.getCurrentAllocation().Ducats; + const one = AmountMath.make(originalAlloc.brand, 1n); + let result; + try { + zcf.atomicRearrange(harden([[seat, stashSeat, { Ducats: one }]])); + result = true; + } catch (e) { + result = false; + } + + seat.clear(); + seat.exit(); + + trace('Intrinsics', result); + // write to vstorage so a test can detect it. + void E(storageNode).setValue(`${result}`); + + return result; + }; + + return zcf.makeInvitation(probeInternal, 'probe intrinsic'); + }, + makeProbeStagingInvitation() { + const { stashSeat } = this.state; + + const probeStaging = seat => { + trace('ProbeStaging'); + + const originalAlloc = seat.getCurrentAllocation().Ducats; + const one = AmountMath.make(originalAlloc.brand, 1n); + let result; + try { + stashSeat.incrementBy(seat.decrementBy({ Ducats: one })); + zcf.reallocate(seat, stashSeat); + result = true; + } catch (e) { + seat.clear(); + stashSeat.clear(); + result = false; + } + + seat.exit(); + return result; + }; + + return zcf.makeInvitation(probeStaging, 'probe staging'); + }, + getAllocation() { + const { stashSeat } = this.state; + trace('getAllocation'); + + return stashSeat.getCurrentAllocation(); + }, + makeFaucetInvitation() { + return zcf.makeInvitation(async seat => { + trace('faucet'); + const { brand } = await probeMint.getIssuerRecord(); + + await probeMint.mintGains( + { Ducats: AmountMath.make(brand, 16n) }, + seat, + ); + seat.exit(); + return 'minted 16n Ducats'; + }, 'faucet'); + }, + }, + ); + + const probe = await provide(baggage, 'probe', () => makeZcfProbe()); + return harden({ + creatorFacet: probe, + }); +};