Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Repair vaultUpgrade proposal; also register replacement scaledPriceAuthority #9748

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 21 additions & 10 deletions a3p-integration/proposals/a:upgrade-next/upgradeVaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,35 @@ import {
pushPrices,
registerOraclesForBrand,
} from './agd-tools.js';
import { getDetailsMatchingVats } from './vatDetails.js';

const BRANDNAMES = ['ATOM', 'stATOM', 'stTIA', 'stOSMO', 'stkATOM'];
const BRANDNAMES = ['ATOM', 'stATOM'];
const oraclesByBrand = generateOracleMap('u16', BRANDNAMES);

// There are no old prices for the other currencies.
const atomOutPre = await getPriceQuote('ATOM');
assert.equal(atomOutPre, '+12010000');

console.log('adding oracle for each brand');
console.log('UPGV: adding oracle for each brand');
await registerOraclesForBrand('ATOM', oraclesByBrand);
await registerOraclesForBrand('stATOM', oraclesByBrand);
await registerOraclesForBrand('stTIA', oraclesByBrand);
await registerOraclesForBrand('stOSMO', oraclesByBrand);
await registerOraclesForBrand('stkATOM', oraclesByBrand);

console.log('pushing new prices');
console.log('UPGV: pushing new prices');
await pushPrices(11.2, 'ATOM', oraclesByBrand);
await pushPrices(11.3, 'stTIA', oraclesByBrand);
await pushPrices(11.4, 'stATOM', oraclesByBrand);
await pushPrices(11.5, 'stOSMO', oraclesByBrand);
await pushPrices(11.6, 'stkATOM', oraclesByBrand);

// price_feed and governor, old and new for two tokens
const priceFeedDetails = await getDetailsMatchingVats('price_feed');
assert.equal(Object.keys(priceFeedDetails).length, 8);

// Two old SPAs, and two new ones
const details = await getDetailsMatchingVats('scaledPriceAuthority');
assert.equal(Object.keys(details).length, 4, Object.keys(details));
console.log('UPGV 8 price feeds and 4 scaledPriceAuthorities found');

// We previously created price feeds for some tokens that aren't in A3P
const osmoDetails = await getDetailsMatchingVats('stOSMO');
assert.equal(Object.keys(osmoDetails).length, 0);
const tiaDetails = await getDetailsMatchingVats('stTIA');
assert.equal(Object.keys(tiaDetails).length, 0);
const stkAtomDetails = await getDetailsMatchingVats('stkATOM');
assert.equal(Object.keys(stkAtomDetails).length, 0);
24 changes: 3 additions & 21 deletions a3p-integration/proposals/a:upgrade-next/upgradeVaults.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,39 +23,21 @@ import {
import { getDetailsMatchingVats } from './vatDetails.js';

const checkPriceFeedVatsUpdated = async t => {
const atomDetails = await getVatDetails('ATOM-USD_price_feed');
const atomDetails = await getVatDetails('-ATOM-USD_price_feed');
// both the original and the new ATOM vault are incarnation 0
t.is(atomDetails.incarnation, 0);
const stAtomDetails = await getVatDetails('stATOM');
t.is(stAtomDetails.incarnation, 0);
const stOsmoDetails = await getVatDetails('stOSMO');
t.is(stOsmoDetails.incarnation, 0);
const stTiaDetails = await getVatDetails('stTIA');
t.is(stTiaDetails.incarnation, 0);
await Promise.all([
checkForOracle(t, 'ATOM'),
checkForOracle(t, 'stATOM'),
checkForOracle(t, 'stTIA'),
checkForOracle(t, 'stOSMO'),
checkForOracle(t, 'stkATOM'),
]);
await Promise.all([checkForOracle(t, 'ATOM'), checkForOracle(t, 'stATOM')]);
};

const BRANDNAMES = ['ATOM', 'stATOM', 'stTIA', 'stOSMO', 'stkATOM'];
const BRANDNAMES = ['ATOM', 'stATOM'];
const oraclesByBrand = generateOracleMap('u16', BRANDNAMES);

const checkNewQuotes = async t => {
t.log('awaiting new quotes');
const atomOut = await getPriceQuote('ATOM');
t.is(atomOut, '+11200000');
const tiaOut = await getPriceQuote('stTIA');
t.is(tiaOut, '+11300000');
const stAtomOut = await getPriceQuote('stATOM');
t.is(stAtomOut, '+11400000');
const osmoOut = await getPriceQuote('stOSMO');
t.is(osmoOut, '+11500000');
const stkAtomOut = await getPriceQuote('stkATOM');
t.is(stkAtomOut, '+11600000');
};

const createNewBid = async t => {
Expand Down
3 changes: 2 additions & 1 deletion a3p-integration/proposals/a:upgrade-next/vatDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,11 @@ export const getDetailsMatchingVats = async vatName => {
const infos = [];
for (const vatID of vatIDs) {
const vatInfo = kStore.lookupVat(vatID);
const name = vatInfo.options().name;
const source = vatInfo.source();
// @ts-expect-error cast
const { incarnation } = vatInfo.currentSpan();
infos.push({ vatName, vatID, incarnation, ...source });
infos.push({ vatName: name, vatID, incarnation, ...source });
}

return infos;
Expand Down
9 changes: 8 additions & 1 deletion golang/cosmos/app/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,12 @@ func upgradePriceFeedCoreProposalSteps(upgradeName string) ([]vm.CoreProposalSte

var inBrandNames []string
switch {
case isThisUpgrade("UNRELEASED_A3P_INTEGRATION"), isThisUpgrade("UNRELEASED_main"):
case isThisUpgrade("UNRELEASED_A3P_INTEGRATION"):
inBrandNames = []string{
"ATOM",
"stATOM",
}
case isThisUpgrade("UNRELEASED_main"):
inBrandNames = []string{
"ATOM",
"stATOM",
Expand Down Expand Up @@ -148,6 +153,8 @@ func upgradePriceFeedCoreProposalSteps(upgradeName string) ([]vm.CoreProposalSte
return []vm.CoreProposalStep{
// Add new vats for price feeds. The existing ones will be retired shortly.
vm.CoreProposalStepForModules(proposals...),
// add new scaledPriceAuthorities. The existing ones will be retired shortly.
vm.CoreProposalStepForModules("@agoric/builders/scripts/vats/replaceScaledPriceAuthorities.js"),
// Add new auction contract. The old one will be retired shortly.
vm.CoreProposalStepForModules("@agoric/builders/scripts/vats/add-auction.js"),
// upgrade vaultFactory.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { makeHelpers } from '@agoric/deploy-script-support';
export const defaultProposalBuilder = async ({ publishRef, install }) =>
harden({
sourceSpec:
'@agoric/inter-protocol/src/proposals/upgrade-scaledPriceAuthorities.js',
'@agoric/inter-protocol/src/proposals/replace-scaledPriceAuthorities.js',
getManifestCall: [
'getManifestForUpgradeScaledPriceAuthorities',
'getManifestForReplaceScaledPriceAuthorities',
{
scaledPARef: publishRef(
install('@agoric/zoe/src/contracts/scaledPriceAuthority.js'),
Expand All @@ -18,5 +18,5 @@ export const defaultProposalBuilder = async ({ publishRef, install }) =>
export default async (homeP, endowments) => {
const { writeCoreEval } = await makeHelpers(homeP, endowments);

await writeCoreEval('upgradeScaledPriceAuthorities', defaultProposalBuilder);
await writeCoreEval('replaceScaledPriceAuthorities', defaultProposalBuilder);
};
22 changes: 20 additions & 2 deletions packages/inter-protocol/src/proposals/addAssetToVault.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,15 +129,14 @@ export const publishInterchainAssetFromBank = async (
* @param {object} config.options
* @param {InterchainAssetOptions} config.options.interchainAssetOptions
*/
export const registerScaledPriceAuthority = async (
export const startScaledPriceAuthority = async (
{
consume: {
agoricNamesAdmin,
startUpgradable,
priceAuthorityAdmin,
priceAuthority,
},
instance: { produce: produceInstance },
},
{ options: { interchainAssetOptions } },
) => {
Expand Down Expand Up @@ -230,6 +229,25 @@ export const registerScaledPriceAuthority = async (
true, // force
);

return spaKit;
};

/**
* @param {BootstrapPowers} powers
* @param {object} config
* @param {object} config.options
*/
export const registerScaledPriceAuthority = async (powers, { options }) => {
const {
instance: { produce: produceInstance },
} = powers;

const { keyword, issuerName = keyword } = options.interchainAssetOptions;

const spaKit = await startScaledPriceAuthority(powers, { options });

const label = scaledPriceFeedName(issuerName);

// publish into agoricNames so that others can await its presence.
// This must stay after registerPriceAuthority above so it's evidence of registration.
// eslint-disable-next-line no-restricted-syntax -- computed property
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,8 @@ export const createPriceFeed = async (

// being after the above awaits means that when this resolves, the consumer
// gets notified that the authority is in the registry and its instance is in
// agoricNames.
// agoricNames. reset() in case we're replacing an existing feed.
produceInstance[AGORIC_INSTANCE_NAME].reset();
produceInstance[AGORIC_INSTANCE_NAME].resolve(faKit.instance);

E(E.get(econCharterKit).creatorFacet).addInstance(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { makeTracer } from '@agoric/internal';
import { E } from '@endo/far';
import { deeplyFulfilled } from '@endo/marshal';

import { startScaledPriceAuthority } from './addAssetToVault.js';
import { scaledPriceFeedName } from './utils.js';

const trace = makeTracer('replaceScaledPA', true);

/**
* @param {BootstrapPowers} powers
* @param {object} config
* @param {object} config.options
*/
export const replaceScaledPriceAuthority = async (powers, { options }) => {
const {
instance: { produce: produceInstance },
} = powers;
const { keyword, issuerName = keyword } = options.interchainAssetOptions;

const spaKit = await startScaledPriceAuthority(powers, { options });

const label = scaledPriceFeedName(issuerName);
produceInstance[label].reset();

// publish into agoricNames so that others can await its presence.
// This must stay after registerPriceAuthority above so it's evidence of registration.
produceInstance[label].resolve(spaKit.instance);
};

/**
* Look up the existing assets known to auctions, and replace the corresponding
* scaledPriceAuthorities. The existing contracts will be left behind to be
* cleaned up later.
*
* @param {ChainBootstrapSpace & BootstrapPowers} powers
* @param {{ options: { scaledPARef: { bundleID: string } } }} options
*/
export const replaceScaledPriceAuthorities = async (powers, { options }) => {
trace('start');
const {
consume: { agoricNamesAdmin, contractKits: contractKitsP, zoe },
} = powers;

const { scaledPARef } = options;

const installationsAdmin = E(agoricNamesAdmin).lookupAdmin('installation');
const [spaInstallation, contractKits] = await Promise.all([
E(E(installationsAdmin).readonly()).lookup('scaledPriceAuthority'),
contractKitsP,
]);

const bundleID = scaledPARef.bundleID;
if (scaledPARef && bundleID) {
await E.when(
E(zoe).installBundleID(bundleID),
installation =>
E(installationsAdmin).update('scaledPriceAuthority', installation),
err =>
console.error(
`🚨 failed to update scaledPriceAuthority installation`,
err,
),
);
trace('installed scaledPriceAuthority bundle', bundleID);
}

// Ask Zoe for the installation for each kit's instance, and return all the
// kits where that matches the given installation.
async function selectKitsWithInstallation(kits) {
/** @type {StartedInstanceKit<any>[]} */
const scaledPAKitMapP = Array.from(kits.values()).map(kit => [
kit,
E(zoe).getInstallationForInstance(kit.instance),
]);
const scaledPAKitMap = await deeplyFulfilled(harden(scaledPAKitMapP));
const scaledPAKitEntries = [];
for (const [instance, installation] of scaledPAKitMap) {
if (spaInstallation === installation) {
scaledPAKitEntries.push(instance);
}
}
return scaledPAKitEntries;
}
const scaledPAKitEntries = await selectKitsWithInstallation(contractKits);

for (const kitEntry of scaledPAKitEntries) {
trace({ kitEntry });

const keyword = kitEntry.label.match(/scaledPriceAuthority-(.*)/)[1];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

relying on a label for correctness is awkward.

Did you consider filtering by installation?

I suppose you want the keyword from the label... another way to get that is from the children of published.priceAuthority... oh... but vstorage is write-only on chain. What a pain.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

relying on a label for correctness is awkward.

I agree. There seemed few enough vats at present that I could verify this produced the correct list. A slightly longer or more dynamic list and I'd have tried to find a better way.

Did you consider filtering by installation?

I didn't, but it's a good idea. Added.

const interchainAssetOptions = { keyword };
await replaceScaledPriceAuthority(powers, {
options: { interchainAssetOptions },
});
}
};

const t = 'replaceScaledPriceAuthority';
export const getManifestForReplaceScaledPriceAuthorities = async (
_ign,
upgradeSPAOptions,
) => ({
manifest: {
[replaceScaledPriceAuthorities.name]: {
consume: {
agoricNamesAdmin: t,
contractKits: t,
priceAuthority: t,
priceAuthorityAdmin: t,
zoe: t,
startUpgradable: t,
},
instance: {
produce: t,
},
},
},
options: { ...upgradeSPAOptions },
});

This file was deleted.

Loading
Loading