diff --git a/packages/zoe/src/contractFacet/internal-types.js b/packages/zoe/src/contractFacet/internal-types.js index 08996cd60a7..88367d9659e 100644 --- a/packages/zoe/src/contractFacet/internal-types.js +++ b/packages/zoe/src/contractFacet/internal-types.js @@ -27,6 +27,8 @@ * @typedef ZCFZygote * @property {(bundle: SourceBundle) => void} evaluateContract * @property {(instanceAdminFromZoe: ERef, - instanceRecordFromZoe: InstanceRecord, - issuerStorageFromZoe: IssuerRecords) => Promise} startContract + * instanceRecordFromZoe: InstanceRecord, + * issuerStorageFromZoe: IssuerRecords, + * privateArgs: Object=, + * ) => Promise} startContract */ diff --git a/packages/zoe/src/contractFacet/types.js b/packages/zoe/src/contractFacet/types.js index 587bf006f26..55e641a8e51 100644 --- a/packages/zoe/src/contractFacet/types.js +++ b/packages/zoe/src/contractFacet/types.js @@ -208,6 +208,7 @@ /** * @callback ContractStartFn * @param {ContractFacet} zcf + * @param {Object=} privateArgs * @returns {ContractStartFnResult} */ diff --git a/packages/zoe/src/contractFacet/vatRoot.js b/packages/zoe/src/contractFacet/vatRoot.js index 4d09920432b..12b38327ebb 100644 --- a/packages/zoe/src/contractFacet/vatRoot.js +++ b/packages/zoe/src/contractFacet/vatRoot.js @@ -35,6 +35,7 @@ export function buildRootObject(powers, _params, testJigSetter = undefined) { zoeInstanceAdmin, instanceRecordFromZoe, issuerStorageFromZoe, + privateArgs = undefined, ) => { /** @type {ZCFZygote} */ const zcfZygote = makeZCFZygote( @@ -48,6 +49,7 @@ export function buildRootObject(powers, _params, testJigSetter = undefined) { zoeInstanceAdmin, instanceRecordFromZoe, issuerStorageFromZoe, + privateArgs, ); }; diff --git a/packages/zoe/src/contractFacet/zcfZygote.js b/packages/zoe/src/contractFacet/zcfZygote.js index c6d5e5c7adf..9493ffe8154 100644 --- a/packages/zoe/src/contractFacet/zcfZygote.js +++ b/packages/zoe/src/contractFacet/zcfZygote.js @@ -291,6 +291,7 @@ export const makeZCFZygote = ( instanceAdminFromZoe, instanceRecordFromZoe, issuerStorageFromZoe, + privateArgs = undefined, ) => { zoeInstanceAdminPromiseKit.resolve(instanceAdminFromZoe); instantiateInstanceRecordStorage(instanceRecordFromZoe); @@ -299,7 +300,7 @@ export const makeZCFZygote = ( // Next, execute the contract code, passing in zcf /** @type {Promise} */ const result = E(contractCode) - .start(zcf) + .start(zcf, privateArgs) .then( ({ creatorFacet = Far('emptyCreatorFacet', {}), diff --git a/packages/zoe/src/internal-types.js b/packages/zoe/src/internal-types.js index b464f947676..45eab73c8d5 100644 --- a/packages/zoe/src/internal-types.js +++ b/packages/zoe/src/internal-types.js @@ -178,6 +178,7 @@ * @param {ZoeInstanceAdmin} zoeInstanceAdmin * @param {InstanceRecord} instanceRecord * @param {IssuerRecords} issuerStorageFromZoe + * @param {Object=} privateArgs * @returns {Promise} * */ diff --git a/packages/zoe/src/zoeService/startInstance.js b/packages/zoe/src/zoeService/startInstance.js index f8baa931649..6d8e7505e49 100644 --- a/packages/zoe/src/zoeService/startInstance.js +++ b/packages/zoe/src/zoeService/startInstance.js @@ -1,10 +1,10 @@ // @ts-check -import { assert, details as X } from '@agoric/assert'; +import { assert, details as X, quote as q } from '@agoric/assert'; import { E } from '@agoric/eventual-send'; import { makePromiseKit } from '@agoric/promise-kit'; import { makeWeakStore as makeNonVOWeakStore } from '@agoric/store'; -import { Far } from '@agoric/marshal'; +import { Far, passStyleOf } from '@agoric/marshal'; import { makeZoeSeatAdminKit } from './zoeSeat'; import { makeHandle } from '../makeHandle'; @@ -26,6 +26,7 @@ export const makeStartInstance = ( installationP, uncleanIssuerKeywordRecord = harden({}), customTerms = harden({}), + privateArgs = undefined, ) => { /** @type {WeakStore} */ const seatHandleToZoeSeatAdmin = makeNonVOWeakStore('seatHandle'); @@ -33,6 +34,16 @@ export const makeStartInstance = ( const { installation, bundle } = await unwrapInstallation(installationP); // AWAIT /// + if (privateArgs !== undefined) { + const passStyle = passStyleOf(privateArgs); + assert( + passStyle === 'copyRecord', + X`privateArgs must be a pass-by-copy record, but instead was a ${q( + passStyle, + )}: ${privateArgs}`, + ); + } + const instance = makeHandle('Instance'); const zoeInstanceStorageManager = await makeZoeInstanceStorageManager( @@ -190,6 +201,7 @@ export const makeStartInstance = ( zoeInstanceAdminForZcf, zoeInstanceStorageManager.getInstanceRecord(), zoeInstanceStorageManager.getIssuerRecords(), + privateArgs, ); handleOfferObjPromiseKit.resolve(handleOfferObj); diff --git a/packages/zoe/src/zoeService/types.js b/packages/zoe/src/zoeService/types.js index 52613cc263c..fe1f9023695 100644 --- a/packages/zoe/src/zoeService/types.js +++ b/packages/zoe/src/zoeService/types.js @@ -111,6 +111,9 @@ * @param {ERef} installation * @param {IssuerKeywordRecord=} issuerKeywordRecord * @param {Object=} terms + * @param {Object=} privateArgs - an optional configuration object + * that can be used to pass in arguments that should not be in the + * public terms * @returns {Promise} */ diff --git a/packages/zoe/test/privateArgsUsageContract.js b/packages/zoe/test/privateArgsUsageContract.js new file mode 100644 index 00000000000..86b927454ca --- /dev/null +++ b/packages/zoe/test/privateArgsUsageContract.js @@ -0,0 +1,14 @@ +// @ts-check + +import { E } from '@agoric/eventual-send'; +import { Far } from '@agoric/marshal'; + +/** @type {ContractStartFn} */ +const start = (_zcf, privateArgs) => { + const creatorFacet = Far('creatorFacet', { + usePrivateArgs: () => E(privateArgs.myArg).doTest(), + }); + return harden({ creatorFacet }); +}; +harden(start); +export { start }; diff --git a/packages/zoe/test/swingsetTests/privateArgs/bootstrap.js b/packages/zoe/test/swingsetTests/privateArgs/bootstrap.js new file mode 100644 index 00000000000..fd98743fef4 --- /dev/null +++ b/packages/zoe/test/swingsetTests/privateArgs/bootstrap.js @@ -0,0 +1,22 @@ +import { E } from '@agoric/eventual-send'; +import { Far } from '@agoric/marshal'; + +export function buildRootObject(vatPowers, vatParameters) { + const { contractBundles: cb } = vatParameters; + return Far('root', { + async bootstrap(vats, devices) { + const vatAdminSvc = await E(vats.vatAdmin).createVatAdminService( + devices.vatAdmin, + ); + const zoe = await E(vats.zoe).buildZoe(vatAdminSvc); + const installations = { + privateArgsUsageContract: await E(zoe).install( + cb.privateArgsUsageContract, + ), + }; + + const aliceP = E(vats.alice).build(zoe, installations); + await E(aliceP).privateArgsUsageTest(); + }, + }); +} diff --git a/packages/zoe/test/swingsetTests/privateArgs/test-privateArgs.js b/packages/zoe/test/swingsetTests/privateArgs/test-privateArgs.js new file mode 100644 index 00000000000..f2bb81bbbf9 --- /dev/null +++ b/packages/zoe/test/swingsetTests/privateArgs/test-privateArgs.js @@ -0,0 +1,76 @@ +/* global __dirname */ + +// TODO Remove babel-standalone preinitialization +// https://github.com/endojs/endo/issues/768 +import '@agoric/babel-standalone'; +// eslint-disable-next-line import/no-extraneous-dependencies +import '@agoric/install-ses'; +// eslint-disable-next-line import/no-extraneous-dependencies +import test from 'ava'; +// eslint-disable-next-line import/no-extraneous-dependencies +import { buildVatController, buildKernelBundles } from '@agoric/swingset-vat'; +import bundleSource from '@agoric/bundle-source'; + +const CONTRACT_FILES = ['privateArgsUsageContract']; + +test.before(async t => { + const start = Date.now(); + const kernelBundles = await buildKernelBundles(); + const step2 = Date.now(); + const contractBundles = {}; + await Promise.all( + CONTRACT_FILES.map(async settings => { + let bundleName; + let contractPath; + if (typeof settings === 'string') { + bundleName = settings; + contractPath = settings; + } else { + ({ bundleName, contractPath } = settings); + } + const source = `${__dirname}/../../${contractPath}`; + const bundle = await bundleSource(source); + contractBundles[bundleName] = bundle; + }), + ); + const step3 = Date.now(); + + const vats = {}; + await Promise.all( + ['alice', 'zoe'].map(async name => { + const source = `${__dirname}/vat-${name}.js`; + const bundle = await bundleSource(source); + vats[name] = { bundle }; + }), + ); + const bootstrapSource = `${__dirname}/bootstrap.js`; + vats.bootstrap = { + bundle: await bundleSource(bootstrapSource), + parameters: { contractBundles }, // argv will be added to this + }; + const config = { bootstrap: 'bootstrap', vats }; + config.defaultManagerType = 'xs-worker'; + + const step4 = Date.now(); + const ktime = `${(step2 - start) / 1000}s kernel`; + const ctime = `${(step3 - step2) / 1000}s contracts`; + const vtime = `${(step4 - step3) / 1000}s vats`; + const ttime = `${(step4 - start) / 1000}s total`; + console.log(`bundling: ${ktime}, ${ctime}, ${vtime}, ${ttime}`); + + t.context.data = { kernelBundles, config }; +}); + +async function main(t, argv) { + const { kernelBundles, config } = t.context.data; + const controller = await buildVatController(config, argv, { kernelBundles }); + await controller.run(); + return controller.dump(); +} + +const expected = ['privateArgs.myArg was accessed in the contract']; + +test.serial('private args usage', async t => { + const dump = await main(t); + t.deepEqual(dump.log, expected); +}); diff --git a/packages/zoe/test/swingsetTests/privateArgs/vat-alice.js b/packages/zoe/test/swingsetTests/privateArgs/vat-alice.js new file mode 100644 index 00000000000..0c8edbd1504 --- /dev/null +++ b/packages/zoe/test/swingsetTests/privateArgs/vat-alice.js @@ -0,0 +1,29 @@ +import { E } from '@agoric/eventual-send'; +import { Far } from '@agoric/marshal'; + +const build = async (log, zoe, installations) => { + return Far('build', { + privateArgsUsageTest: async () => { + const privateArgs = harden({ + myArg: Far('arg', { + doTest: () => 'privateArgs.myArg was accessed in the contract', + }), + }); + const { creatorFacet } = await E(zoe).startInstance( + installations.privateArgsUsageContract, + undefined, + undefined, + privateArgs, + ); + + const testResult = await E(creatorFacet).usePrivateArgs(); + log(testResult); + }, + }); +}; + +export function buildRootObject(vatPowers) { + return Far('root', { + build: (...args) => build(vatPowers.testLog, ...args), + }); +} diff --git a/packages/zoe/test/swingsetTests/privateArgs/vat-zoe.js b/packages/zoe/test/swingsetTests/privateArgs/vat-zoe.js new file mode 100644 index 00000000000..be0a4736bbe --- /dev/null +++ b/packages/zoe/test/swingsetTests/privateArgs/vat-zoe.js @@ -0,0 +1,10 @@ +import { Far } from '@agoric/marshal'; + +// noinspection ES6PreferShortImport +import { makeZoe } from '../../../src/zoeService/zoe'; + +export function buildRootObject(_vatPowers) { + return Far('root', { + buildZoe: vatAdminSvc => makeZoe(vatAdminSvc), + }); +}