From 0767f72848fdaf17deae4f939e52b693391e37b1 Mon Sep 17 00:00:00 2001 From: anilhelvaci Date: Tue, 5 Nov 2024 18:34:21 +0300 Subject: [PATCH 1/4] chore(pp-upgrade): create and set a new bridgeHandler in the proposal Refs: https://github.com/Agoric/agoric-sdk/issues/10395 --- golang/cosmos/app/upgrade.go | 6 +++ .../upgrade-provisionPool-proposal.js | 44 ++++++++++++++++++- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/golang/cosmos/app/upgrade.go b/golang/cosmos/app/upgrade.go index 8770675d792..9f81d6bb0ca 100644 --- a/golang/cosmos/app/upgrade.go +++ b/golang/cosmos/app/upgrade.go @@ -212,6 +212,12 @@ func unreleasedUpgradeHandler(app *GaiaApp, targetUpgrade string) func(sdk.Conte // Upgrade to include a cleanup from https://github.com/Agoric/agoric-sdk/pull/10319 "@agoric/builders/scripts/smart-wallet/build-wallet-factory2-upgrade.js", ), + vm.CoreProposalStepForModules( + "@agoric/builders/scripts/vats/upgrade-provisionPool.js", + ), + vm.CoreProposalStepForModules( + "@agoric/builders/scripts/vats/upgrade-bank.js", + ), ) // // CoreProposals for Upgrade 19. These should not be introduced diff --git a/packages/vats/src/proposals/upgrade-provisionPool-proposal.js b/packages/vats/src/proposals/upgrade-provisionPool-proposal.js index 9d31c752607..4dd74af9c69 100644 --- a/packages/vats/src/proposals/upgrade-provisionPool-proposal.js +++ b/packages/vats/src/proposals/upgrade-provisionPool-proposal.js @@ -16,6 +16,10 @@ export const upgradeProvisionPool = async ( economicCommitteeCreatorFacet: electorateCreatorFacet, instancePrivateArgs: instancePrivateArgsP, provisionPoolStartResult: provisionPoolStartResultP, + bankManager, + namesByAddressAdmin: namesByAddressAdminP, + walletFactoryStartResult: walletFactoryStartResultP, + provisionWalletBridgeManager: provisionWalletBridgeManagerP, }, }, options, @@ -25,11 +29,25 @@ export const upgradeProvisionPool = async ( assert(provisionPoolRef.bundleID); console.log(`PROVISION POOL BUNDLE ID: `, provisionPoolRef.bundleID); - const [provisionPoolStartResult, instancePrivateArgs] = await Promise.all([ + const [ + provisionPoolStartResult, + instancePrivateArgs, + namesByAddressAdmin, + walletFactoryStartResult, + provisionWalletBridgeManager, + ] = await Promise.all([ provisionPoolStartResultP, instancePrivateArgsP, + namesByAddressAdminP, + walletFactoryStartResultP, + provisionWalletBridgeManagerP, ]); - const { adminFacet, instance } = provisionPoolStartResult; + const { + adminFacet, + instance, + creatorFacet: ppCreatorFacet, + } = provisionPoolStartResult; + const { creatorFacet: wfCreatorFacet } = walletFactoryStartResult; const [originalPrivateArgs, poserInvitation] = await Promise.all([ // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -49,6 +67,24 @@ export const upgradeProvisionPool = async ( ); console.log('ProvisionPool upgraded: ', upgradeResult); + + const references = { + bankManager, + namesByAddressAdmin, + walletFactory: wfCreatorFacet, + }; + + console.log('Calling setReferences with: ', references); + await E(ppCreatorFacet).setReferences(references); + + console.log('Creating bridgeHandler...'); + const bridgeHandler = await E(ppCreatorFacet).makeHandler(); + + console.log('Setting new bridgeHandler...'); + // @ts-expect-error casting + await E(provisionWalletBridgeManager).setHandler(bridgeHandler); + + console.log('Done.'); }; export const getManifestForUpgradingProvisionPool = ( @@ -61,6 +97,10 @@ export const getManifestForUpgradingProvisionPool = ( economicCommitteeCreatorFacet: true, instancePrivateArgs: true, provisionPoolStartResult: true, + bankManager: true, + namesByAddressAdmin: true, + walletFactoryStartResult: true, + provisionWalletBridgeManager: true, }, produce: {}, }, From c8916b25289540ccd92c81a11fb4ae8c2efb9bac Mon Sep 17 00:00:00 2001 From: anilhelvaci Date: Wed, 6 Nov 2024 16:43:15 +0300 Subject: [PATCH 2/4] chore(pp-upgrade): create helper core-evals Refs: https://github.com/Agoric/agoric-sdk/issues/10395 --- .../deposit-usd-lemons-permit.json | 8 +++ .../depositUSD-LEMONS/deposit-usd-lemons.js | 49 +++++++++++++++++++ .../scripts/testing/add-USD-LEMONS.js | 19 +++++++ 3 files changed, 76 insertions(+) create mode 100644 a3p-integration/proposals/p:upgrade-19/depositUSD-LEMONS/deposit-usd-lemons-permit.json create mode 100644 a3p-integration/proposals/p:upgrade-19/depositUSD-LEMONS/deposit-usd-lemons.js create mode 100644 packages/builders/scripts/testing/add-USD-LEMONS.js diff --git a/a3p-integration/proposals/p:upgrade-19/depositUSD-LEMONS/deposit-usd-lemons-permit.json b/a3p-integration/proposals/p:upgrade-19/depositUSD-LEMONS/deposit-usd-lemons-permit.json new file mode 100644 index 00000000000..0a580519276 --- /dev/null +++ b/a3p-integration/proposals/p:upgrade-19/depositUSD-LEMONS/deposit-usd-lemons-permit.json @@ -0,0 +1,8 @@ +{ + "consume": { + "contractKits": true, + "namesByAddressAdmin": true, + "agoricNames": true + } +} + diff --git a/a3p-integration/proposals/p:upgrade-19/depositUSD-LEMONS/deposit-usd-lemons.js b/a3p-integration/proposals/p:upgrade-19/depositUSD-LEMONS/deposit-usd-lemons.js new file mode 100644 index 00000000000..2d9a6f0650f --- /dev/null +++ b/a3p-integration/proposals/p:upgrade-19/depositUSD-LEMONS/deposit-usd-lemons.js @@ -0,0 +1,49 @@ +const PROVISIONING_POOL_ADDR = 'agoric1megzytg65cyrgzs6fvzxgrcqvwwl7ugpt62346'; + +const depositUsdLemons = async powers => { + const { + consume: { contractKits: contractKitsP, namesByAddressAdmin: namesByAddressAdminP , agoricNames }, + // instance: { consume: { ['psm-IST-USD_LEMONS']: usdLemonsPsmInstanceP }} + } = powers; + + const namesByAddressAdmin = await namesByAddressAdminP; + + const getDepositFacet = async address => { + const admin = await E(namesByAddressAdmin).lookupAdmin(address); + console.log('ADMIN', admin) + + const nameHub = await E(admin).readonly(); + console.log('NAME_HUB', nameHub); + const hub = E(E(namesByAddressAdmin).lookupAdmin(address)).readonly(); + return E(hub).lookup('depositFacet'); + }; + + const [contractKits, usdLemonsIssuer, usdLemonsBrand, ppDepositFacet] = await Promise.all([ + contractKitsP, + E(agoricNames).lookup('issuer', 'USD_LEMONS'), + E(agoricNames).lookup('brand', 'USD_LEMONS'), + getDepositFacet(PROVISIONING_POOL_ADDR), + ]); + + console.log('[CONTRACT_KITS]', contractKits); + console.log('[ISSUER]', usdLemonsIssuer); + + let usdLemonsMint; + for (const { publicFacet, creatorFacet: mint } of contractKits.values()) { + if (publicFacet === usdLemonsIssuer) { + usdLemonsMint = mint; + console.log('BINGO', mint) + break; + } + } + + console.log('Minting USD_LEMONS'); + const helloPayment = await E(usdLemonsMint).mintPayment(harden({ brand: usdLemonsBrand, value: 500000n })); + + console.log('Funding provision pool...'); + await E(ppDepositFacet).receive(helloPayment); + + console.log('Done.'); +}; + +depositUsdLemons; diff --git a/packages/builders/scripts/testing/add-USD-LEMONS.js b/packages/builders/scripts/testing/add-USD-LEMONS.js new file mode 100644 index 00000000000..89afc1279cb --- /dev/null +++ b/packages/builders/scripts/testing/add-USD-LEMONS.js @@ -0,0 +1,19 @@ +import { makeHelpers } from '@agoric/deploy-script-support'; +import { psmProposalBuilder } from '../inter-protocol/add-collateral-core.js'; + +const addUsdLemonsProposalBuilder = async powers => { + return psmProposalBuilder(powers, { + anchorOptions: { + denom: 'ibc/000C0AAAEECAFE000', + keyword: 'USD_LEMONS', + decimalPlaces: 6, + proposedName: 'USD_LEMONS', + } + }); +}; + +/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').DeployScriptFunction} */ +export default async (homeP, endowments) => { + const { writeCoreEval } = await makeHelpers(homeP, endowments); + await writeCoreEval('add-LEMONS-PSM', addUsdLemonsProposalBuilder); + }; \ No newline at end of file From bf6c1ffa75885fc1d2a7901b2f36977544aaf476 Mon Sep 17 00:00:00 2001 From: anilhelvaci Date: Thu, 7 Nov 2024 11:29:24 +0300 Subject: [PATCH 3/4] chore(pp-upgrade): test functionality after provisionPool and bank upgrade Refs: https://github.com/Agoric/agoric-sdk/issues/10395 fix: format fixes --- .../proposals/p:upgrade-19/.gitignore | 1 + .../depositUSD-LEMONS/deposit-usd-lemons.js | 32 ++- .../proposals/p:upgrade-19/package.json | 1 + .../p:upgrade-19/provisionPool.test.js | 129 +++++++++ .../proposals/p:upgrade-19/test-lib/errors.js | 20 ++ .../p:upgrade-19/test-lib/sync-tools.js | 272 ++++++++++++++++++ .../proposals/p:upgrade-19/test.sh | 2 + golang/cosmos/app/upgrade.go | 38 +-- .../scripts/testing/add-USD-LEMONS.js | 22 +- 9 files changed, 472 insertions(+), 45 deletions(-) create mode 100644 a3p-integration/proposals/p:upgrade-19/provisionPool.test.js create mode 100644 a3p-integration/proposals/p:upgrade-19/test-lib/errors.js create mode 100644 a3p-integration/proposals/p:upgrade-19/test-lib/sync-tools.js diff --git a/a3p-integration/proposals/p:upgrade-19/.gitignore b/a3p-integration/proposals/p:upgrade-19/.gitignore index 17bb7d663e2..d348dadf7d9 100644 --- a/a3p-integration/proposals/p:upgrade-19/.gitignore +++ b/a3p-integration/proposals/p:upgrade-19/.gitignore @@ -1,2 +1,3 @@ replaceFeeDistributor/ testUpgradedBoard/ +addUsdLemons/ diff --git a/a3p-integration/proposals/p:upgrade-19/depositUSD-LEMONS/deposit-usd-lemons.js b/a3p-integration/proposals/p:upgrade-19/depositUSD-LEMONS/deposit-usd-lemons.js index 2d9a6f0650f..4a4a3c0313c 100644 --- a/a3p-integration/proposals/p:upgrade-19/depositUSD-LEMONS/deposit-usd-lemons.js +++ b/a3p-integration/proposals/p:upgrade-19/depositUSD-LEMONS/deposit-usd-lemons.js @@ -1,29 +1,29 @@ +/* eslint-disable no-undef */ const PROVISIONING_POOL_ADDR = 'agoric1megzytg65cyrgzs6fvzxgrcqvwwl7ugpt62346'; const depositUsdLemons = async powers => { const { - consume: { contractKits: contractKitsP, namesByAddressAdmin: namesByAddressAdminP , agoricNames }, - // instance: { consume: { ['psm-IST-USD_LEMONS']: usdLemonsPsmInstanceP }} + consume: { + contractKits: contractKitsP, + namesByAddressAdmin: namesByAddressAdminP, + agoricNames, + }, } = powers; const namesByAddressAdmin = await namesByAddressAdminP; const getDepositFacet = async address => { - const admin = await E(namesByAddressAdmin).lookupAdmin(address); - console.log('ADMIN', admin) - - const nameHub = await E(admin).readonly(); - console.log('NAME_HUB', nameHub); const hub = E(E(namesByAddressAdmin).lookupAdmin(address)).readonly(); return E(hub).lookup('depositFacet'); }; - const [contractKits, usdLemonsIssuer, usdLemonsBrand, ppDepositFacet] = await Promise.all([ - contractKitsP, - E(agoricNames).lookup('issuer', 'USD_LEMONS'), - E(agoricNames).lookup('brand', 'USD_LEMONS'), - getDepositFacet(PROVISIONING_POOL_ADDR), - ]); + const [contractKits, usdLemonsIssuer, usdLemonsBrand, ppDepositFacet] = + await Promise.all([ + contractKitsP, + E(agoricNames).lookup('issuer', 'USD_LEMONS'), + E(agoricNames).lookup('brand', 'USD_LEMONS'), + getDepositFacet(PROVISIONING_POOL_ADDR), + ]); console.log('[CONTRACT_KITS]', contractKits); console.log('[ISSUER]', usdLemonsIssuer); @@ -32,13 +32,15 @@ const depositUsdLemons = async powers => { for (const { publicFacet, creatorFacet: mint } of contractKits.values()) { if (publicFacet === usdLemonsIssuer) { usdLemonsMint = mint; - console.log('BINGO', mint) + console.log('BINGO', mint); break; } } console.log('Minting USD_LEMONS'); - const helloPayment = await E(usdLemonsMint).mintPayment(harden({ brand: usdLemonsBrand, value: 500000n })); + const helloPayment = await E(usdLemonsMint).mintPayment( + harden({ brand: usdLemonsBrand, value: 500000n }), + ); console.log('Funding provision pool...'); await E(ppDepositFacet).receive(helloPayment); diff --git a/a3p-integration/proposals/p:upgrade-19/package.json b/a3p-integration/proposals/p:upgrade-19/package.json index f3b062f3c76..9a18e4f658c 100644 --- a/a3p-integration/proposals/p:upgrade-19/package.json +++ b/a3p-integration/proposals/p:upgrade-19/package.json @@ -3,6 +3,7 @@ "type": "/agoric.swingset.CoreEvalProposal", "sdk-generate": [ "testing/replace-feeDistributor-short.js replaceFeeDistributor", + "testing/add-USD-LEMONS.js addUsdLemons", "vats/upgrade-paRegistry.js", "vats/upgrade-board.js", "testing/test-upgraded-board.js testUpgradedBoard" diff --git a/a3p-integration/proposals/p:upgrade-19/provisionPool.test.js b/a3p-integration/proposals/p:upgrade-19/provisionPool.test.js new file mode 100644 index 00000000000..df8511305d9 --- /dev/null +++ b/a3p-integration/proposals/p:upgrade-19/provisionPool.test.js @@ -0,0 +1,129 @@ +/* eslint-env node */ +/** + * @file The goal of this file is to make sure v28-provisionPool and v14-bank can be successfully + * upgraded. These vats are related because of the issues below; + * - https://github.com/Agoric/agoric-sdk/issues/8722 + * - https://github.com/Agoric/agoric-sdk/issues/8724 + * + * The test scenario is as follows; + * - Prerequisite: provisionPool and bank are already upgraded. See `upgrade.go` + * 1. Add a new account and successfully provision it + * - Observe new account's address under `published.wallet.${address}` + * 2. Send some USDC_axl to provisionPoolAddress and observe its IST balances increases accordingly + * 3. Introduce a new asset to the chain and start a PSM instance for the new asset + * 3a. Deposit some of that asset to provisionPoolAddress + * 3b. Observe provisionPoolAddress' IST balance increase by the amount deposited in step 3a + */ + +import '@endo/init'; +import test from 'ava'; +import { execFileSync } from 'node:child_process'; +import { + addUser, + makeAgd, + evalBundles, + agd as agdAmbient, + agoric, + bankSend, + getISTBalance, +} from '@agoric/synthetic-chain'; +import { NonNullish } from './test-lib/errors.js'; +import { + retryUntilCondition, + waitUntilAccountFunded, + waitUntilContractDeployed, +} from './test-lib/sync-tools.js'; + +const PROVISIONING_POOL_ADDR = 'agoric1megzytg65cyrgzs6fvzxgrcqvwwl7ugpt62346'; + +const ADD_PSM_DIR = 'addUsdLemons'; +const DEPOSIT_USD_LEMONS_DIR = 'depositUSD-LEMONS'; + +const USDC_DENOM = NonNullish(process.env.USDC_DENOM); + +const agd = makeAgd({ execFileSync }).withOpts({ keyringBackend: 'test' }); + +const ambientAuthority = { + query: agdAmbient.query, + follow: agoric.follow, + setTimeout, +}; + +const provision = (name, address) => + agd.tx(['swingset', 'provision-one', name, address, 'SMART_WALLET'], { + chainId: 'agoriclocal', + from: 'validator', + yes: true, + }); + +const introduceAndProvision = async name => { + const address = await addUser(name); + console.log('ADDR', name, address); + + const provisionP = provision(name, address); + + return { provisionP, address }; +}; + +const getProvisionedAddresses = async () => { + const { children } = await agd.query([ + 'vstorage', + 'children', + 'published.wallet', + ]); + return children; +}; + +const checkUserProvisioned = addr => + retryUntilCondition( + getProvisionedAddresses, + children => children.includes(addr), + 'Account not provisioned', + { maxRetries: 5, retryIntervalMs: 1000, log: console.log, setTimeout }, + ); + +test(`upgrade provision pool`, async t => { + // Introduce new user then provision + const { address } = await introduceAndProvision('provisionTester'); + await checkUserProvisioned(address); + + // Send USDC_axl to pp + const istBalanceBefore = await getISTBalance(PROVISIONING_POOL_ADDR); + await bankSend(PROVISIONING_POOL_ADDR, `500000${USDC_DENOM}`); + + // Check IST balance + await waitUntilAccountFunded( + PROVISIONING_POOL_ADDR, + ambientAuthority, + { denom: 'uist', value: istBalanceBefore + 500000 }, + { errorMessage: 'Provision pool not able to swap USDC_axl for IST.' }, + ); + + // Introduce USD_LEMONS + await evalBundles(ADD_PSM_DIR); + await waitUntilContractDeployed('psm-IST-USD_LEMONS', ambientAuthority, { + errorMessage: 'psm-IST-USD_LEMONS instance not observed.', + }); + + // Provision the provisionPoolAddress. This is a workaround of provisionPoolAddress + // not having a depositFacet published to namesByAddress. Shouldn't be a problem since + // vat-bank keeps track of virtual purses per address basis. We need there to be + // depositFacet for provisionPoolAddress since we'll fund it with USD_LEMONS + await provision('provisionPoolAddress', PROVISIONING_POOL_ADDR); + await checkUserProvisioned(PROVISIONING_POOL_ADDR); + + // Send USD_LEMONS to provisionPoolAddress + const istBalanceBeforeLemonsSent = await getISTBalance( + PROVISIONING_POOL_ADDR, + ); + await evalBundles(DEPOSIT_USD_LEMONS_DIR); + + // Check balance again + await waitUntilAccountFunded( + PROVISIONING_POOL_ADDR, + ambientAuthority, + { denom: 'uist', value: istBalanceBeforeLemonsSent + 500000 }, + { errorMessage: 'Provision pool not bale swap USDC_axl for IST.' }, + ); + t.pass(); +}); diff --git a/a3p-integration/proposals/p:upgrade-19/test-lib/errors.js b/a3p-integration/proposals/p:upgrade-19/test-lib/errors.js new file mode 100644 index 00000000000..57dc771e6a5 --- /dev/null +++ b/a3p-integration/proposals/p:upgrade-19/test-lib/errors.js @@ -0,0 +1,20 @@ +/** + * @file Copied from "@agoric/internal" + */ + +import { q } from '@endo/errors'; + +/** + * @template T + * @param {T | null | undefined} val + * @param {string} [optDetails] + * @returns {T} + */ +export const NonNullish = (val, optDetails = `unexpected ${q(val)}`) => { + if (val != null) { + // This `!= null` idiom checks that `val` is neither `null` nor `undefined`. + return val; + } + assert.fail(optDetails); +}; +harden(NonNullish); diff --git a/a3p-integration/proposals/p:upgrade-19/test-lib/sync-tools.js b/a3p-integration/proposals/p:upgrade-19/test-lib/sync-tools.js new file mode 100644 index 00000000000..dac2ba7e04f --- /dev/null +++ b/a3p-integration/proposals/p:upgrade-19/test-lib/sync-tools.js @@ -0,0 +1,272 @@ +/* eslint-env node */ + +/** + * @file The purpose of this file is to bring together a set of tools that + * developers can use to synchronize operations they carry out in their tests. + * + * These operations include; + * - Making sure a core-eval resulted in successfully deploying a contract + * - Making sure a core-eval successfully sent zoe invitations to committee members for governance + * - Making sure an account is successfully funded with vbank assets like IST, BLD etc. + * - operation: query dest account's balance + * - condition: dest account has a balance >= sent token + * - Making sure an offer resulted successfully + * + */ + +/** + * @typedef {object} RetryOptions + * @property {number} [maxRetries] + * @property {number} [retryIntervalMs] + * @property {(...arg0: string[]) => void} [log] + * @property {(callback: Function, delay: number) => void} [setTimeout] + * + * @typedef {RetryOptions & {errorMessage: string}} WaitUntilOptions + * + * @typedef {object} CosmosBalanceThreshold + * @property {string} denom + * @property {number} value + */ + +const ambientSetTimeout = global.setTimeout; + +/** + * From https://github.com/Agoric/agoric-sdk/blob/442f07c8f0af03281b52b90e90c27131eef6f331/multichain-testing/tools/sleep.ts#L10 + * + * @param {number} ms + * @param {*} sleepOptions + */ +export const sleep = (ms, { log = () => {}, setTimeout = ambientSetTimeout }) => + new Promise(resolve => { + log(`Sleeping for ${ms}ms...`); + setTimeout(resolve, ms); + }); + +/** + * From https://github.com/Agoric/agoric-sdk/blob/442f07c8f0af03281b52b90e90c27131eef6f331/multichain-testing/tools/sleep.ts#L24 + * + * @param {() => Promise} operation + * @param {(result: any) => boolean} condition + * @param {string} message + * @param {RetryOptions} options + */ +export const retryUntilCondition = async ( + operation, + condition, + message, + { maxRetries = 6, retryIntervalMs = 3500, log = console.log, setTimeout }, +) => { + console.log({ maxRetries, retryIntervalMs, message }); + let retries = 0; + + while (retries < maxRetries) { + try { + const result = await operation(); + log('RESULT', result); + if (condition(result)) { + return result; + } + } catch (error) { + if (error instanceof Error) { + log(`Error: ${error.message}`); + } else { + log(`Unknown error: ${String(error)}`); + } + } + + retries += 1; + console.log( + `Retry ${retries}/${maxRetries} - Waiting for ${retryIntervalMs}ms for ${message}...`, + ); + await sleep(retryIntervalMs, { log, setTimeout }); + } + + throw Error(`${message} condition failed after ${maxRetries} retries.`); +}; + +/** + * @param {WaitUntilOptions} options + */ +const overrideDefaultOptions = options => { + const defaultValues = { + maxRetries: 6, + retryIntervalMs: 3500, + log: console.log, + errorMessage: 'Error', + }; + + return { ...defaultValues, ...options }; +}; + +/// ////////// Making sure a core-eval resulted successfully deploying a contract ///////////// + +const makeGetInstances = follow => async () => { + const instanceEntries = await follow( + '-lF', + `:published.agoricNames.instance`, + ); + + return Object.fromEntries(instanceEntries); +}; + +/** + * + * @param {string} contractName + * @param {{follow: () => object, setTimeout: (object) => void}} ambientAuthority + * @param {WaitUntilOptions} options + */ +export const waitUntilContractDeployed = ( + contractName, + ambientAuthority, + options, +) => { + const { follow, setTimeout } = ambientAuthority; + const getInstances = makeGetInstances(follow); + const { maxRetries, retryIntervalMs, errorMessage, log } = + overrideDefaultOptions(options); + + return retryUntilCondition( + getInstances, + instanceObject => Object.keys(instanceObject).includes(contractName), + errorMessage, + { maxRetries, retryIntervalMs, log, setTimeout }, + ); +}; + +/// ////////// Making sure an account is successfully funded with vbank assets like IST, BLD etc. /////////////// + +const makeQueryCosmosBalance = queryCb => async dest => { + const coins = await queryCb('bank', 'balances', dest); + return coins.balances; +}; + +/** + * + * @param {Array} balances + * @param {CosmosBalanceThreshold} threshold + * @returns {boolean} + */ +const checkCosmosBalance = (balances, threshold) => { + const balance = [...balances].find(({ denom }) => denom === threshold.denom); + return Number(balance.amount) >= threshold.value; +}; + +/** + * @param {string} destAcct + * @param {{query: () => Promise, setTimeout: (object) => void}} ambientAuthority + * @param {{denom: string, value: number}} threshold + * @param {WaitUntilOptions} options + */ +export const waitUntilAccountFunded = ( + destAcct, + ambientAuthority, + threshold, + options, +) => { + const { query, setTimeout } = ambientAuthority; + const queryCosmosBalance = makeQueryCosmosBalance(query); + const { maxRetries, retryIntervalMs, errorMessage, log } = + overrideDefaultOptions(options); + + return retryUntilCondition( + async () => queryCosmosBalance(destAcct), + balances => checkCosmosBalance(balances, threshold), + errorMessage, + { maxRetries, retryIntervalMs, log, setTimeout }, + ); +}; + +/// ////////// Making sure an offers get results ///////////// + +const makeQueryWallet = follow => async (/** @type {string} */ addr) => { + const update = await follow('-lF', `:published.wallet.${addr}`); + + return update; +}; + +/** + * + * @param {object} offerStatus + * @param {boolean} waitForPayouts + * @param {string} offerId + */ +const checkOfferState = (offerStatus, waitForPayouts, offerId) => { + const { updated, status } = offerStatus; + + if (updated !== 'offerStatus') return false; + if (!status) return false; + if (status.id !== offerId) return false; + if (!status.numWantsSatisfied || status.numWantsSatisfied !== 1) return false; + if (waitForPayouts && status.payouts) return true; + if (!waitForPayouts && status.result) return true; + + return false; +}; + +/** + * + * @param {string} addr + * @param {string} offerId + * @param {boolean} waitForPayouts + * @param {{follow: () => object, setTimeout: (callback: Function, delay: number) => void}} ambientAuthority + * @param {WaitUntilOptions} options + */ +export const waitUntilOfferResult = ( + addr, + offerId, + waitForPayouts, + ambientAuthority, + options, +) => { + const { follow, setTimeout } = ambientAuthority; + const queryWallet = makeQueryWallet(follow); + const { maxRetries, retryIntervalMs, errorMessage, log } = + overrideDefaultOptions(options); + + return retryUntilCondition( + async () => queryWallet(addr), + status => checkOfferState(status, waitForPayouts, offerId), + errorMessage, + { maxRetries, retryIntervalMs, log, setTimeout }, + ); +}; + +/// ////////// Making sure a core-eval successfully sent zoe invitations to committee members for governance ///////////// + +/** + * + * @param {{ updated: string, currentAmount: any }} update + * @returns {boolean} + */ +const checkForInvitation = update => { + const { updated, currentAmount } = update; + + if (updated !== 'balance') return false; + if (!currentAmount || !currentAmount.brand) return false; + + return currentAmount.brand.includes('Invitation'); +}; + +/** + * + * @param {string} addr + * @param {{follow: () => object, setTimeout: (object) => void}} ambientAuthority + * @param {WaitUntilOptions} options + */ +export const waitUntilInvitationReceived = ( + addr, + ambientAuthority, + options, +) => { + const { follow, setTimeout } = ambientAuthority; + const queryWallet = makeQueryWallet(follow); + const { maxRetries, retryIntervalMs, errorMessage, log } = + overrideDefaultOptions(options); + + return retryUntilCondition( + async () => queryWallet(addr), + checkForInvitation, + errorMessage, + { maxRetries, retryIntervalMs, log, setTimeout }, + ); +}; diff --git a/a3p-integration/proposals/p:upgrade-19/test.sh b/a3p-integration/proposals/p:upgrade-19/test.sh index 056fe6836ac..055727518f8 100644 --- a/a3p-integration/proposals/p:upgrade-19/test.sh +++ b/a3p-integration/proposals/p:upgrade-19/test.sh @@ -2,3 +2,5 @@ yarn ava replaceFeeDistributor.test.js yarn ava upgradedBoard.test.js + +yarn ava provisionPool.test.js diff --git a/golang/cosmos/app/upgrade.go b/golang/cosmos/app/upgrade.go index 9f81d6bb0ca..2858370cf05 100644 --- a/golang/cosmos/app/upgrade.go +++ b/golang/cosmos/app/upgrade.go @@ -212,27 +212,27 @@ func unreleasedUpgradeHandler(app *GaiaApp, targetUpgrade string) func(sdk.Conte // Upgrade to include a cleanup from https://github.com/Agoric/agoric-sdk/pull/10319 "@agoric/builders/scripts/smart-wallet/build-wallet-factory2-upgrade.js", ), - vm.CoreProposalStepForModules( - "@agoric/builders/scripts/vats/upgrade-provisionPool.js", - ), - vm.CoreProposalStepForModules( - "@agoric/builders/scripts/vats/upgrade-bank.js", - ), ) - // // CoreProposals for Upgrade 19. These should not be introduced - // // before upgrade 18 is done because they would be run in n:upgrade-next - // CoreProposalSteps = append(CoreProposalSteps, - // vm.CoreProposalStepForModules( - // "@agoric/builders/scripts/inter-protocol/replace-feeDistributor.js", - // ), - // vm.CoreProposalStepForModules( - // "@agoric/builders/scripts/vats/upgrade-paRegistry.js", - // ), - // vm.CoreProposalStepForModules( - // "@agoric/builders/scripts/vats/upgrade-board.js", - // ), - // ) + // CoreProposals for Upgrade 19. These should not be introduced + // before upgrade 18 is done because they would be run in n:upgrade-next + // CoreProposalSteps = append(CoreProposalSteps, + // vm.CoreProposalStepForModules( + // "@agoric/builders/scripts/inter-protocol/replace-feeDistributor.js", + // ), + // vm.CoreProposalStepForModules( + // "@agoric/builders/scripts/vats/upgrade-paRegistry.js", + // ), + // vm.CoreProposalStepForModules( + // "@agoric/builders/scripts/vats/upgrade-board.js", + // ), + // vm.CoreProposalStepForModules( + // "@agoric/builders/scripts/vats/upgrade-provisionPool.js", + // ), + // vm.CoreProposalStepForModules( + // "@agoric/builders/scripts/vats/upgrade-bank.js", + // ), + // ) } app.upgradeDetails = &upgradeDetails{ diff --git a/packages/builders/scripts/testing/add-USD-LEMONS.js b/packages/builders/scripts/testing/add-USD-LEMONS.js index 89afc1279cb..ed548b5561c 100644 --- a/packages/builders/scripts/testing/add-USD-LEMONS.js +++ b/packages/builders/scripts/testing/add-USD-LEMONS.js @@ -2,18 +2,18 @@ import { makeHelpers } from '@agoric/deploy-script-support'; import { psmProposalBuilder } from '../inter-protocol/add-collateral-core.js'; const addUsdLemonsProposalBuilder = async powers => { - return psmProposalBuilder(powers, { - anchorOptions: { - denom: 'ibc/000C0AAAEECAFE000', - keyword: 'USD_LEMONS', - decimalPlaces: 6, - proposedName: 'USD_LEMONS', - } - }); + return psmProposalBuilder(powers, { + anchorOptions: { + denom: 'ibc/000C0AAAEECAFE000', + keyword: 'USD_LEMONS', + decimalPlaces: 6, + proposedName: 'USD_LEMONS', + }, + }); }; /** @type {import('@agoric/deploy-script-support/src/externalTypes.js').DeployScriptFunction} */ export default async (homeP, endowments) => { - const { writeCoreEval } = await makeHelpers(homeP, endowments); - await writeCoreEval('add-LEMONS-PSM', addUsdLemonsProposalBuilder); - }; \ No newline at end of file + const { writeCoreEval } = await makeHelpers(homeP, endowments); + await writeCoreEval('add-LEMONS-PSM', addUsdLemonsProposalBuilder); +}; From 164151ecf34ae08b217817e970da1c8d64f763cb Mon Sep 17 00:00:00 2001 From: anilhelvaci Date: Mon, 25 Nov 2024 23:31:46 +0300 Subject: [PATCH 4/4] chore: make provisionBridgeHandler durable Make sure the handler returned from E(creatorFacet).makeHandler() is durable and provisionPool is upgradable multiple times. Refs: #10425 Refs: #10564 fix: remove unnecessary comment chore: get rid of ephemera, address change requests Refs: #10395 Refs: #10425 fix: drop trace chore: address change requests fix: rebase fixes fix: yarn.lock fix fix: bring back missing upgrade-paRegistry proposal --- .../proposals/p:upgrade-19/.gitignore | 1 + .../depositUSD-LEMONS/deposit-usd-lemons.js | 1 + .../nullUpgradePP/null-upgrade-pp-permit.json | 7 + .../nullUpgradePP/null-upgrade-pp.js | 38 +++ .../proposals/p:upgrade-19/package.json | 2 + .../p:upgrade-19/provisionPool.test.js | 215 +++++++++----- .../proposals/p:upgrade-19/test-lib/errors.js | 20 -- .../test-lib/provision-helpers.js | 56 ++++ .../p:upgrade-19/test-lib/sync-tools.js | 272 ------------------ .../proposals/p:upgrade-19/tsconfig.json | 7 +- .../proposals/p:upgrade-19/yarn.lock | 53 +++- .../proposals/z:acceptance/yarn.lock | 1 + packages/inter-protocol/package.json | 2 +- packages/inter-protocol/src/provisionPool.js | 12 +- .../inter-protocol/src/provisionPoolKit.js | 134 +++++---- .../inter-protocol/test/provisionPool.test.js | 16 +- .../upgrade-provisionPool-proposal.js | 15 +- 17 files changed, 398 insertions(+), 454 deletions(-) create mode 100644 a3p-integration/proposals/p:upgrade-19/nullUpgradePP/null-upgrade-pp-permit.json create mode 100644 a3p-integration/proposals/p:upgrade-19/nullUpgradePP/null-upgrade-pp.js delete mode 100644 a3p-integration/proposals/p:upgrade-19/test-lib/errors.js create mode 100644 a3p-integration/proposals/p:upgrade-19/test-lib/provision-helpers.js delete mode 100644 a3p-integration/proposals/p:upgrade-19/test-lib/sync-tools.js diff --git a/a3p-integration/proposals/p:upgrade-19/.gitignore b/a3p-integration/proposals/p:upgrade-19/.gitignore index d348dadf7d9..80f57c98cb2 100644 --- a/a3p-integration/proposals/p:upgrade-19/.gitignore +++ b/a3p-integration/proposals/p:upgrade-19/.gitignore @@ -1,3 +1,4 @@ replaceFeeDistributor/ testUpgradedBoard/ addUsdLemons/ +upgradeProvisionPool/ diff --git a/a3p-integration/proposals/p:upgrade-19/depositUSD-LEMONS/deposit-usd-lemons.js b/a3p-integration/proposals/p:upgrade-19/depositUSD-LEMONS/deposit-usd-lemons.js index 4a4a3c0313c..c7ee0db948f 100644 --- a/a3p-integration/proposals/p:upgrade-19/depositUSD-LEMONS/deposit-usd-lemons.js +++ b/a3p-integration/proposals/p:upgrade-19/depositUSD-LEMONS/deposit-usd-lemons.js @@ -1,3 +1,4 @@ +// @ts-nocheck /* eslint-disable no-undef */ const PROVISIONING_POOL_ADDR = 'agoric1megzytg65cyrgzs6fvzxgrcqvwwl7ugpt62346'; diff --git a/a3p-integration/proposals/p:upgrade-19/nullUpgradePP/null-upgrade-pp-permit.json b/a3p-integration/proposals/p:upgrade-19/nullUpgradePP/null-upgrade-pp-permit.json new file mode 100644 index 00000000000..668a9c7f0d5 --- /dev/null +++ b/a3p-integration/proposals/p:upgrade-19/nullUpgradePP/null-upgrade-pp-permit.json @@ -0,0 +1,7 @@ +{ + "consume": { + "provisionPoolStartResult": true, + "instancePrivateArgs": true, + "economicCommitteeCreatorFacet": true + } +} diff --git a/a3p-integration/proposals/p:upgrade-19/nullUpgradePP/null-upgrade-pp.js b/a3p-integration/proposals/p:upgrade-19/nullUpgradePP/null-upgrade-pp.js new file mode 100644 index 00000000000..be3fdc96b3d --- /dev/null +++ b/a3p-integration/proposals/p:upgrade-19/nullUpgradePP/null-upgrade-pp.js @@ -0,0 +1,38 @@ +// @ts-nocheck +/* eslint-disable no-undef */ +const nullUpgradePP = async powers => { + const { + consume: { + provisionPoolStartResult: provisionPoolStartResultP, + instancePrivateArgs: instancePrivateArgsP, + economicCommitteeCreatorFacet, + }, + } = powers; + + console.log('awaiting powers'); + const { adminFacet, instance } = await provisionPoolStartResultP; + const instancePrivateArgs = await instancePrivateArgsP; + + console.log('get privateArgs'); + const privateArgs = instancePrivateArgs.get(instance); + const [poolBank, poserInvitation] = await Promise.all([ + privateArgs.poolBank, + E(economicCommitteeCreatorFacet).getPoserInvitation(), + ]); + + console.log('DEBUG', { + adminFacet, + instance, + privateArgs, + poserInvitation, + }); + + await E(adminFacet).restartContract({ + ...privateArgs, + poolBank, + initialPoserInvitation: poserInvitation, + }); + console.log('Done'); +}; + +nullUpgradePP; diff --git a/a3p-integration/proposals/p:upgrade-19/package.json b/a3p-integration/proposals/p:upgrade-19/package.json index 9a18e4f658c..6848af6d060 100644 --- a/a3p-integration/proposals/p:upgrade-19/package.json +++ b/a3p-integration/proposals/p:upgrade-19/package.json @@ -4,6 +4,7 @@ "sdk-generate": [ "testing/replace-feeDistributor-short.js replaceFeeDistributor", "testing/add-USD-LEMONS.js addUsdLemons", + "vats/upgrade-provisionPool.js upgradeProvisionPool", "vats/upgrade-paRegistry.js", "vats/upgrade-board.js", "testing/test-upgraded-board.js testUpgradedBoard" @@ -14,6 +15,7 @@ "dependencies": { "@agoric/client-utils": "0.1.1-dev-02c06c4.0", "@agoric/ertp": "dev", + "@agoric/internal": "dev", "@agoric/synthetic-chain": "^0.4.3", "@agoric/zoe": "dev", "@endo/errors": "1.2.7", diff --git a/a3p-integration/proposals/p:upgrade-19/provisionPool.test.js b/a3p-integration/proposals/p:upgrade-19/provisionPool.test.js index df8511305d9..12934603921 100644 --- a/a3p-integration/proposals/p:upgrade-19/provisionPool.test.js +++ b/a3p-integration/proposals/p:upgrade-19/provisionPool.test.js @@ -6,124 +6,181 @@ * - https://github.com/Agoric/agoric-sdk/issues/8724 * * The test scenario is as follows; - * - Prerequisite: provisionPool and bank are already upgraded. See `upgrade.go` - * 1. Add a new account and successfully provision it - * - Observe new account's address under `published.wallet.${address}` - * 2. Send some USDC_axl to provisionPoolAddress and observe its IST balances increases accordingly - * 3. Introduce a new asset to the chain and start a PSM instance for the new asset - * 3a. Deposit some of that asset to provisionPoolAddress - * 3b. Observe provisionPoolAddress' IST balance increase by the amount deposited in step 3a + * 1. Upgrade provisionPool. This upgrade overrides provisionWalletBridgerManager with a durable one + * 2. Add a new account and successfully provision it + * - Observe new account's address under `published.wallet.${address}` + * 3. Send some USDC_axl to provisionPoolAddress and observe its IST balances increases accordingly + * 4. Introduce a new asset to the chain and start a PSM instance for the new asset + * 4a. Deposit some of that asset to provisionPoolAddress + * 4b. Observe provisionPoolAddress' IST balance increase by the amount deposited in step 4a + * 5. Perform a null upgrade for provisionPool. This upgrade does NOT override provisionWalletBridgerManager + * - The goal here is to allow testing the bridgeHandler from the first upgrade is in fact durable + * 6. Auto provision + * 6a. Introduce a new account + * 6b. Fund it with IST and ATOM to be able to open a vault + * 6c. Try to open a vault WITHOUT provisioning the newly introduced account + * 6d. Observe the new account's address under `published.wallet` + * 7. Same as step 2. Checks manual provision works after null upgrade */ import '@endo/init'; import test from 'ava'; -import { execFileSync } from 'node:child_process'; import { addUser, - makeAgd, evalBundles, agd as agdAmbient, agoric, - bankSend, getISTBalance, + getDetailsMatchingVats, + GOV1ADDR, + openVault, + ATOM_DENOM, } from '@agoric/synthetic-chain'; -import { NonNullish } from './test-lib/errors.js'; import { - retryUntilCondition, + makeVstorageKit, waitUntilAccountFunded, waitUntilContractDeployed, -} from './test-lib/sync-tools.js'; +} from '@agoric/client-utils'; +import { NonNullish } from '@agoric/internal'; +import { + bankSend, + checkUserProvisioned, + introduceAndProvision, + provision, +} from './test-lib/provision-helpers.js'; const PROVISIONING_POOL_ADDR = 'agoric1megzytg65cyrgzs6fvzxgrcqvwwl7ugpt62346'; const ADD_PSM_DIR = 'addUsdLemons'; const DEPOSIT_USD_LEMONS_DIR = 'depositUSD-LEMONS'; +const UPGRADE_PP_DIR = 'upgradeProvisionPool'; +const NULL_UPGRADE_PP_DIR = 'nullUpgradePP'; const USDC_DENOM = NonNullish(process.env.USDC_DENOM); -const agd = makeAgd({ execFileSync }).withOpts({ keyringBackend: 'test' }); - const ambientAuthority = { query: agdAmbient.query, follow: agoric.follow, setTimeout, + log: console.log, }; -const provision = (name, address) => - agd.tx(['swingset', 'provision-one', name, address, 'SMART_WALLET'], { - chainId: 'agoriclocal', - from: 'validator', - yes: true, - }); +test.before(async t => { + const vstorageKit = await makeVstorageKit( + { fetch }, + { rpcAddrs: ['http://localhost:26657'], chainName: 'agoriclocal' }, + ); -const introduceAndProvision = async name => { - const address = await addUser(name); - console.log('ADDR', name, address); + t.context = { + vstorageKit, + }; +}); - const provisionP = provision(name, address); +test.serial('upgrade provisionPool', async t => { + await evalBundles(UPGRADE_PP_DIR); - return { provisionP, address }; -}; + const vatDetailsAfter = await getDetailsMatchingVats('provisionPool'); + const { incarnation } = vatDetailsAfter.find(vat => + vat.vatName.endsWith('provisionPool'), + ); -const getProvisionedAddresses = async () => { - const { children } = await agd.query([ - 'vstorage', - 'children', - 'published.wallet', - ]); - return children; -}; + t.log(vatDetailsAfter); + t.is(incarnation, 1, 'incorrect incarnation'); + t.pass(); +}); -const checkUserProvisioned = addr => - retryUntilCondition( - getProvisionedAddresses, - children => children.includes(addr), - 'Account not provisioned', - { maxRetries: 5, retryIntervalMs: 1000, log: console.log, setTimeout }, - ); +test.serial( + `check provisionPool can recover purse and asset subscribers after upgrade`, + async t => { + // @ts-expect-error casting + const { vstorageKit } = t.context; + + // Introduce new user then provision + const { address } = await introduceAndProvision('provisionTester'); + await checkUserProvisioned(address, vstorageKit); + + // Send USDC_axl to pp + const istBalanceBefore = await getISTBalance(PROVISIONING_POOL_ADDR); + await bankSend(PROVISIONING_POOL_ADDR, `500000${USDC_DENOM}`); + + // Check IST balance + await waitUntilAccountFunded( + PROVISIONING_POOL_ADDR, + ambientAuthority, + { denom: 'uist', value: istBalanceBefore + 500000 }, + { errorMessage: 'Provision pool not able to swap USDC_axl for IST.' }, + ); + + // Introduce USD_LEMONS + await evalBundles(ADD_PSM_DIR); + await waitUntilContractDeployed('psm-IST-USD_LEMONS', ambientAuthority, { + errorMessage: 'psm-IST-USD_LEMONS instance not observed.', + }); + + // Provision the provisionPoolAddress. This is a workaround of provisionPoolAddress + // not having a depositFacet published to namesByAddress. Shouldn't be a problem since + // vat-bank keeps track of virtual purses per address basis. We need there to be + // depositFacet for provisionPoolAddress since we'll fund it with USD_LEMONS + await provision('provisionPoolAddress', PROVISIONING_POOL_ADDR); + await checkUserProvisioned(PROVISIONING_POOL_ADDR, vstorageKit); + + // Send USD_LEMONS to provisionPoolAddress + const istBalanceBeforeLemonsSent = await getISTBalance( + PROVISIONING_POOL_ADDR, + ); + await evalBundles(DEPOSIT_USD_LEMONS_DIR); + + // Check balance again + await waitUntilAccountFunded( + PROVISIONING_POOL_ADDR, + ambientAuthority, + { denom: 'uist', value: istBalanceBeforeLemonsSent + 500000 }, + { errorMessage: 'Provision pool not able to swap USDC_axl for IST.' }, + ); + t.pass(); + }, +); + +test.serial('null upgrade', async t => { + await evalBundles(NULL_UPGRADE_PP_DIR); + + const vatDetailsAfter = await getDetailsMatchingVats('provisionPool'); + const { incarnation } = vatDetailsAfter.find(vat => vat.vatID === 'v28'); // provisionPool is v28 + + t.log(vatDetailsAfter); + t.is(incarnation, 2, 'incorrect incarnation'); + t.pass(); +}); -test(`upgrade provision pool`, async t => { - // Introduce new user then provision - const { address } = await introduceAndProvision('provisionTester'); - await checkUserProvisioned(address); +test.serial('auto provision', async t => { + // @ts-expect-error casting + const { vstorageKit } = t.context; - // Send USDC_axl to pp - const istBalanceBefore = await getISTBalance(PROVISIONING_POOL_ADDR); - await bankSend(PROVISIONING_POOL_ADDR, `500000${USDC_DENOM}`); + const address = await addUser('automaticallyProvisioned'); + console.log('ADDR', 'automaticallyProvisioned', address); - // Check IST balance + await bankSend(address, `50000000${ATOM_DENOM}`); + // some ist is needed for opening a new vault + await bankSend(address, `10000000uist`, GOV1ADDR); await waitUntilAccountFunded( - PROVISIONING_POOL_ADDR, - ambientAuthority, - { denom: 'uist', value: istBalanceBefore + 500000 }, - { errorMessage: 'Provision pool not able to swap USDC_axl for IST.' }, + address, + // TODO: drop agd.query and switch to vstorgeKit + { log: console.log, setTimeout, query: agdAmbient.query }, + { denom: ATOM_DENOM, value: 50_000_000 }, + { errorMessage: `not able to fund ${address}` }, ); - // Introduce USD_LEMONS - await evalBundles(ADD_PSM_DIR); - await waitUntilContractDeployed('psm-IST-USD_LEMONS', ambientAuthority, { - errorMessage: 'psm-IST-USD_LEMONS instance not observed.', - }); - - // Provision the provisionPoolAddress. This is a workaround of provisionPoolAddress - // not having a depositFacet published to namesByAddress. Shouldn't be a problem since - // vat-bank keeps track of virtual purses per address basis. We need there to be - // depositFacet for provisionPoolAddress since we'll fund it with USD_LEMONS - await provision('provisionPoolAddress', PROVISIONING_POOL_ADDR); - await checkUserProvisioned(PROVISIONING_POOL_ADDR); - - // Send USD_LEMONS to provisionPoolAddress - const istBalanceBeforeLemonsSent = await getISTBalance( - PROVISIONING_POOL_ADDR, - ); - await evalBundles(DEPOSIT_USD_LEMONS_DIR); + await openVault(address, '10.0', '20.0'); + await checkUserProvisioned(address, vstorageKit); + t.pass(); +}); - // Check balance again - await waitUntilAccountFunded( - PROVISIONING_POOL_ADDR, - ambientAuthority, - { denom: 'uist', value: istBalanceBeforeLemonsSent + 500000 }, - { errorMessage: 'Provision pool not bale swap USDC_axl for IST.' }, - ); +test.serial('manual provision', async t => { + // @ts-expect-error casting + const { vstorageKit } = t.context; + + const { address } = await introduceAndProvision('manuallyProvisioned'); + await checkUserProvisioned(address, vstorageKit); + t.log('manuallyProvisioned address:', address); t.pass(); }); diff --git a/a3p-integration/proposals/p:upgrade-19/test-lib/errors.js b/a3p-integration/proposals/p:upgrade-19/test-lib/errors.js deleted file mode 100644 index 57dc771e6a5..00000000000 --- a/a3p-integration/proposals/p:upgrade-19/test-lib/errors.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @file Copied from "@agoric/internal" - */ - -import { q } from '@endo/errors'; - -/** - * @template T - * @param {T | null | undefined} val - * @param {string} [optDetails] - * @returns {T} - */ -export const NonNullish = (val, optDetails = `unexpected ${q(val)}`) => { - if (val != null) { - // This `!= null` idiom checks that `val` is neither `null` nor `undefined`. - return val; - } - assert.fail(optDetails); -}; -harden(NonNullish); diff --git a/a3p-integration/proposals/p:upgrade-19/test-lib/provision-helpers.js b/a3p-integration/proposals/p:upgrade-19/test-lib/provision-helpers.js new file mode 100644 index 00000000000..1a8eabee12c --- /dev/null +++ b/a3p-integration/proposals/p:upgrade-19/test-lib/provision-helpers.js @@ -0,0 +1,56 @@ +/* eslint-env node */ +import { retryUntilCondition } from '@agoric/client-utils'; +import { + addUser, + CHAINID, + makeAgd, + VALIDATORADDR, +} from '@agoric/synthetic-chain'; +import { execFileSync } from 'node:child_process'; + +const agd = makeAgd({ execFileSync }).withOpts({ keyringBackend: 'test' }); + +/** + * @param {string} addr + * @param {string} wanted + * @param {string} [from] + */ +export const bankSend = (addr, wanted, from = VALIDATORADDR) => { + return agd.tx(['bank', 'send', from, addr, wanted], { + chainId: CHAINID, + from, + yes: true, + }); +}; + +export const provision = (name, address) => + agd.tx(['swingset', 'provision-one', name, address, 'SMART_WALLET'], { + chainId: 'agoriclocal', + from: 'validator', + yes: true, + }); + +export const introduceAndProvision = async name => { + const address = await addUser(name); + console.log('ADDR', name, address); + + const provisionP = provision(name, address); + + return { provisionP, address }; +}; + +/** + * @param {import('@agoric/client-utils').VstorageKit} vstorageKit + */ +export const getProvisionedAddresses = async vstorageKit => { + const children = await vstorageKit.vstorage.keys('published.wallet'); + return children; +}; + +export const checkUserProvisioned = (addr, vstorageKit) => + retryUntilCondition( + () => getProvisionedAddresses(vstorageKit), + children => children.includes(addr), + 'Account not provisioned', + { maxRetries: 5, retryIntervalMs: 1000, log: console.log, setTimeout }, + ); diff --git a/a3p-integration/proposals/p:upgrade-19/test-lib/sync-tools.js b/a3p-integration/proposals/p:upgrade-19/test-lib/sync-tools.js deleted file mode 100644 index dac2ba7e04f..00000000000 --- a/a3p-integration/proposals/p:upgrade-19/test-lib/sync-tools.js +++ /dev/null @@ -1,272 +0,0 @@ -/* eslint-env node */ - -/** - * @file The purpose of this file is to bring together a set of tools that - * developers can use to synchronize operations they carry out in their tests. - * - * These operations include; - * - Making sure a core-eval resulted in successfully deploying a contract - * - Making sure a core-eval successfully sent zoe invitations to committee members for governance - * - Making sure an account is successfully funded with vbank assets like IST, BLD etc. - * - operation: query dest account's balance - * - condition: dest account has a balance >= sent token - * - Making sure an offer resulted successfully - * - */ - -/** - * @typedef {object} RetryOptions - * @property {number} [maxRetries] - * @property {number} [retryIntervalMs] - * @property {(...arg0: string[]) => void} [log] - * @property {(callback: Function, delay: number) => void} [setTimeout] - * - * @typedef {RetryOptions & {errorMessage: string}} WaitUntilOptions - * - * @typedef {object} CosmosBalanceThreshold - * @property {string} denom - * @property {number} value - */ - -const ambientSetTimeout = global.setTimeout; - -/** - * From https://github.com/Agoric/agoric-sdk/blob/442f07c8f0af03281b52b90e90c27131eef6f331/multichain-testing/tools/sleep.ts#L10 - * - * @param {number} ms - * @param {*} sleepOptions - */ -export const sleep = (ms, { log = () => {}, setTimeout = ambientSetTimeout }) => - new Promise(resolve => { - log(`Sleeping for ${ms}ms...`); - setTimeout(resolve, ms); - }); - -/** - * From https://github.com/Agoric/agoric-sdk/blob/442f07c8f0af03281b52b90e90c27131eef6f331/multichain-testing/tools/sleep.ts#L24 - * - * @param {() => Promise} operation - * @param {(result: any) => boolean} condition - * @param {string} message - * @param {RetryOptions} options - */ -export const retryUntilCondition = async ( - operation, - condition, - message, - { maxRetries = 6, retryIntervalMs = 3500, log = console.log, setTimeout }, -) => { - console.log({ maxRetries, retryIntervalMs, message }); - let retries = 0; - - while (retries < maxRetries) { - try { - const result = await operation(); - log('RESULT', result); - if (condition(result)) { - return result; - } - } catch (error) { - if (error instanceof Error) { - log(`Error: ${error.message}`); - } else { - log(`Unknown error: ${String(error)}`); - } - } - - retries += 1; - console.log( - `Retry ${retries}/${maxRetries} - Waiting for ${retryIntervalMs}ms for ${message}...`, - ); - await sleep(retryIntervalMs, { log, setTimeout }); - } - - throw Error(`${message} condition failed after ${maxRetries} retries.`); -}; - -/** - * @param {WaitUntilOptions} options - */ -const overrideDefaultOptions = options => { - const defaultValues = { - maxRetries: 6, - retryIntervalMs: 3500, - log: console.log, - errorMessage: 'Error', - }; - - return { ...defaultValues, ...options }; -}; - -/// ////////// Making sure a core-eval resulted successfully deploying a contract ///////////// - -const makeGetInstances = follow => async () => { - const instanceEntries = await follow( - '-lF', - `:published.agoricNames.instance`, - ); - - return Object.fromEntries(instanceEntries); -}; - -/** - * - * @param {string} contractName - * @param {{follow: () => object, setTimeout: (object) => void}} ambientAuthority - * @param {WaitUntilOptions} options - */ -export const waitUntilContractDeployed = ( - contractName, - ambientAuthority, - options, -) => { - const { follow, setTimeout } = ambientAuthority; - const getInstances = makeGetInstances(follow); - const { maxRetries, retryIntervalMs, errorMessage, log } = - overrideDefaultOptions(options); - - return retryUntilCondition( - getInstances, - instanceObject => Object.keys(instanceObject).includes(contractName), - errorMessage, - { maxRetries, retryIntervalMs, log, setTimeout }, - ); -}; - -/// ////////// Making sure an account is successfully funded with vbank assets like IST, BLD etc. /////////////// - -const makeQueryCosmosBalance = queryCb => async dest => { - const coins = await queryCb('bank', 'balances', dest); - return coins.balances; -}; - -/** - * - * @param {Array} balances - * @param {CosmosBalanceThreshold} threshold - * @returns {boolean} - */ -const checkCosmosBalance = (balances, threshold) => { - const balance = [...balances].find(({ denom }) => denom === threshold.denom); - return Number(balance.amount) >= threshold.value; -}; - -/** - * @param {string} destAcct - * @param {{query: () => Promise, setTimeout: (object) => void}} ambientAuthority - * @param {{denom: string, value: number}} threshold - * @param {WaitUntilOptions} options - */ -export const waitUntilAccountFunded = ( - destAcct, - ambientAuthority, - threshold, - options, -) => { - const { query, setTimeout } = ambientAuthority; - const queryCosmosBalance = makeQueryCosmosBalance(query); - const { maxRetries, retryIntervalMs, errorMessage, log } = - overrideDefaultOptions(options); - - return retryUntilCondition( - async () => queryCosmosBalance(destAcct), - balances => checkCosmosBalance(balances, threshold), - errorMessage, - { maxRetries, retryIntervalMs, log, setTimeout }, - ); -}; - -/// ////////// Making sure an offers get results ///////////// - -const makeQueryWallet = follow => async (/** @type {string} */ addr) => { - const update = await follow('-lF', `:published.wallet.${addr}`); - - return update; -}; - -/** - * - * @param {object} offerStatus - * @param {boolean} waitForPayouts - * @param {string} offerId - */ -const checkOfferState = (offerStatus, waitForPayouts, offerId) => { - const { updated, status } = offerStatus; - - if (updated !== 'offerStatus') return false; - if (!status) return false; - if (status.id !== offerId) return false; - if (!status.numWantsSatisfied || status.numWantsSatisfied !== 1) return false; - if (waitForPayouts && status.payouts) return true; - if (!waitForPayouts && status.result) return true; - - return false; -}; - -/** - * - * @param {string} addr - * @param {string} offerId - * @param {boolean} waitForPayouts - * @param {{follow: () => object, setTimeout: (callback: Function, delay: number) => void}} ambientAuthority - * @param {WaitUntilOptions} options - */ -export const waitUntilOfferResult = ( - addr, - offerId, - waitForPayouts, - ambientAuthority, - options, -) => { - const { follow, setTimeout } = ambientAuthority; - const queryWallet = makeQueryWallet(follow); - const { maxRetries, retryIntervalMs, errorMessage, log } = - overrideDefaultOptions(options); - - return retryUntilCondition( - async () => queryWallet(addr), - status => checkOfferState(status, waitForPayouts, offerId), - errorMessage, - { maxRetries, retryIntervalMs, log, setTimeout }, - ); -}; - -/// ////////// Making sure a core-eval successfully sent zoe invitations to committee members for governance ///////////// - -/** - * - * @param {{ updated: string, currentAmount: any }} update - * @returns {boolean} - */ -const checkForInvitation = update => { - const { updated, currentAmount } = update; - - if (updated !== 'balance') return false; - if (!currentAmount || !currentAmount.brand) return false; - - return currentAmount.brand.includes('Invitation'); -}; - -/** - * - * @param {string} addr - * @param {{follow: () => object, setTimeout: (object) => void}} ambientAuthority - * @param {WaitUntilOptions} options - */ -export const waitUntilInvitationReceived = ( - addr, - ambientAuthority, - options, -) => { - const { follow, setTimeout } = ambientAuthority; - const queryWallet = makeQueryWallet(follow); - const { maxRetries, retryIntervalMs, errorMessage, log } = - overrideDefaultOptions(options); - - return retryUntilCondition( - async () => queryWallet(addr), - checkForInvitation, - errorMessage, - { maxRetries, retryIntervalMs, log, setTimeout }, - ); -}; diff --git a/a3p-integration/proposals/p:upgrade-19/tsconfig.json b/a3p-integration/proposals/p:upgrade-19/tsconfig.json index 960c1f4587a..23a6b14091b 100644 --- a/a3p-integration/proposals/p:upgrade-19/tsconfig.json +++ b/a3p-integration/proposals/p:upgrade-19/tsconfig.json @@ -11,5 +11,10 @@ "noImplicitThis": true, // XXX synthetic-chain has some errors "skipLibCheck": true - } + }, + "exclude": [ + "addUsdLemons/", + "replaceFeeDistributor/", + "upgradeProvisionPool/" + ] } diff --git a/a3p-integration/proposals/p:upgrade-19/yarn.lock b/a3p-integration/proposals/p:upgrade-19/yarn.lock index f74d4368e0e..e795ff2f376 100644 --- a/a3p-integration/proposals/p:upgrade-19/yarn.lock +++ b/a3p-integration/proposals/p:upgrade-19/yarn.lock @@ -31,6 +31,21 @@ __metadata: languageName: node linkType: hard +"@agoric/base-zone@npm:0.1.1-dev-1dd4589.0+1dd4589": + version: 0.1.1-dev-1dd4589.0 + resolution: "@agoric/base-zone@npm:0.1.1-dev-1dd4589.0" + dependencies: + "@agoric/store": "npm:0.9.3-dev-1dd4589.0+1dd4589" + "@endo/common": "npm:^1.2.8" + "@endo/errors": "npm:^1.2.8" + "@endo/exo": "npm:^1.5.7" + "@endo/far": "npm:^1.1.9" + "@endo/pass-style": "npm:^1.4.7" + "@endo/patterns": "npm:^1.4.7" + checksum: 10c0/d0c214a7e69b427df1632d1ac10bc97f977823d723e4696ad8cf92a0eb9dd6297142ce4b98ae211c994547c43380c9a6487959a1719f7ee90fb128a00e5ff669 + languageName: node + linkType: hard + "@agoric/base-zone@npm:0.1.1-dev-3b799b8.0+3b799b8": version: 0.1.1-dev-3b799b8.0 resolution: "@agoric/base-zone@npm:0.1.1-dev-3b799b8.0" @@ -200,6 +215,26 @@ __metadata: languageName: node linkType: hard +"@agoric/internal@npm:dev": + version: 0.3.3-dev-1dd4589.0 + resolution: "@agoric/internal@npm:0.3.3-dev-1dd4589.0" + dependencies: + "@agoric/base-zone": "npm:0.1.1-dev-1dd4589.0+1dd4589" + "@endo/common": "npm:^1.2.8" + "@endo/errors": "npm:^1.2.8" + "@endo/far": "npm:^1.1.9" + "@endo/init": "npm:^1.1.7" + "@endo/marshal": "npm:^1.6.2" + "@endo/pass-style": "npm:^1.4.7" + "@endo/patterns": "npm:^1.4.7" + "@endo/promise-kit": "npm:^1.1.8" + "@endo/stream": "npm:^1.2.8" + anylogger: "npm:^0.21.0" + jessie.js: "npm:^0.3.4" + checksum: 10c0/eb261f7bde265f168698c9da04af7458e2747fd54e945cdef82b822af5749e8a9443bb827bbace959269a04ae58d33c4ea465c31b580bf8d07b15fa87cb80ca3 + languageName: node + linkType: hard + "@agoric/kmarshal@npm:0.1.1-dev-02c06c4.0+02c06c4": version: 0.1.1-dev-02c06c4.0 resolution: "@agoric/kmarshal@npm:0.1.1-dev-02c06c4.0" @@ -305,6 +340,19 @@ __metadata: languageName: node linkType: hard +"@agoric/store@npm:0.9.3-dev-1dd4589.0+1dd4589": + version: 0.9.3-dev-1dd4589.0 + resolution: "@agoric/store@npm:0.9.3-dev-1dd4589.0" + dependencies: + "@endo/errors": "npm:^1.2.8" + "@endo/exo": "npm:^1.5.7" + "@endo/marshal": "npm:^1.6.2" + "@endo/pass-style": "npm:^1.4.7" + "@endo/patterns": "npm:^1.4.7" + checksum: 10c0/72d2c3ff3cb0d8b48f06146e4deb0967273e10a86ec7c01e7bc176c5e1ab31d016b909bde1230c8f031ba60552c31ee7d0eccb5791985fc12330fdb6dcda5edb + languageName: node + linkType: hard + "@agoric/store@npm:0.9.3-dev-3b799b8.0+3b799b8": version: 0.9.3-dev-3b799b8.0 resolution: "@agoric/store@npm:0.9.3-dev-3b799b8.0" @@ -1096,7 +1144,7 @@ __metadata: languageName: node linkType: hard -"@endo/exo@npm:^1.5.6": +"@endo/exo@npm:^1.5.6, @endo/exo@npm:^1.5.7": version: 1.5.7 resolution: "@endo/exo@npm:1.5.7" dependencies: @@ -1238,7 +1286,7 @@ __metadata: languageName: node linkType: hard -"@endo/stream@npm:^1.2.7": +"@endo/stream@npm:^1.2.7, @endo/stream@npm:^1.2.8": version: 1.2.8 resolution: "@endo/stream@npm:1.2.8" dependencies: @@ -5203,6 +5251,7 @@ __metadata: dependencies: "@agoric/client-utils": "npm:0.1.1-dev-02c06c4.0" "@agoric/ertp": "npm:dev" + "@agoric/internal": "npm:dev" "@agoric/synthetic-chain": "npm:^0.4.3" "@agoric/zoe": "npm:dev" "@endo/errors": "npm:1.2.7" diff --git a/a3p-integration/proposals/z:acceptance/yarn.lock b/a3p-integration/proposals/z:acceptance/yarn.lock index 92dc8a54bfe..d76e6d95485 100644 --- a/a3p-integration/proposals/z:acceptance/yarn.lock +++ b/a3p-integration/proposals/z:acceptance/yarn.lock @@ -156,6 +156,7 @@ __metadata: "@agoric/vat-data": "npm:^0.5.2" "@agoric/vats": "npm:^0.15.1" "@agoric/zoe": "npm:^0.26.2" + "@agoric/zone": "npm:^0.2.2" "@endo/captp": "npm:^4.4.3" "@endo/errors": "npm:^1.2.8" "@endo/eventual-send": "npm:^1.2.8" diff --git a/packages/inter-protocol/package.json b/packages/inter-protocol/package.json index 76f545f1e35..46f5336f45a 100644 --- a/packages/inter-protocol/package.json +++ b/packages/inter-protocol/package.json @@ -41,6 +41,7 @@ "@agoric/vat-data": "^0.5.2", "@agoric/vats": "^0.15.1", "@agoric/zoe": "^0.26.2", + "@agoric/zone": "^0.2.2", "@endo/captp": "^4.4.3", "@endo/eventual-send": "^1.2.8", "@endo/far": "^1.1.9", @@ -53,7 +54,6 @@ "@agoric/smart-wallet": "^0.5.3", "@agoric/swingset-liveslots": "^0.10.2", "@agoric/swingset-vat": "^0.32.2", - "@agoric/zone": "^0.2.2", "@endo/bundle-source": "^3.5.0", "@endo/init": "^1.1.7", "@endo/promise-kit": "^1.1.8", diff --git a/packages/inter-protocol/src/provisionPool.js b/packages/inter-protocol/src/provisionPool.js index 7eb37c61dd1..de96cfabaab 100644 --- a/packages/inter-protocol/src/provisionPool.js +++ b/packages/inter-protocol/src/provisionPool.js @@ -12,7 +12,11 @@ import { prepareExo } from '@agoric/vat-data'; import { provideSingleton } from '@agoric/zoe/src/contractSupport/durability.js'; import { prepareRecorderKitMakers } from '@agoric/zoe/src/contractSupport/recorder.js'; import { TopicsRecordShape } from '@agoric/zoe/src/contractSupport/topics.js'; -import { prepareProvisionPoolKit } from './provisionPoolKit.js'; +import { makeDurableZone } from '@agoric/zone/durable.js'; +import { + prepareBridgeProvisionTool, + prepareProvisionPoolKit, +} from './provisionPoolKit.js'; /** @import {Marshal} from '@endo/marshal'; */ @@ -72,11 +76,15 @@ export const start = async (zcf, privateArgs, baggage) => { privateArgs.marshaller, ); - const makeProvisionPoolKit = prepareProvisionPoolKit(baggage, { + const zone = makeDurableZone(baggage); + + const makeBridgeProvisionTool = prepareBridgeProvisionTool(zone); + const makeProvisionPoolKit = prepareProvisionPoolKit(zone, { makeRecorderKit, params, poolBank, zcf, + makeBridgeProvisionTool, }); const provisionPoolKit = await provideSingleton( diff --git a/packages/inter-protocol/src/provisionPoolKit.js b/packages/inter-protocol/src/provisionPoolKit.js index ee05ea5cc06..6c3694e6097 100644 --- a/packages/inter-protocol/src/provisionPoolKit.js +++ b/packages/inter-protocol/src/provisionPoolKit.js @@ -1,7 +1,6 @@ // @ts-check import { X, q, Fail } from '@endo/errors'; import { E } from '@endo/far'; -import { Far } from '@endo/marshal'; import { AmountMath, BrandShape } from '@agoric/ertp'; import { deeplyFulfilledObject, makeTracer } from '@agoric/internal'; @@ -15,7 +14,6 @@ import { M, makeScalarBigMapStore, makeScalarBigSetStore, - prepareExoClassKit, } from '@agoric/vat-data'; import { PowerFlags } from '@agoric/vats/src/walletFlags.js'; import { @@ -75,18 +73,22 @@ const FIRST_LOWER_NEAR_KEYWORD = /^[a-z][a-zA-Z0-9_$]*$/; * Given attenuated access to the funding purse, handle requests to provision * smart wallets. * - * @param {(depositBank: ERef) => Promise} sendInitialPayment - * @param {() => void} onProvisioned + * @param {import('@agoric/zone').Zone} zone */ -export const makeBridgeProvisionTool = (sendInitialPayment, onProvisioned) => { - /** @param {ProvisionPoolKitReferences} refs */ - const makeBridgeHandler = ({ - bankManager, - namesByAddressAdmin, - walletFactory, - }) => - Far('provisioningHandler', { - fromBridge: async obj => { +export const prepareBridgeProvisionTool = zone => + zone.exoClass( + 'smartWalletProvisioningHandler', + M.interface('ProvisionBridgeHandlerMaker', { + fromBridge: M.callWhen(M.record()).returns(), + }), + (bankManager, walletFactory, namesByAddressAdmin, forHandler) => ({ + bankManager, + walletFactory, + namesByAddressAdmin, + forHandler, + }), + { + async fromBridge(obj) { obj.type === 'PLEASE_PROVISION' || Fail`Unrecognized request ${obj.type}`; trace('PLEASE_PROVISION', obj); @@ -94,9 +96,12 @@ export const makeBridgeProvisionTool = (sendInitialPayment, onProvisioned) => { powerFlags.includes(PowerFlags.SMART_WALLET) || Fail`missing SMART_WALLET in powerFlags`; + const { bankManager, walletFactory, namesByAddressAdmin, forHandler } = + this.state; + const bank = E(bankManager).getBankForAddress(address); // only proceed if we can provide funds - await sendInitialPayment(bank); + await forHandler.sendInitialPayment(bank); const [_, created] = await E(walletFactory).provideSmartWallet( address, @@ -104,31 +109,30 @@ export const makeBridgeProvisionTool = (sendInitialPayment, onProvisioned) => { namesByAddressAdmin, ); if (created) { - onProvisioned(); + forHandler.onProvisioned(); } trace(created ? 'provisioned' : 're-provisioned', address); }, - }); - return makeBridgeHandler; -}; + }, + ); /** - * @param {import('@agoric/vat-data').Baggage} baggage + * @param {import('@agoric/zone').Zone} zone * @param {{ * makeRecorderKit: import('@agoric/zoe/src/contractSupport/recorder.js').MakeRecorderKit; * params: any; * poolBank: import('@endo/far').ERef; * zcf: ZCF; + * makeBridgeProvisionTool: ReturnType; * }} powers */ export const prepareProvisionPoolKit = ( - baggage, - { makeRecorderKit, params, poolBank, zcf }, + zone, + { makeRecorderKit, params, poolBank, zcf, makeBridgeProvisionTool }, ) => { const zoe = zcf.getZoeService(); - const makeProvisionPoolKitInternal = prepareExoClassKit( - baggage, + const makeProvisionPoolKitInternal = zone.exoClassKit( 'ProvisionPoolKit', { machine: M.interface('ProvisionPoolKit machine', { @@ -151,6 +155,7 @@ export const prepareProvisionPoolKit = ( ackWallet: M.call(M.string()).returns(M.boolean()), }), helper: UnguardedHelperI, + forHandler: UnguardedHelperI, public: M.interface('ProvisionPoolKit public', { getPublicTopics: M.call().returns({ metrics: PublicTopicShape }), }), @@ -217,23 +222,24 @@ export const prepareProvisionPoolKit = ( const refs = await deeplyFulfilledObject(obj); Object.assign(this.state, refs); }, + /** @returns {import('@agoric/vats').BridgeHandler} */ makeHandler() { const { bankManager, namesByAddressAdmin, walletFactory } = this.state; if (!bankManager || !namesByAddressAdmin || !walletFactory) { throw Fail`must set references before handling requests`; } - const { helper } = this.facets; - // a bit obtuse but leave for backwards compatibility with tests - const innerMaker = makeBridgeProvisionTool( - bank => helper.sendInitialPayment(bank), - () => helper.onProvisioned(), - ); - return innerMaker({ + + const { forHandler } = this.facets; + + const provisionHandler = makeBridgeProvisionTool( bankManager, - namesByAddressAdmin, walletFactory, - }); + namesByAddressAdmin, + forHandler, + ); + + return provisionHandler; }, /** * @param {Brand} brand @@ -311,37 +317,6 @@ export const prepareProvisionPoolKit = ( ); facets.helper.publishMetrics(); }, - onProvisioned() { - const { state, facets } = this; - state.walletsProvisioned += 1n; - facets.helper.publishMetrics(); - }, - /** @param {ERef} destBank */ - async sendInitialPayment(destBank) { - const { - facets: { helper }, - state: { fundPurse, poolBrand }, - } = this; - const perAccountInitialAmount = /** @type {Amount<'nat'>} */ ( - params.getPerAccountInitialAmount() - ); - const initialPmt = await E(fundPurse).withdraw( - perAccountInitialAmount, - ); - - const destPurse = E(destBank).getPurse(poolBrand); - return E(destPurse) - .deposit(initialPmt) - .then(amt => { - helper.onSendFunds(perAccountInitialAmount); - trace('provisionPool sent', amt); - }) - .catch(reason => { - console.error(X`initial deposit failed: ${q(reason)}`); - void E(fundPurse).deposit(initialPmt); - throw reason; - }); - }, /** * @param {ERef} exchangePurse * @param {ERef} brand @@ -491,6 +466,39 @@ export const prepareProvisionPoolKit = ( return rxd; }, }, + forHandler: { + onProvisioned() { + const { state, facets } = this; + state.walletsProvisioned += 1n; + facets.helper.publishMetrics(); + }, + /** @param {ERef} destBank */ + async sendInitialPayment(destBank) { + const { + facets: { helper }, + state: { fundPurse, poolBrand }, + } = this; + const perAccountInitialAmount = /** @type {Amount<'nat'>} */ ( + params.getPerAccountInitialAmount() + ); + const initialPmt = await E(fundPurse).withdraw( + perAccountInitialAmount, + ); + + const destPurse = E(destBank).getPurse(poolBrand); + return E(destPurse) + .deposit(initialPmt) + .then(amt => { + helper.onSendFunds(perAccountInitialAmount); + trace('provisionPool sent', amt); + }) + .catch(reason => { + console.error(X`initial deposit failed: ${q(reason)}`); + void E(fundPurse).deposit(initialPmt); + throw reason; + }); + }, + }, public: { getPublicTopics() { return { diff --git a/packages/inter-protocol/test/provisionPool.test.js b/packages/inter-protocol/test/provisionPool.test.js index 48ea08f7312..e591643a927 100644 --- a/packages/inter-protocol/test/provisionPool.test.js +++ b/packages/inter-protocol/test/provisionPool.test.js @@ -19,7 +19,8 @@ import { makeFakeBoard } from '@agoric/vats/tools/board-utils.js'; import { makeRatio } from '@agoric/zoe/src/contractSupport/ratio.js'; import { E, Far } from '@endo/far'; import path from 'path'; -import { makeBridgeProvisionTool } from '../src/provisionPoolKit.js'; +import { makeHeapZone } from '@agoric/zone'; +import { prepareBridgeProvisionTool } from '../src/provisionPoolKit.js'; import { makeMockChainStorageRoot, setUpZoeForTest, @@ -364,15 +365,14 @@ test('makeBridgeProvisionTool handles duplicate requests', async t => { const { nameHub: namesByAddress, nameAdmin: namesByAddressAdmin } = makeNameHubKit(); const publishMetrics = () => {}; - const makeHandler = makeBridgeProvisionTool( - sendInitialPayment, - publishMetrics, - ); - const handler = makeHandler({ + const zone = makeHeapZone(); + const makeBridgeProvisionTool = prepareBridgeProvisionTool(zone); + const handler = makeBridgeProvisionTool( bankManager, - namesByAddressAdmin, walletFactory, - }); + namesByAddressAdmin, + Far('helpers', { sendInitialPayment, onProvisioned: publishMetrics }), + ); t.log('1st request to provision a SMART_WALLET for', address); await handler.fromBridge({ diff --git a/packages/vats/src/proposals/upgrade-provisionPool-proposal.js b/packages/vats/src/proposals/upgrade-provisionPool-proposal.js index 4dd74af9c69..a94fd6ef418 100644 --- a/packages/vats/src/proposals/upgrade-provisionPool-proposal.js +++ b/packages/vats/src/proposals/upgrade-provisionPool-proposal.js @@ -1,5 +1,8 @@ import { E } from '@endo/far'; import { deeplyFulfilled } from '@endo/marshal'; +import { makeTracer } from '@agoric/internal'; + +const tracer = makeTracer('UpgradeProvisionPool'); /** * @param {BootstrapPowers & { @@ -27,7 +30,7 @@ export const upgradeProvisionPool = async ( const { provisionPoolRef } = options.options; assert(provisionPoolRef.bundleID); - console.log(`PROVISION POOL BUNDLE ID: `, provisionPoolRef.bundleID); + tracer(`PROVISION POOL BUNDLE ID: `, provisionPoolRef); const [ provisionPoolStartResult, @@ -66,7 +69,7 @@ export const upgradeProvisionPool = async ( newPrivateArgs, ); - console.log('ProvisionPool upgraded: ', upgradeResult); + tracer('ProvisionPool upgraded: ', upgradeResult); const references = { bankManager, @@ -74,17 +77,17 @@ export const upgradeProvisionPool = async ( walletFactory: wfCreatorFacet, }; - console.log('Calling setReferences with: ', references); + tracer('Calling setReferences with: ', references); await E(ppCreatorFacet).setReferences(references); - console.log('Creating bridgeHandler...'); + tracer('Creating bridgeHandler...'); const bridgeHandler = await E(ppCreatorFacet).makeHandler(); - console.log('Setting new bridgeHandler...'); + tracer('Setting new bridgeHandler...'); // @ts-expect-error casting await E(provisionWalletBridgeManager).setHandler(bridgeHandler); - console.log('Done.'); + tracer('Done.'); }; export const getManifestForUpgradingProvisionPool = (