-
Notifications
You must be signed in to change notification settings - Fork 214
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(pp-upgrade): test functionality after provisionPool and bank up…
…grade (#10419) closes: #10395 closes: #10425 closes: #10564 ## Description As mentioned in #8158, we need the ability to restart or replace all vats. This PR focuses on the vats v28-provisionPool and v14-bank. The goal is to make sure after upgrading both of those vats, v28-provisionPool functionality keeps working. We pay special attention to #8722 and #8724 during the tests as those issues were identified as potential problems against v28-provisionPool upgrade. ### Security Considerations v28-provisionPool and v14-bank are critical vats for the system when it comes to onboarding new users and keeping track of ERTP representations of user assets. Reviewers, please highlight the slightest risk you see. ### Scaling Considerations v28-provisionPool vat has a linear relationship with the number of users entering the Agoric system. So it is pretty important it keeps working. Reviewers, please highlight the slightest risk you see. ### Documentation Considerations None. ### Testing Considerations During the testing, we focused on `provisionPoolAddress`' cosmos level balances as our source of truth. The reasoning behind this is; if the IST balance of the `provisionPoolAddress` it can only mean - it has received the anchor asset we've sent - v28-provisionPool is notified of this balance change - executed a swap against corresponding PSM - deposited the payout to IST purse - v14-bank updated the balances correctly In our test we send two anchor assets; * USDC_axl to make sure v28-provisionPool recovered existing purses * USD_LEMONS to make sure v28-provisionPool is notified of new assets ### Upgrade Considerations Both v28-provisionPool and v14-bank are staged upgrades in `upgrade.go`
- Loading branch information
Showing
19 changed files
with
587 additions
and
94 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,4 @@ | ||
replaceFeeDistributor/ | ||
testUpgradedBoard/ | ||
addUsdLemons/ | ||
upgradeProvisionPool/ |
8 changes: 8 additions & 0 deletions
8
a3p-integration/proposals/p:upgrade-19/depositUSD-LEMONS/deposit-usd-lemons-permit.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"consume": { | ||
"contractKits": true, | ||
"namesByAddressAdmin": true, | ||
"agoricNames": true | ||
} | ||
} | ||
|
52 changes: 52 additions & 0 deletions
52
a3p-integration/proposals/p:upgrade-19/depositUSD-LEMONS/deposit-usd-lemons.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// @ts-nocheck | ||
/* eslint-disable no-undef */ | ||
const PROVISIONING_POOL_ADDR = 'agoric1megzytg65cyrgzs6fvzxgrcqvwwl7ugpt62346'; | ||
|
||
const depositUsdLemons = async powers => { | ||
const { | ||
consume: { | ||
contractKits: contractKitsP, | ||
namesByAddressAdmin: namesByAddressAdminP, | ||
agoricNames, | ||
}, | ||
} = powers; | ||
|
||
const namesByAddressAdmin = await namesByAddressAdminP; | ||
|
||
const getDepositFacet = async address => { | ||
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; |
7 changes: 7 additions & 0 deletions
7
a3p-integration/proposals/p:upgrade-19/nullUpgradePP/null-upgrade-pp-permit.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"consume": { | ||
"provisionPoolStartResult": true, | ||
"instancePrivateArgs": true, | ||
"economicCommitteeCreatorFacet": true | ||
} | ||
} |
38 changes: 38 additions & 0 deletions
38
a3p-integration/proposals/p:upgrade-19/nullUpgradePP/null-upgrade-pp.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
186 changes: 186 additions & 0 deletions
186
a3p-integration/proposals/p:upgrade-19/provisionPool.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
/* 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; | ||
* 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 { | ||
addUser, | ||
evalBundles, | ||
agd as agdAmbient, | ||
agoric, | ||
getISTBalance, | ||
getDetailsMatchingVats, | ||
GOV1ADDR, | ||
openVault, | ||
ATOM_DENOM, | ||
} from '@agoric/synthetic-chain'; | ||
import { | ||
makeVstorageKit, | ||
waitUntilAccountFunded, | ||
waitUntilContractDeployed, | ||
} 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 ambientAuthority = { | ||
query: agdAmbient.query, | ||
follow: agoric.follow, | ||
setTimeout, | ||
log: console.log, | ||
}; | ||
|
||
test.before(async t => { | ||
const vstorageKit = await makeVstorageKit( | ||
{ fetch }, | ||
{ rpcAddrs: ['http://localhost:26657'], chainName: 'agoriclocal' }, | ||
); | ||
|
||
t.context = { | ||
vstorageKit, | ||
}; | ||
}); | ||
|
||
test.serial('upgrade provisionPool', async t => { | ||
await evalBundles(UPGRADE_PP_DIR); | ||
|
||
const vatDetailsAfter = await getDetailsMatchingVats('provisionPool'); | ||
const { incarnation } = vatDetailsAfter.find(vat => | ||
vat.vatName.endsWith('provisionPool'), | ||
); | ||
|
||
t.log(vatDetailsAfter); | ||
t.is(incarnation, 1, 'incorrect incarnation'); | ||
t.pass(); | ||
}); | ||
|
||
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.serial('auto provision', async t => { | ||
// @ts-expect-error casting | ||
const { vstorageKit } = t.context; | ||
|
||
const address = await addUser('automaticallyProvisioned'); | ||
console.log('ADDR', 'automaticallyProvisioned', address); | ||
|
||
await bankSend(address, `50000000${ATOM_DENOM}`); | ||
// some ist is needed for opening a new vault | ||
await bankSend(address, `10000000uist`, GOV1ADDR); | ||
await waitUntilAccountFunded( | ||
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}` }, | ||
); | ||
|
||
await openVault(address, '10.0', '20.0'); | ||
await checkUserProvisioned(address, vstorageKit); | ||
t.pass(); | ||
}); | ||
|
||
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(); | ||
}); |
56 changes: 56 additions & 0 deletions
56
a3p-integration/proposals/p:upgrade-19/test-lib/provision-helpers.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 }, | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,5 @@ | |
|
||
yarn ava replaceFeeDistributor.test.js | ||
yarn ava upgradedBoard.test.js | ||
|
||
yarn ava provisionPool.test.js |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.