Skip to content

Commit

Permalink
Merge pull request #7617 from Agoric/mfig-upgradable-bank
Browse files Browse the repository at this point in the history
Durable vat-bank and virtual-purse
  • Loading branch information
michaelfig authored May 8, 2023
2 parents 0ce9545 + 18a3395 commit 628de13
Show file tree
Hide file tree
Showing 27 changed files with 1,221 additions and 447 deletions.
41 changes: 29 additions & 12 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,31 @@ const process = require('process');

const lintTypes = !!process.env.AGORIC_ESLINT_TYPES;

const deprecatedTerminology = [
const notLoanDeprecated = [
['currency', 'brand, asset or another descriptor'],
['loan', 'debt'],
['blacklist', 'denylist'],
['whitelist', 'allowlist'],
['RUN', 'IST', '/RUN/'],
].flatMap(([bad, good, badRgx = `/${bad}/i`]) =>
[
['Literal', 'value'],
['TemplateElement', 'value.raw'],
['Identifier', 'name'],
].map(([selectorType, field]) => ({
selector: `${selectorType}[${field}=${badRgx}]`,
message: `Use '${good}' instead of deprecated '${bad}'`,
})),
];
const allDeprecated = [...notLoanDeprecated, ['loan', 'debt']];

const deprecatedTerminology = Object.fromEntries(
Object.entries({
all: allDeprecated,
notLoan: notLoanDeprecated,
}).map(([category, deprecated]) => [
category,
deprecated.flatMap(([bad, good, badRgx = `/${bad}/i`]) =>
[
['Literal', 'value'],
['TemplateElement', 'value.raw'],
['Identifier', 'name'],
].map(([selectorType, field]) => ({
selector: `${selectorType}[${field}=${badRgx}]`,
message: `Use '${good}' instead of deprecated '${bad}'`,
})),
),
]),
);

module.exports = {
Expand Down Expand Up @@ -85,7 +95,14 @@ module.exports = {
// instead, and upgrade to 'error' when possible
// '@jessie.js/safe-await-separator': 'warn',
// TODO upgrade this (or a subset) to 'error'
'no-restricted-syntax': ['warn', ...deprecatedTerminology],
'no-restricted-syntax': ['warn', ...deprecatedTerminology.all],
},
},
{
// Allow "loan" contracts to mention the word "loan".
files: ['packages/zoe/src/contracts/loan/*.js'],
rules: {
'no-restricted-syntax': ['warn', ...deprecatedTerminology.notLoan],
},
},
{
Expand Down
2 changes: 1 addition & 1 deletion packages/ERTP/src/types-ambient.js
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@
* @property {() => Amount<K>} getCurrentAmount
* Get the amount contained in this purse.
*
* @property {() => Notifier<Amount<K>>} getCurrentAmountNotifier
* @property {() => LatestTopic<Amount<K>>} getCurrentAmountNotifier
* Get a lossy notifier for changes to this purse's balance.
*
* @property {PurseDeposit<K>} deposit
Expand Down
2 changes: 1 addition & 1 deletion packages/agoric-cli/src/commands/reserve.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { outputActionAndHint } from '../lib/wallet.js';

/**
* @param {import('anylogger').Logger} _logger
* @param io
* @param {*} io
*/
export const makeReserveCommand = (_logger, io = {}) => {
const { stdout = process.stdout, stderr = process.stderr, now } = io;
Expand Down
2 changes: 1 addition & 1 deletion packages/agoric-cli/src/lib/format.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export const asBoardRemote = x => {

/**
* Summarize the balances array as user-facing informative tuples
*
* @param {import('@agoric/smart-wallet/src/smartWallet').CurrentWalletRecord['purses']} purses
* @param {AssetDescriptor[]} assets
*/
Expand Down
1 change: 0 additions & 1 deletion packages/inter-protocol/src/price/fluxAggregatorKit.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ const priceDescriptionFromQuote = quote => quote.quoteAmount.value[0];
* @param {StorageNode} storageNode
* @param {() => PublishKit<any>} makeDurablePublishKit
* @param {import('@agoric/zoe/src/contractSupport/recorder.js').MakeRecorder} makeRecorder
* @returns a method to call once to create the prepared kit
*/
export const prepareFluxAggregatorKit = async (
baggage,
Expand Down
18 changes: 10 additions & 8 deletions packages/internal/src/callback.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const ownKeys =

/**
* @template T
* @typedef {(...args: Parameters<ReturnType<prepareAttenuator>>) => Farable<T>} AttenuatorMaker
* @typedef {(...args: Parameters<ReturnType<prepareAttenuator>>) => Farable<T>} MakeAttenuator
*/

/**
Expand Down Expand Up @@ -192,9 +192,9 @@ harden(isCallback);
/**
* Prepare an attenuator class whose methods can be redirected via callbacks.
*
* @template {{ [K in PropertyKey]: (this: any, ...args: unknown[]) => any}} Methods
* @template {PropertyKey} M
* @param {import('@agoric/zone').Zone} zone The zone in which to allocate attenuators.
* @param {(keyof Methods)[]} methodNames Methods to forward.
* @param {M[]} methodNames Methods to forward.
* @param {object} opts
* @param {InterfaceGuard} [opts.interfaceGuard] An interface guard for the
* new attenuator.
Expand All @@ -207,7 +207,8 @@ export const prepareAttenuator = (
) => {
/**
* @typedef {(this: any, ...args: unknown[]) => any} Method
* @typedef {{ [K in keyof Methods]?: Callback<any> | null}} Overrides
* @typedef {{ [K in M]?: Callback<any> | null}} Overrides
* @typedef {{ [K in M]: (this: any, ...args: unknown[]) => any }} Methods
*/
const methods = /** @type {Methods} */ (
fromEntries(
Expand Down Expand Up @@ -307,7 +308,6 @@ harden(prepareAttenuator);
/**
* Prepare an attenuator whose methodNames are derived from the interfaceGuard.
*
* @template {{ [K in PropertyKey]: (this: any, ...args: unknown[]) => any}} Methods
* @param {import('@agoric/zone').Zone} zone
* @param {InterfaceGuard} interfaceGuard
* @param {object} [opts]
Expand All @@ -316,8 +316,10 @@ harden(prepareAttenuator);
export const prepareGuardedAttenuator = (zone, interfaceGuard, opts = {}) => {
const { methodGuards } = interfaceGuard;
const methodNames = ownKeys(methodGuards);
return /** @type {AttenuatorMaker<Methods>} */ (
prepareAttenuator(zone, methodNames, { ...opts, interfaceGuard })
);
const makeAttenuator = prepareAttenuator(zone, methodNames, {
...opts,
interfaceGuard,
});
return /** @type {MakeAttenuator<any>} */ (makeAttenuator);
};
harden(prepareGuardedAttenuator);
3 changes: 3 additions & 0 deletions packages/internal/test/test-callback.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ test('makeAttenuator', async t => {
throw Error('unexpected original.m3');
},
});
// @ts-expect-error deliberate: omitted method
t.throws(() => makeAttenuator({ target, overrides: { m3: null } }), {
message: `"Attenuator" overrides["m3"] not allowed by methodNames`,
});
Expand All @@ -299,6 +300,7 @@ test('makeAttenuator', async t => {
message: `unimplemented "Attenuator" method "m1"`,
});
await t.throwsAsync(() => atE.m2(), { message: `unexpected original.m2` });
// @ts-expect-error deliberate: omitted method
t.throws(() => atE.m3(), { message: /not a function/ });
await t.throwsAsync(() => atE.m4(), { message: /target has no method "m4"/ });

Expand All @@ -325,6 +327,7 @@ test('makeAttenuator', async t => {
const p2 = atSync.m2();
t.assert(p2 instanceof Promise);
t.is(await p2, 'return abc');
// @ts-expect-error deliberate: omitted method
t.throws(() => atSync.m3(), { message: /not a function/ });
t.throws(() => atSync.m4(), { message: /not a function/ });
});
2 changes: 1 addition & 1 deletion packages/notifier/src/asyncIterableAdaptor.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export const observeIteration = (asyncIterableP, iterationObserver) => {
* states are assumed irrelevant and dropped.
*
* @template T
* @param {ERef<Notifier<T>>} notifierP
* @param {ERef<LatestTopic<T>>} notifierP
* @param {Partial<IterationObserver<T>>} iterationObserver
* @returns {Promise<undefined>}
*/
Expand Down
20 changes: 13 additions & 7 deletions packages/notifier/src/subscribe.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,17 @@ const reconnectAsNeeded = async (getter, seed = []) => {
* Create a near iterable that corresponds to a potentially far one.
*
* @template T
* @param {ERef<AsyncIterable<T>>} itP
* @param {ERef<AsyncIterableIterator<T>>} itP
*/
export const subscribe = itP =>
Far('AsyncIterable', {
[Symbol.asyncIterator]: () => {
const it = E(itP)[Symbol.asyncIterator]();
return Far('AsyncIterator', {
const self = Far('AsyncIterableIterator', {
[Symbol.asyncIterator]: () => self,
next: async () => E(it).next(),
});
return self;
},
});

Expand All @@ -84,12 +86,13 @@ export const subscribe = itP =>
* @param {ERef<EachTopic<T>>} topic
* @param {ERef<PublicationRecord<T>>} nextCellP
* PublicationRecord corresponding with the first iteration result
* @returns {ForkableAsyncIterator<T, T>}
* @returns {ForkableAsyncIterableIterator<T, T>}
*/
const makeEachIterator = (topic, nextCellP) => {
// To understand the implementation, start with
// https://web.archive.org/web/20160404122250/http://wiki.ecmascript.org/doku.php?id=strawman:concurrency#infinite_queue
return Far('EachIterator', {
const self = Far('EachIterator', {
[Symbol.asyncIterator]: () => self,
next: () => {
const {
head: resultP,
Expand Down Expand Up @@ -124,6 +127,7 @@ const makeEachIterator = (topic, nextCellP) => {
},
fork: () => makeEachIterator(topic, nextCellP),
});
return self;
};

/**
Expand Down Expand Up @@ -158,7 +162,7 @@ harden(subscribeEach);
* @param {ERef<LatestTopic<T>>} topic
* @param {bigint} [localUpdateCount]
* @param {IteratorReturnResult<T>} [terminalResult]
* @returns {ForkableAsyncIterator<T, T>}
* @returns {ForkableAsyncIterableIterator<T, T>}
*/
const cloneLatestIterator = (topic, localUpdateCount, terminalResult) => {
let mutex = Promise.resolve();
Expand Down Expand Up @@ -192,8 +196,9 @@ const cloneLatestIterator = (topic, localUpdateCount, terminalResult) => {
return harden({ done: false, value });
};

return Far('LatestIterator', {
const self = Far('LatestIterator', {
fork: () => cloneLatestIterator(topic, localUpdateCount, terminalResult),
[Symbol.asyncIterator]: () => self,
next: async () => {
// In this adaptor, once `next()` is called and returns an unresolved
// promise, further `next()` calls will also return unresolved promises
Expand Down Expand Up @@ -228,12 +233,13 @@ const cloneLatestIterator = (topic, localUpdateCount, terminalResult) => {
return nextResult;
},
});
return self;
};

/**
* @template T
* @param {ERef<LatestTopic<T>>} topic
* @returns {ForkableAsyncIterator<T, T>}
* @returns {ForkableAsyncIterableIterator<T, T>}
*/
const makeLatestIterator = topic => cloneLatestIterator(topic);

Expand Down
7 changes: 7 additions & 0 deletions packages/notifier/src/types-ambient.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@
* into multiple independent ForkableAsyncIterators starting from that position.
*/

/**
* @template T
* @template [TReturn=any]
* @template [TNext=undefined]
* @typedef {ForkableAsyncIterator<T, TReturn, TNext> & { [Symbol.asyncIterator](): ForkableAsyncIterableIterator<T, TReturn, TNext> }} ForkableAsyncIterableIterator
*/

/**
* @template T
* @template [TReturn=any]
Expand Down
2 changes: 1 addition & 1 deletion packages/smart-wallet/src/walletFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export const makeAssetRegistry = assetPublisher => {
* @typedef {import('@agoric/vats').NameHub} NameHub
*
* @typedef {{
* getAssetSubscription: () => ERef<Subscription<import('@agoric/vats/src/vat-bank').AssetDescriptor>>
* getAssetSubscription: () => ERef<AsyncIterable<import('@agoric/vats/src/vat-bank').AssetDescriptor>>
* }} AssetPublisher
*/

Expand Down
6 changes: 5 additions & 1 deletion packages/smart-wallet/test/test-addAsset.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { E } from '@endo/far';
import { buildRootObject as buildBankVatRoot } from '@agoric/vats/src/vat-bank.js';
import { makeIssuerKit } from '@agoric/ertp';
import { eventLoopIteration } from '@agoric/internal/src/testing-utils.js';
import { makeScalarMapStore } from '@agoric/store';
import { makeDefaultTestContext } from './contexts.js';
import { makeMockTestSpace } from './supports.js';

Expand All @@ -13,7 +14,10 @@ const test = anyTest;
test.before(async t => {
const withBankManager = async () => {
const noBridge = undefined;
const bankManager = E(buildBankVatRoot()).makeBankManager(noBridge);
const baggage = makeScalarMapStore('baggage');
const bankManager = E(
buildBankVatRoot(undefined, undefined, baggage),
).makeBankManager(noBridge);
const noop = () => {};
const space0 = await makeMockTestSpace(noop);
space0.produce.bankManager.reset();
Expand Down
9 changes: 8 additions & 1 deletion packages/swingset-liveslots/src/virtualObjectManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,13 @@ const insistSameCapData = (oldCD, newCD) => {
* @param {import('@endo/marshal').FromCapData<string>} unserialize Unserializer for this vat
* @param {*} assertAcceptableSyscallCapdataSize Function to check for oversized
* syscall params
* @param {import('./types').LiveSlotsOptions} liveSlotsOptions
* @param {import('./types').LiveSlotsOptions} [liveSlotsOptions]
* @param {{ WeakMap: typeof WeakMap, WeakSet: typeof WeakSet }} [powers]
* Specifying the underlying WeakMap/WeakSet objects to wrap with
* VirtualObjectAwareWeakMap/Set. By default, capture the ones currently
* defined on `globalThis` when the maker is invoked, to avoid infinite
* recursion if our returned WeakMap/WeakSet wrappers are subsequently installed
* on globalThis.
*
* @returns {object} a new virtual object manager.
*
Expand Down Expand Up @@ -348,6 +354,7 @@ export const makeVirtualObjectManager = (
unserialize,
assertAcceptableSyscallCapdataSize,
liveSlotsOptions = {},
{ WeakMap, WeakSet } = globalThis,
) => {
const { allowStateShapeChanges = false } = liveSlotsOptions;

Expand Down
2 changes: 1 addition & 1 deletion packages/vats/src/core/basic-behaviors.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export const makeVatsFromBundles = async ({
};
harden(makeVatsFromBundles);

/** @param {BootstrapPowers} powers */
/** @param {BootstrapSpace} powers */
export const produceStartUpgradable = async ({
consume: { zoe },
produce, // startUpgradable
Expand Down
2 changes: 1 addition & 1 deletion packages/vats/src/nameHub.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export const prepareMixinMyAddress = zone => {
...NameHubIKit.nameAdmin.methodGuards,
getMyAddress: M.call().returns(M.string()),
});
/** @type {import('@agoric/internal/src/callback.js').AttenuatorMaker<import('./types.js').MyAddressNameAdmin>} */
/** @type {import('@agoric/internal/src/callback.js').MakeAttenuator<import('./types.js').MyAddressNameAdmin>} */
const mixin = prepareGuardedAttenuator(zone, MixinI, {
tag: 'MyAddressNameAdmin',
});
Expand Down
Loading

0 comments on commit 628de13

Please sign in to comment.