From 51d1a7255e7b7c22120b573561a9e65c331eba99 Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Mon, 2 Jan 2023 19:41:46 -0800 Subject: [PATCH 1/2] refactor: pulling up more await weeds --- packages/SwingSet/package.json | 1 - .../src/devices/plugin/device-plugin.js | 9 ++- packages/SwingSet/src/vats/network/network.js | 8 +-- packages/agoric-cli/package.json | 2 +- packages/agoric-cli/src/deploy.js | 8 +-- packages/agoric-cli/src/init.js | 19 +++---- packages/agoric-cli/src/install.js | 55 +++++++++---------- packages/agoric-cli/src/open.js | 4 +- packages/agoric-cli/src/publish.js | 14 +++-- packages/agoric-cli/src/start.js | 32 +++++++---- packages/cache/package.json | 4 +- packages/cache/src/store.js | 9 ++- packages/cosmic-swingset/src/sim-chain.js | 24 ++++---- .../vaultFactory/liquidateIncrementally.js | 3 +- packages/internal/package.json | 3 +- packages/internal/src/utils.js | 30 ++++++++++ packages/smart-wallet/src/offers.js | 7 +-- packages/vats/src/vat-bank.js | 3 +- .../src/admin-websocket-connector.js | 3 +- packages/xsnap/package.json | 1 + packages/xsnap/src/replay.js | 5 +- packages/xsnap/src/xsnap.js | 4 +- .../contracts/callSpread/pricedCallSpread.js | 3 +- 23 files changed, 141 insertions(+), 110 deletions(-) diff --git a/packages/SwingSet/package.json b/packages/SwingSet/package.json index 537c40f2ce0..44b35244560 100644 --- a/packages/SwingSet/package.json +++ b/packages/SwingSet/package.json @@ -48,7 +48,6 @@ "@endo/zip": "^0.2.28", "anylogger": "^0.21.0", "import-meta-resolve": "^1.1.1", - "jessie.js": "^0.3.2", "microtime": "^3.1.0", "semver": "^6.3.0" }, diff --git a/packages/SwingSet/src/devices/plugin/device-plugin.js b/packages/SwingSet/src/devices/plugin/device-plugin.js index e0836246a33..b4fd49740db 100644 --- a/packages/SwingSet/src/devices/plugin/device-plugin.js +++ b/packages/SwingSet/src/devices/plugin/device-plugin.js @@ -51,8 +51,8 @@ export function buildRootDeviceNode(tools) { * @param {number} epoch which generation of CapTP instances this is * @returns {Promise<(obj: Record) => void>} send a message to the module */ - async function createConnection(mod, index, epoch) { - try { + const createConnection = async (mod, index, epoch) => + (async () => { const modNS = await endowments.import(mod); const receiver = obj => { // console.info('receiver', index, obj); @@ -87,11 +87,10 @@ export function buildRootDeviceNode(tools) { const { dispatch } = makeCapTP(mod, receiver, bootstrap, { epoch }); return dispatch; - } catch (e) { + })().catch(e => { console.error(`Cannot connect to ${mod}:`, e); throw e; - } - } + }); /** * Load a module and connect to it. diff --git a/packages/SwingSet/src/vats/network/network.js b/packages/SwingSet/src/vats/network/network.js index 4e0da8d9b7a..31695eb594c 100644 --- a/packages/SwingSet/src/vats/network/network.js +++ b/packages/SwingSet/src/vats/network/network.js @@ -3,7 +3,7 @@ import { E } from '@endo/eventual-send'; import { Far } from '@endo/marshal'; import { makePromiseKit } from '@endo/promise-kit'; import { assert, details as X, Fail } from '@agoric/assert'; -import { asyncGenerate } from 'jessie.js'; +import { whileTrue } from '@agoric/internal'; import { toBytes } from './bytes.js'; import '@agoric/store/exported.js'; @@ -237,11 +237,7 @@ export function makeNetworkProtocol(protocolHandler) { const bind = async localAddr => { // Check if we are underspecified (ends in slash) const underspecified = localAddr.endsWith(ENDPOINT_SEPARATOR); - const whileUnderspecified = asyncGenerate(() => ({ - done: !underspecified, - value: null, - })); - for await (const _ of whileUnderspecified) { + for await (const _ of whileTrue(() => underspecified)) { const portID = await E(protocolHandler).generatePortID( localAddr, protocolHandler, diff --git a/packages/agoric-cli/package.json b/packages/agoric-cli/package.json index 06708c53c89..1b48a015fb9 100644 --- a/packages/agoric-cli/package.json +++ b/packages/agoric-cli/package.json @@ -34,6 +34,7 @@ "@agoric/casting": "^0.3.2", "@agoric/cosmic-proto": "^0.2.1", "@agoric/ertp": "^0.15.3", + "@agoric/internal": "^0.2.1", "@agoric/nat": "^4.1.0", "@agoric/smart-wallet": "^0.4.2", "@agoric/swingset-vat": "^0.30.2", @@ -59,7 +60,6 @@ "deterministic-json": "^1.0.5", "esm": "agoric-labs/esm#Agoric-built", "inquirer": "^8.2.2", - "jessie.js": "^0.3.2", "opener": "^1.5.2", "tmp": "^0.2.1", "ws": "^7.2.0" diff --git a/packages/agoric-cli/src/deploy.js b/packages/agoric-cli/src/deploy.js index a7475482c85..294bd7422b9 100644 --- a/packages/agoric-cli/src/deploy.js +++ b/packages/agoric-cli/src/deploy.js @@ -12,7 +12,7 @@ import inquirer from 'inquirer'; import createEsmRequire from 'esm'; import { createRequire } from 'module'; import { SigningStargateClient } from '@cosmjs/stargate'; -import { asyncGenerate } from 'jessie.js'; +import { whileTrue } from '@agoric/internal'; import { getAccessToken } from '@agoric/access-token'; @@ -248,11 +248,7 @@ export default async function deployMain(progname, rawArgs, powers, opts) { let lastUpdateCount; let stillLoading = [...need].sort(); progressDot = 'o'; - const untilNotLoading = asyncGenerate(() => ({ - done: !stillLoading.length, - value: stillLoading, - })); - for await (const _ of untilNotLoading) { + for await (const _ of whileTrue(() => stillLoading.length)) { // Wait for the notifier to report a new state. process.stdout.write(progressDot); console.debug('need:', stillLoading.join(', ')); diff --git a/packages/agoric-cli/src/init.js b/packages/agoric-cli/src/init.js index 97ad35185d7..a81283f8487 100644 --- a/packages/agoric-cli/src/init.js +++ b/packages/agoric-cli/src/init.js @@ -40,15 +40,14 @@ export default async function initMain(_progname, rawArgs, priv, opts) { dappBranch = ['-b', opts.dappBranch]; } - if ( - await pspawn( - 'git', - ['clone', '--origin=upstream', dappURL, DIR, ...dappBranch], - { - stdio: 'inherit', - }, - ) - ) { + const exitStatus = await pspawn( + 'git', + ['clone', '--origin=upstream', dappURL, DIR, ...dappBranch], + { + stdio: 'inherit', + }, + ); + if (exitStatus) { throw Error('cannot clone'); } @@ -57,7 +56,7 @@ export default async function initMain(_progname, rawArgs, priv, opts) { let topLevelName; const subdirs = ['', 'api/', 'contract/', 'ui/', '_agstate/agoric-servers/']; - for (const dir of subdirs) { + for await (const dir of subdirs) { const path = `${DIR}/${dir}package.json`; log('rewriting ', path); diff --git a/packages/agoric-cli/src/install.js b/packages/agoric-cli/src/install.js index 7d0b9b80d11..e5f77f04f77 100644 --- a/packages/agoric-cli/src/install.js +++ b/packages/agoric-cli/src/install.js @@ -59,7 +59,7 @@ export default async function installMain(progname, rawArgs, powers, opts) { } const yarnInstallEachWorktree = async (phase, ...flags) => { - for (const workTree of workTrees) { + for await (const workTree of workTrees) { log.info(`yarn install ${phase} in ${workTree}`); // eslint-disable-next-line no-await-in-loop const yarnInstall = await pspawn( @@ -226,15 +226,14 @@ export default async function installMain(progname, rawArgs, powers, opts) { } } } else { + const subdirMapper = async subd => { + const exists = await fs.stat(`${subd}/package.json`).catch(_ => false); + return exists && subd; + }; const existingSubdirs = await Promise.all( ['.', '_agstate/agoric-servers', 'contract', 'api', 'ui'] .sort() - .map(async subd => { - const exists = await fs - .stat(`${subd}/package.json`) - .catch(_ => false); - return exists && subd; - }), + .map(subdirMapper), ); subdirs = existingSubdirs.filter(subd => subd); } @@ -254,22 +253,21 @@ export default async function installMain(progname, rawArgs, powers, opts) { await fs.symlink(sdkRoot, sdkWorktree); } - await Promise.all( - subdirs.map(async subdir => { - const nm = `${subdir}/node_modules`; - log(chalk.bold.green(`removing ${nm} link`)); - await fs.unlink(nm).catch(_ => {}); + const subdirMapper = async subdir => { + const nm = `${subdir}/node_modules`; + log(chalk.bold.green(`removing ${nm} link`)); + await fs.unlink(nm).catch(_ => {}); - // Remove all the package links. - // This is needed to prevent yarn errors when installing new versions of - // linked modules (like `@agoric/zoe`). - await Promise.all( - [...sdkPackageToPath.keys()].map(async pjName => - fs.unlink(`${nm}/${pjName}`).catch(_ => {}), - ), - ); - }), - ); + // Remove all the package links. + // This is needed to prevent yarn errors when installing new versions of + // linked modules (like `@agoric/zoe`). + await Promise.all( + [...sdkPackageToPath.keys()].map(async pjName => + fs.unlink(`${nm}/${pjName}`).catch(_ => {}), + ), + ); + }; + await Promise.all(subdirs.map(subdirMapper)); } else { DEFAULT_SDK_PACKAGE_NAMES.forEach(name => sdkPackageToPath.set(name, null)); } @@ -310,17 +308,14 @@ export default async function installMain(progname, rawArgs, powers, opts) { ); const sdkPackages = [...sdkPackageToPath.keys()].sort(); - for (const subdir of subdirs) { - // eslint-disable-next-line no-await-in-loop + for await (const subdir of subdirs) { const exists = await fs.stat(`${subdir}/package.json`).catch(_ => false); - if ( - exists && - // eslint-disable-next-line no-await-in-loop - (await pspawn('yarn', [...linkFlags, 'link', ...sdkPackages], { + const flag = await (exists && + pspawn('yarn', [...linkFlags, 'link', ...sdkPackages], { stdio: 'inherit', cwd: subdir, - })) - ) { + })); + if (flag) { log.error('Cannot yarn link', ...sdkPackages); return 1; } diff --git a/packages/agoric-cli/src/open.js b/packages/agoric-cli/src/open.js index d11ce74d811..2e481dd69c3 100644 --- a/packages/agoric-cli/src/open.js +++ b/packages/agoric-cli/src/open.js @@ -53,8 +53,10 @@ export default async function walletMain(progname, rawArgs, powers, opts) { }${suffix}#accessToken=${encodeURIComponent(walletAccessToken)}`; process.stdout.write(`${walletUrl}\n`); + let p; if (opts.browser) { const browser = opener(walletUrl); - await new Promise(resolve => browser.on('exit', resolve)); + p = new Promise(resolve => browser.on('exit', resolve)); } + await p; } diff --git a/packages/agoric-cli/src/publish.js b/packages/agoric-cli/src/publish.js index 302a55ccb17..8e929076bd8 100644 --- a/packages/agoric-cli/src/publish.js +++ b/packages/agoric-cli/src/publish.js @@ -253,10 +253,12 @@ export const makeCosmosBundlePublisher = ({ const leader = makeLeaderFromRpcAddresses(rpcAddresses); + const file = await readFile( + pathResolve(homeDirectory, 'ag-solo-mnemonic'), + 'ascii', + ); // AWAIT - const mnemonic = ( - await readFile(pathResolve(homeDirectory, 'ag-solo-mnemonic'), 'ascii') - ).trim(); + const mnemonic = file.trim(); const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { prefix: Agoric.Bech32MainPrefix, @@ -438,20 +440,21 @@ const publishBundle = async ( const { type } = connectionSpec; assert.typeof(type, 'string', X`Expected string "type" on connectionSpec`); + let p; if (type === 'http') { assertHttpConnectionSpec(connectionSpec); assert( publishBundleHttp, 'HTTP installation transaction publisher required', ); - await publishBundleHttp(bundle, connectionSpec); + p = publishBundleHttp(bundle, connectionSpec); } else if (type === 'chain-cosmos-sdk') { assertCosmosConnectionSpec(connectionSpec); assert( publishBundleCosmos, 'Cosmos SDK installation transaction publisher required', ); - await publishBundleCosmos(bundle, connectionSpec); + p = publishBundleCosmos(bundle, connectionSpec); } else if (type === 'fake-chain') { // For the purposes of submitting a bundle to an API like // E(zoe).install(bundle), in the cases where the publication target does @@ -464,6 +467,7 @@ const publishBundle = async ( throw new Error(`Unsupported connection type ${type}`); } + await p; return hashBundle; }; diff --git a/packages/agoric-cli/src/start.js b/packages/agoric-cli/src/start.js index 4ad59fb189b..3169f4fb48f 100644 --- a/packages/agoric-cli/src/start.js +++ b/packages/agoric-cli/src/start.js @@ -5,6 +5,7 @@ import path from 'path'; import { createRequire } from 'module'; import { Nat, isNat } from '@agoric/nat'; +import { untilTrue } from '@agoric/internal'; import { CENTRAL_DENOM, @@ -179,7 +180,8 @@ export default async function startMain(progname, rawArgs, powers, opts) { } if (!opts.dockerTag) { - if (!(await exists('node_modules/@agoric/solo'))) { + const soloExists = await exists('node_modules/@agoric/solo'); + if (!soloExists) { log.error(`you must first run '${progname} install'`); return 1; } @@ -195,7 +197,8 @@ export default async function startMain(progname, rawArgs, powers, opts) { } const fakeGCI = 'sim-chain'; - if (!(await exists(agServer))) { + const serverExists = await exists(agServer); + if (!serverExists) { log(chalk.yellow(`initializing ${profileName}`)); await pspawn( agSolo, @@ -290,7 +293,8 @@ export default async function startMain(progname, rawArgs, powers, opts) { ); } - if (!(await exists(agServer))) { + const serverExists = await exists(agServer); + if (!serverExists) { const exitStatus = await chainSpawn([ 'init', 'local-chain', @@ -306,7 +310,8 @@ export default async function startMain(progname, rawArgs, powers, opts) { for (const keyName of ['provision', 'delegate0']) { /* eslint-disable no-await-in-loop */ let capret = showKey(keyName); - if (await capret[0]) { + const capretZero = await capret[0]; + if (capretZero) { const exitStatus = await keysSpawn([ 'keys', 'add', @@ -327,7 +332,8 @@ export default async function startMain(progname, rawArgs, powers, opts) { } const genesisFile = `${agServer}/config/genesis.json`; - if (!(await exists(`${genesisFile}.stamp`))) { + const stampExists = await exists(`${genesisFile}.stamp`); + if (!stampExists) { let exitStatus; exitStatus = await chainSpawn([ 'add-genesis-account', @@ -497,8 +503,9 @@ export default async function startMain(progname, rawArgs, powers, opts) { ); } + const serverExists = await exists(agServer); // Initialise the solo directory and key. - if (!(await exists(agServer))) { + if (!serverExists) { const initArgs = [`--webport=${portNum}`]; if (opts.dockerTag) { initArgs.push(`--webhost=0.0.0.0`); @@ -545,13 +552,13 @@ export default async function startMain(progname, rawArgs, powers, opts) { const gciFile = `_agstate/agoric-servers/local-chain-${CHAIN_PORT}/config/genesis.json.sha256`; process.stdout.write(`Waiting for local-chain-${CHAIN_PORT} to start...`); let hasGci = false; - while (!hasGci) { + for await (const _ of untilTrue(() => hasGci)) { process.stdout.write('.'); // eslint-disable-next-line no-await-in-loop await new Promise((resolve, reject) => { fs.stat(gciFile).then( - _ => { + _2 => { hasGci = true; resolve(true); }, @@ -581,8 +588,8 @@ export default async function startMain(progname, rawArgs, powers, opts) { // Provision the ag-solo, if necessary. let bestRpcAddr; - while (!bestRpcAddr) { - for (const rpcAddr of rpcAddrs) { + for await (const _ of untilTrue(() => bestRpcAddr)) { + for await (const rpcAddr of rpcAddrs) { // eslint-disable-next-line no-await-in-loop exitStatus = await keysSpawn([ 'query', @@ -630,7 +637,7 @@ export default async function startMain(progname, rawArgs, powers, opts) { SOLO_COINS, ], ]; - for (const cmd of provCmds) { + for (/* await */ const cmd of provCmds) { const capret = capture(keysSpawn, cmd, true); // eslint-disable-next-line no-await-in-loop exitStatus = await capret[0]; @@ -665,8 +672,9 @@ export default async function startMain(progname, rawArgs, powers, opts) { return exitStatus; } + const file = await fs.readFile(gciFile, 'utf-8'); // Connect to the chain. - const gci = (await fs.readFile(gciFile, 'utf-8')).trimRight(); + const gci = file.trimRight(); exitStatus = await soloSpawn( ['set-gci-ingress', `--chainID=${CHAIN_ID}`, gci, bestRpcAddr], spawnOpts, diff --git a/packages/cache/package.json b/packages/cache/package.json index 3c5ec067ff6..ee1528d1d18 100644 --- a/packages/cache/package.json +++ b/packages/cache/package.json @@ -19,13 +19,13 @@ "author": "Agoric", "license": "Apache-2.0", "dependencies": { + "@agoric/internal": "^0.2.1", "@agoric/notifier": "^0.5.1", "@agoric/store": "^0.8.3", "@agoric/vat-data": "^0.4.3", "@agoric/vats": "^0.13.0", "@endo/far": "^0.2.14", - "@endo/marshal": "^0.8.1", - "jessie.js": "^0.3.2" + "@endo/marshal": "^0.8.1" }, "devDependencies": { "@agoric/zoe": "^0.25.3", diff --git a/packages/cache/src/store.js b/packages/cache/src/store.js index 2c7cf60c03f..d1aa03fd9df 100644 --- a/packages/cache/src/store.js +++ b/packages/cache/src/store.js @@ -2,7 +2,7 @@ import { E, Far } from '@endo/far'; import { deeplyFulfilled, makeMarshal } from '@endo/marshal'; import { matches, makeScalarMapStore } from '@agoric/store'; import { makeScalarBigMapStore } from '@agoric/vat-data'; -import { asyncGenerate } from 'jessie.js'; +import { untilTrue } from '@agoric/internal'; import { withGroundState, makeState } from './state.js'; import './types.js'; @@ -77,10 +77,9 @@ const applyCacheTransaction = async ( // Loop until our updated state is fresh wrt our current state. basisState = stateStore.get(keyStr); - const untilUpdateSynced = asyncGenerate(() => ({ - value: null, - done: !updatedState || updatedState.generation > basisState.generation, - })); + const untilUpdateSynced = untilTrue( + () => !updatedState || updatedState.generation > basisState.generation, + ); for await (const _ of untilUpdateSynced) { updatedState = await getUpdatedState(basisState); // AWAIT INTERLEAVING diff --git a/packages/cosmic-swingset/src/sim-chain.js b/packages/cosmic-swingset/src/sim-chain.js index ac5c702b0a9..37bf4b1da65 100644 --- a/packages/cosmic-swingset/src/sim-chain.js +++ b/packages/cosmic-swingset/src/sim-chain.js @@ -39,12 +39,11 @@ async function makeMapStorage(file) { }; let obj = {}; - try { + await (async () => { content = await fs.promises.readFile(file); obj = JSON.parse(content); - } catch (e) { - return map; - } + })().catch(() => map); + Object.entries(obj).forEach(([k, v]) => map.set(k, importMailbox(v))); return map; @@ -70,13 +69,12 @@ export async function connectToFakeChain(basedir, GCI, delay, inbound) { }, }; - const vatconfig = new URL( - await importMetaResolve( - process.env.CHAIN_BOOTSTRAP_VAT_CONFIG || - argv.bootMsg.params.bootstrap_vat_config, - import.meta.url, - ), - ).pathname; + const url = await importMetaResolve( + process.env.CHAIN_BOOTSTRAP_VAT_CONFIG || + argv.bootMsg.params.bootstrap_vat_config, + import.meta.url, + ); + const vatconfig = new URL(url).pathname; const stateDBdir = path.join(basedir, `fake-chain-${GCI}-state`); function replayChainSends() { Fail`Replay not implemented`; @@ -191,10 +189,12 @@ export async function connectToFakeChain(basedir, GCI, delay, inbound) { intoChain.push([newMessages, acknum]); // Only actually simulate a block if we're not in bootstrap. + let p; if (blockHeight && !delay) { clearTimeout(nextBlockTimeout); - await simulateBlock(); + p = simulateBlock(); } + await p; } const bootSimChain = async () => { diff --git a/packages/inter-protocol/src/vaultFactory/liquidateIncrementally.js b/packages/inter-protocol/src/vaultFactory/liquidateIncrementally.js index 2377fd83b00..5a77de1d437 100644 --- a/packages/inter-protocol/src/vaultFactory/liquidateIncrementally.js +++ b/packages/inter-protocol/src/vaultFactory/liquidateIncrementally.js @@ -11,6 +11,7 @@ import { } from '@agoric/zoe/src/contractSupport/index.js'; import { AmountMath } from '@agoric/ertp'; import { Far } from '@endo/marshal'; +import { forever } from '@agoric/internal'; import { makeTracer } from '../makeTracer.js'; const { Fail } = assert; @@ -187,7 +188,7 @@ const start = async zcf => { * @yields {LiquidationStep} */ async function* processTranches(seat, originalDebt) { - while (true) { + for await (const _ of forever) { const proceedsSoFar = seat.getAmountAllocated('Out'); const toSell = seat.getAmountAllocated('In'); if ( diff --git a/packages/internal/package.json b/packages/internal/package.json index 5bd28d3f484..e8292d59c1a 100755 --- a/packages/internal/package.json +++ b/packages/internal/package.json @@ -22,7 +22,8 @@ "dependencies": { "@endo/eventual-send": "^0.16.8", "@endo/marshal": "^0.8.1", - "@endo/promise-kit": "^0.2.52" + "@endo/promise-kit": "^0.2.52", + "jessie.js": "^0.3.2" }, "devDependencies": { "@endo/init": "^0.5.52", diff --git a/packages/internal/src/utils.js b/packages/internal/src/utils.js index fac4b1ff47a..c6474092f5c 100644 --- a/packages/internal/src/utils.js +++ b/packages/internal/src/utils.js @@ -2,6 +2,7 @@ import { E } from '@endo/eventual-send'; import { deeplyFulfilled, isObject } from '@endo/marshal'; import { isPromise } from '@endo/promise-kit'; +import { asyncGenerate } from 'jessie.js'; /** @typedef {import('@endo/marshal/src/types').Remotable} Remotable */ @@ -409,3 +410,32 @@ export const assertAllDefined = obj => { Fail`missing ${q(missing)}`; } }; + +const neverDone = harden({ done: false, value: null }); + +/** @type {AsyncIterable} */ +export const forever = asyncGenerate(() => neverDone); + +/** + * @param {() => boolean} boolFunc + * @returns {AsyncIterable} + */ +export const whileTrue = boolFunc => + asyncGenerate(() => + harden({ + done: !boolFunc(), + value: null, + }), + ); + +/** + * @param {() => boolean} boolFunc + * @returns {AsyncIterable} + */ +export const untilTrue = boolFunc => + asyncGenerate(() => + harden({ + done: !!boolFunc(), + value: null, + }), + ); diff --git a/packages/smart-wallet/src/offers.js b/packages/smart-wallet/src/offers.js index ec7503849e0..d9bede8bb44 100644 --- a/packages/smart-wallet/src/offers.js +++ b/packages/smart-wallet/src/offers.js @@ -86,7 +86,7 @@ export const makeOfferExecutor = ({ }); }; - try { + const tryBody = async () => { // 1. Prepare values and validate synchronously. const { id, invitationSpec, proposal, offerArgs } = offerSpec; @@ -177,9 +177,8 @@ export const makeOfferExecutor = ({ }), handleError, ); - } catch (err) { - handleError(err); - } + }; + await tryBody().catch(err => handleError(err)); }, }; }; diff --git a/packages/vats/src/vat-bank.js b/packages/vats/src/vat-bank.js index df8b5e261d4..5719ca7bbcf 100644 --- a/packages/vats/src/vat-bank.js +++ b/packages/vats/src/vat-bank.js @@ -4,6 +4,7 @@ import { AmountMath, AssetKind } from '@agoric/ertp'; import { E, Far } from '@endo/far'; import { makeNotifierKit, makeSubscriptionKit } from '@agoric/notifier'; import { makeStore, makeWeakStore } from '@agoric/store'; +import { whileTrue } from '@agoric/internal'; import { makeVirtualPurse } from './virtual-purse.js'; import '@agoric/notifier/exported.js'; @@ -40,7 +41,7 @@ const makePurseController = ( async *getBalances(b) { assert.equal(b, brand); let updateRecord = await balanceNotifier.getUpdateSince(); - while (updateRecord.updateCount) { + for await (const _ of whileTrue(() => !!updateRecord.updateCount)) { yield updateRecord.value; // eslint-disable-next-line no-await-in-loop updateRecord = await balanceNotifier.getUpdateSince( diff --git a/packages/web-components/src/admin-websocket-connector.js b/packages/web-components/src/admin-websocket-connector.js index 68f6f6eb61a..4a8fef5ce5c 100644 --- a/packages/web-components/src/admin-websocket-connector.js +++ b/packages/web-components/src/admin-websocket-connector.js @@ -1,5 +1,6 @@ /* eslint-disable no-underscore-dangle */ import { E } from '@endo/eventual-send'; +import { whileTrue } from '@agoric/internal'; const CONNECTION_TIMEOUT_MS = 5000; @@ -8,7 +9,7 @@ export const waitForBootstrap = async getBootstrap => { const getLoadingUpdate = (...args) => E(E.get(getBootstrap()).loadingNotifier).getUpdateSince(...args); let update = await getLoadingUpdate(); - while (update.value.includes('wallet')) { + for await (const _ of whileTrue(() => update.value.includes('wallet'))) { console.log('waiting for wallet'); // eslint-disable-next-line no-await-in-loop update = await getLoadingUpdate(update.updateCount); diff --git a/packages/xsnap/package.json b/packages/xsnap/package.json index fcdfc18695b..f6555c39d19 100644 --- a/packages/xsnap/package.json +++ b/packages/xsnap/package.json @@ -28,6 +28,7 @@ }, "dependencies": { "@agoric/assert": "^0.5.1", + "@agoric/internal": "^0.2.1", "@endo/bundle-source": "^2.4.2", "@endo/eventual-send": "^0.16.8", "@endo/init": "^0.5.52", diff --git a/packages/xsnap/src/replay.js b/packages/xsnap/src/replay.js index 29cd0069017..36e25ca3964 100644 --- a/packages/xsnap/src/replay.js +++ b/packages/xsnap/src/replay.js @@ -208,7 +208,7 @@ export async function replayXSnap( */ async function runSteps(rd, steps) { const folder = rd.path; - for (const step of steps) { + for await (const step of steps) { const parts = step.match(/(\d+)-([a-zA-Z]+)\.(dat|json)$/); if (!parts) { throw Error(`expected 0001-abc.dat; got: ${step}`); @@ -217,14 +217,12 @@ export async function replayXSnap( const seq = parseInt(digits, 10); console.log(folder, seq, kind); if (running && !['command', 'reply'].includes(kind)) { - // eslint-disable-next-line no-await-in-loop await running; running = undefined; } const file = rd.file(step); switch (kind) { case 'isReady': - // eslint-disable-next-line no-await-in-loop await it.isReady(); break; case 'evaluate': @@ -245,7 +243,6 @@ export async function replayXSnap( return; } else { try { - // eslint-disable-next-line no-await-in-loop await it.snapshot(file.getText()); } catch (err) { console.warn(err, 'while taking snapshot:', err); diff --git a/packages/xsnap/src/xsnap.js b/packages/xsnap/src/xsnap.js index e9e751074e2..203fdb6225b 100644 --- a/packages/xsnap/src/xsnap.js +++ b/packages/xsnap/src/xsnap.js @@ -13,6 +13,7 @@ import { makeNetstringReader, makeNetstringWriter } from '@endo/netstring'; import { makeNodeReader, makeNodeWriter } from '@endo/stream-node'; import { racePromises } from '@endo/promise-kit'; +import { forever } from '@agoric/internal'; import { ErrorCode, ErrorSignal, ErrorMessage, METER_TYPE } from '../api.js'; import { defer } from './defer.js'; @@ -166,7 +167,7 @@ export function xsnap(options) { * @returns {Promise>} */ async function runToIdle() { - for (;;) { + for await (const _ of forever) { const iteration = await messagesFromXsnap.next(undefined); if (iteration.done) { xsnapProcess.kill(); @@ -215,6 +216,7 @@ export function xsnap(options) { ); } } + throw Error(`unreachable, but tools don't know that`); } /** diff --git a/packages/zoe/src/contracts/callSpread/pricedCallSpread.js b/packages/zoe/src/contracts/callSpread/pricedCallSpread.js index 71315660ad5..166530a241f 100644 --- a/packages/zoe/src/contracts/callSpread/pricedCallSpread.js +++ b/packages/zoe/src/contracts/callSpread/pricedCallSpread.js @@ -99,8 +99,9 @@ const start = zcf => { const option = payoffHandler.makeOptionInvitation(position); const invitationIssuer = zcf.getInvitationIssuer(); const payment = harden({ Option: option }); + const Option = await E(invitationIssuer).getAmountOf(option); const spreadAmount = harden({ - Option: await E(invitationIssuer).getAmountOf(option), + Option, }); // AWAIT //// From 453db21a10015c49afed5371e5807940f7e1d352 Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Mon, 2 Jan 2023 20:55:53 -0800 Subject: [PATCH 2/2] fix: review suggestions --- packages/agoric-cli/src/install.js | 12 +++++------ packages/agoric-cli/src/start.js | 26 +++++++++++------------ packages/cosmic-swingset/src/sim-chain.js | 11 +++++----- packages/internal/src/utils.js | 10 +++++++-- packages/vats/src/vat-bank.js | 2 +- 5 files changed, 34 insertions(+), 27 deletions(-) diff --git a/packages/agoric-cli/src/install.js b/packages/agoric-cli/src/install.js index e5f77f04f77..5650f10514a 100644 --- a/packages/agoric-cli/src/install.js +++ b/packages/agoric-cli/src/install.js @@ -226,14 +226,14 @@ export default async function installMain(progname, rawArgs, powers, opts) { } } } else { - const subdirMapper = async subd => { + const subdirPackageJsonExists = async subd => { const exists = await fs.stat(`${subd}/package.json`).catch(_ => false); return exists && subd; }; const existingSubdirs = await Promise.all( ['.', '_agstate/agoric-servers', 'contract', 'api', 'ui'] .sort() - .map(subdirMapper), + .map(subdirPackageJsonExists), ); subdirs = existingSubdirs.filter(subd => subd); } @@ -253,7 +253,7 @@ export default async function installMain(progname, rawArgs, powers, opts) { await fs.symlink(sdkRoot, sdkWorktree); } - const subdirMapper = async subdir => { + const removeNodeModulesSymlinks = async subdir => { const nm = `${subdir}/node_modules`; log(chalk.bold.green(`removing ${nm} link`)); await fs.unlink(nm).catch(_ => {}); @@ -267,7 +267,7 @@ export default async function installMain(progname, rawArgs, powers, opts) { ), ); }; - await Promise.all(subdirs.map(subdirMapper)); + await Promise.all(subdirs.map(removeNodeModulesSymlinks)); } else { DEFAULT_SDK_PACKAGE_NAMES.forEach(name => sdkPackageToPath.set(name, null)); } @@ -310,12 +310,12 @@ export default async function installMain(progname, rawArgs, powers, opts) { const sdkPackages = [...sdkPackageToPath.keys()].sort(); for await (const subdir of subdirs) { const exists = await fs.stat(`${subdir}/package.json`).catch(_ => false); - const flag = await (exists && + const exitStatus = await (exists && pspawn('yarn', [...linkFlags, 'link', ...sdkPackages], { stdio: 'inherit', cwd: subdir, })); - if (flag) { + if (exitStatus) { log.error('Cannot yarn link', ...sdkPackages); return 1; } diff --git a/packages/agoric-cli/src/start.js b/packages/agoric-cli/src/start.js index 3169f4fb48f..8dcdc78f0ac 100644 --- a/packages/agoric-cli/src/start.js +++ b/packages/agoric-cli/src/start.js @@ -128,17 +128,17 @@ export default async function startMain(progname, rawArgs, powers, opts) { } const capture = (spawner, args, show = false) => { - const capret = [ + const statusOut = [ spawner(args, { stdio: ['inherit', 'pipe', 'inherit'] }), '', ]; - capret[0].childProcess.stdout.on('data', chunk => { + statusOut[0].childProcess.stdout.on('data', chunk => { if (show) { process.stdout.write(chunk); } - capret[1] += chunk.toString('utf-8'); + statusOut[1] += chunk.toString('utf-8'); }); - return capret; + return statusOut; }; const showKey = keyName => @@ -309,9 +309,9 @@ export default async function startMain(progname, rawArgs, powers, opts) { const addrs = {}; for (const keyName of ['provision', 'delegate0']) { /* eslint-disable no-await-in-loop */ - let capret = showKey(keyName); - const capretZero = await capret[0]; - if (capretZero) { + let statusOut = showKey(keyName); + const exitStatusOut = await statusOut[0]; + if (exitStatusOut) { const exitStatus = await keysSpawn([ 'keys', 'add', @@ -321,13 +321,13 @@ export default async function startMain(progname, rawArgs, powers, opts) { if (exitStatus) { return exitStatus; } - capret = showKey(keyName); - const status2 = await capret[0]; + statusOut = showKey(keyName); + const status2 = await statusOut[0]; if (status2) { return status2; } } - addrs[keyName] = capret[1].trimRight(); + addrs[keyName] = statusOut[1].trimRight(); /* eslint-enable no-await-in-loop */ } @@ -638,11 +638,11 @@ export default async function startMain(progname, rawArgs, powers, opts) { ], ]; for (/* await */ const cmd of provCmds) { - const capret = capture(keysSpawn, cmd, true); + const statusOut = capture(keysSpawn, cmd, true); // eslint-disable-next-line no-await-in-loop - exitStatus = await capret[0]; + exitStatus = await statusOut[0]; if (!exitStatus) { - const json = capret[1].replace(/^gas estimate: \d+$/m, ''); + const json = statusOut[1].replace(/^gas estimate: \d+$/m, ''); try { const ret = JSON.parse(json); if (ret.code !== 0) { diff --git a/packages/cosmic-swingset/src/sim-chain.js b/packages/cosmic-swingset/src/sim-chain.js index 37bf4b1da65..32c69d6b055 100644 --- a/packages/cosmic-swingset/src/sim-chain.js +++ b/packages/cosmic-swingset/src/sim-chain.js @@ -38,13 +38,14 @@ async function makeMapStorage(file) { await fs.promises.writeFile(file, json); }; - let obj = {}; await (async () => { content = await fs.promises.readFile(file); - obj = JSON.parse(content); - })().catch(() => map); - - Object.entries(obj).forEach(([k, v]) => map.set(k, importMailbox(v))); + return JSON.parse(content); + })().then( + obj => + Object.entries(obj).forEach(([k, v]) => map.set(k, importMailbox(v))), + () => {}, + ); return map; } diff --git a/packages/internal/src/utils.js b/packages/internal/src/utils.js index c6474092f5c..cbf0c93f52e 100644 --- a/packages/internal/src/utils.js +++ b/packages/internal/src/utils.js @@ -417,7 +417,10 @@ const neverDone = harden({ done: false, value: null }); export const forever = asyncGenerate(() => neverDone); /** - * @param {() => boolean} boolFunc + * @param {() => unknown} boolFunc + * `boolFunc`'s return value is used for its truthiness vs falsiness. + * IOW, it is coerced to a boolean so the caller need not bother doing this + * themselves. * @returns {AsyncIterable} */ export const whileTrue = boolFunc => @@ -429,7 +432,10 @@ export const whileTrue = boolFunc => ); /** - * @param {() => boolean} boolFunc + * @param {() => unknown} boolFunc + * `boolFunc`'s return value is used for its truthiness vs falsiness. + * IOW, it is coerced to a boolean so the caller need not bother doing this + * themselves. * @returns {AsyncIterable} */ export const untilTrue = boolFunc => diff --git a/packages/vats/src/vat-bank.js b/packages/vats/src/vat-bank.js index 5719ca7bbcf..c83b9a45d8b 100644 --- a/packages/vats/src/vat-bank.js +++ b/packages/vats/src/vat-bank.js @@ -41,7 +41,7 @@ const makePurseController = ( async *getBalances(b) { assert.equal(b, brand); let updateRecord = await balanceNotifier.getUpdateSince(); - for await (const _ of whileTrue(() => !!updateRecord.updateCount)) { + for await (const _ of whileTrue(() => updateRecord.updateCount)) { yield updateRecord.value; // eslint-disable-next-line no-await-in-loop updateRecord = await balanceNotifier.getUpdateSince(