Skip to content

Commit

Permalink
feat: Add agoric-upgrade-17 (proposal 78)
Browse files Browse the repository at this point in the history
  • Loading branch information
gibson042 committed Oct 18, 2024
1 parent 4a9fda3 commit c56f0a3
Show file tree
Hide file tree
Showing 15 changed files with 2,984 additions and 0 deletions.
6 changes: 6 additions & 0 deletions proposals/78:upgrade-17/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Generated Artifacts to Ignore in CI
add-LEMONS/
add-OLIVES/
upgrade-bank/
upgrade-provisionPool/
upgrade-orch-core/
1 change: 1 addition & 0 deletions proposals/78:upgrade-17/.yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodeLinker: node-modules
5 changes: 5 additions & 0 deletions proposals/78:upgrade-17/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Proposal to upgrade the chain software

This reflects agoric-upgrade-17, which was adopted on mainnet by [governance
proposal 78](https://ping.pub/agoric/gov/78) at block height 16947291 and
block time 2024-10-07T20:09:27.215835092Z.
208 changes: 208 additions & 0 deletions proposals/78:upgrade-17/agd-tools.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
import {
agd,
agops,
agopsLocation,
CHAINID,
executeCommand,
executeOffer,
GOV1ADDR,
GOV2ADDR,
GOV3ADDR,
newOfferId,
VALIDATORADDR,
} from '@agoric/synthetic-chain';

const ORACLE_ADDRESSES = [GOV1ADDR, GOV2ADDR, GOV3ADDR];

export const BID_OFFER_ID = 'bid-vaultUpgrade-test3';

const queryVstorage = path =>
agd.query('vstorage', 'data', '--output', 'json', path);

// XXX use endo/marshal?
const getQuoteBody = async path => {
const queryOut = await queryVstorage(path);

const body = JSON.parse(JSON.parse(queryOut.value).values[0]);
return JSON.parse(body.body.substring(1));
};

export const getOracleInstance = async price => {
const instanceRec = await queryVstorage(`published.agoricNames.instance`);

const value = JSON.parse(instanceRec.value);
const body = JSON.parse(value.values.at(-1));

const feeds = JSON.parse(body.body.substring(1));
const feedName = `${price}-USD price feed`;

const key = Object.keys(feeds).find(k => feeds[k][0] === feedName);
if (key) {
return body.slots[key];
}
return null;
};

export const checkForOracle = async (t, name) => {
const instance = await getOracleInstance(name);
t.truthy(instance);
};

export const registerOraclesForBrand = async (brandIn, oraclesByBrand) => {
await null;
const promiseArray = [];

const oraclesWithID = oraclesByBrand.get(brandIn);
for (const oracle of oraclesWithID) {
const { address, offerId } = oracle;
promiseArray.push(
executeOffer(
address,
agops.oracle('accept', '--offerId', offerId, `--pair ${brandIn}.USD`),
),
);
}

return Promise.all(promiseArray);
};

/**
* Generate a consistent map of oracleIDs for a brand that can be used to
* register oracles or to push prices. The baseID changes each time new
* invitations are sent/accepted, and need to be maintained as constants in
* scripts that use the oracles. Each oracleAddress and brand needs a unique
* offerId, so we create recoverable IDs using the brandName and oracle id,
* mixed with the upgrade at which the invitations were accepted.
*
* @param {string} baseId
* @param {string} brandName
*/
const addOraclesForBrand = (baseId, brandName) => {
const oraclesWithID = [];
for (let i = 0; i < ORACLE_ADDRESSES.length; i += 1) {
const oracleAddress = ORACLE_ADDRESSES[i];
const offerId = `${brandName}.${baseId}.${i}`;
oraclesWithID.push({ address: oracleAddress, offerId });
}
return oraclesWithID;
};

export const addPreexistingOracles = async (brandIn, oraclesByBrand) => {
await null;

const oraclesWithID = [];
for (let i = 0; i < ORACLE_ADDRESSES.length; i += 1) {
const oracleAddress = ORACLE_ADDRESSES[i];

const path = `published.wallet.${oracleAddress}.current`;
const wallet = await getQuoteBody(path);
const idToInvitation = wallet.offerToUsedInvitation.find(([k]) => {
return !isNaN(k[0]);
});
if (idToInvitation) {
oraclesWithID.push({
address: oracleAddress,
offerId: idToInvitation[0],
});
} else {
console.log('AGD addO skip', oraclesWithID);
}
}

oraclesByBrand.set(brandIn, oraclesWithID);
};

/**
* Generate a consistent map of oracleIDs and brands that can be used to
* register oracles or to push prices. The baseID changes each time new
* invitations are sent/accepted, and need to be maintained as constants in
* scripts that use these records to push prices.
*
* @param {string} baseId
* @param {string[]} brandNames
*/
export const generateOracleMap = (baseId, brandNames) => {
const oraclesByBrand = new Map();
for (const brandName of brandNames) {
const oraclesWithID = addOraclesForBrand(baseId, brandName);
oraclesByBrand.set(brandName, oraclesWithID);
}
return oraclesByBrand;
};

export const pushPrices = (price, brandIn, oraclesByBrand) => {
const promiseArray = [];

for (const oracle of oraclesByBrand.get(brandIn)) {
promiseArray.push(
executeOffer(
oracle.address,
agops.oracle(
'pushPriceRound',
'--price',
price,
'--oracleAdminAcceptOfferId',
oracle.offerId,
),
),
);
}

return Promise.all(promiseArray);
};

export const getPriceQuote = async price => {
const path = `published.priceFeed.${price}-USD_price_feed`;
const body = await getQuoteBody(path);
return body.amountOut.value;
};

export const agopsInter = (...params) => {
const newParams = ['inter', ...params];
return executeCommand(agopsLocation, newParams);
};

export const createBid = (price, addr, offerId) => {
return agopsInter(
'bid',
'by-price',
`--price ${price}`,
`--give 1.0IST`,
'--from',
addr,
'--keyring-backend test',
`--offer-id ${offerId}`,
);
};

export const getLiveOffers = async addr => {
const path = `published.wallet.${addr}.current`;
const body = await getQuoteBody(path);
return body.liveOffers;
};

export const getAuctionCollateral = async index => {
const path = `published.auction.book${index}`;
const body = await getQuoteBody(path);
return body.collateralAvailable.value;
};

export const getVaultPrices = async index => {
const path = `published.vaultFactory.managers.manager${index}.quotes`;
const body = await getQuoteBody(path);
return body.quoteAmount;
};

export const bankSend = (addr, wanted) => {
const chain = ['--chain-id', CHAINID];
const from = ['--from', VALIDATORADDR];
const testKeyring = ['--keyring-backend', 'test'];
const noise = [...from, ...chain, ...testKeyring, '--yes'];

return agd.tx('bank', 'send', VALIDATORADDR, addr, wanted, ...noise);
};

export const getProvisionPoolMetrics = async () => {
const path = `published.provisionPool.metrics`;
return getQuoteBody(path);
};
21 changes: 21 additions & 0 deletions proposals/78:upgrade-17/initial.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import test from 'ava';

import { getVatDetails } from '@agoric/synthetic-chain';

const vats = {
network: { incarnation: 1 },
ibc: { incarnation: 1 },
localchain: { incarnation: 1 },
orchestration: { incarnation: 0 },
transfer: { incarnation: 1 },
walletFactory: { incarnation: 4 },
zoe: { incarnation: 2 },
};

test(`vat details`, async t => {
const actual = {};
for await (const vatName of Object.keys(vats)) {
actual[vatName] = await getVatDetails(vatName);
}
t.like(actual, vats, `vat details are alike`);
});
33 changes: 33 additions & 0 deletions proposals/78:upgrade-17/localchain.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import test from 'ava';

import { agd, evalBundles, waitForBlock } from '@agoric/synthetic-chain';

const SUBMISSION_DIR = 'localchaintest-submission';

const readPublished = async path => {
const { value } = await agd.query(
'vstorage',
'data',
'--output',
'json',
`published.${path}`,
);
if (value === '') {
return undefined;
}
const obj = JSON.parse(value);
return obj.values[0];
};

// The testing assertions are in the submission that runs in the core-eval.
// The test here runs that and confirms the eval made it through all the assertions.
test(`localchain passes tests`, async t => {
await evalBundles(SUBMISSION_DIR);

const nodePath = 'test.localchain';
const nodeValue = JSON.stringify({ success: true });

await waitForBlock(2); // enough time for core eval to execute ?

t.is(await readPublished(nodePath), nodeValue);
});
31 changes: 31 additions & 0 deletions proposals/78:upgrade-17/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"agoricProposal": {
"releaseNotes": "https://github.com/Agoric/agoric-sdk/releases/tag/agoric-upgrade-17",
"sdkImageTag": "49",
"planName": "agoric-upgrade-17",
"upgradeInfo": {
"binaries": {
"any": "https://github.com/Agoric/agoric-sdk/archive/5259430561693bfcf58516c3ea54123895859708.zip//agoric-sdk-5259430561693bfcf58516c3ea54123895859708?checksum=sha256:43a4b4e31bb71840b2cbc47ba80b97f904baa14aaeb0b2d26565137ff2668644"
},
"source": "https://github.com/Agoric/agoric-sdk/archive/5259430561693bfcf58516c3ea54123895859708.zip?checksum=sha256:43a4b4e31bb71840b2cbc47ba80b97f904baa14aaeb0b2d26565137ff2668644"
},
"type": "Software Upgrade Proposal"
},
"type": "module",
"license": "Apache-2.0",
"dependencies": {
"@agoric/synthetic-chain": "^0.1.0",
"ava": "^5.3.1"
},
"ava": {
"concurrency": 1,
"timeout": "2m",
"files": [
"!submission"
]
},
"scripts": {
"agops": "yarn --cwd /usr/src/agoric-sdk/ --silent agops"
},
"packageManager": "[email protected]"
}
9 changes: 9 additions & 0 deletions proposals/78:upgrade-17/prepare.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash

# Exit when any command fails
set -uxeo pipefail

# Place here any actions that should happen before the upgrade is proposed. The
# actions are executed in the previous chain software, and the effects are
# persisted so they can be used in the steps after the upgrade is complete,
# such as in the "use" or "test" steps, or further proposal layers.
8 changes: 8 additions & 0 deletions proposals/78:upgrade-17/priceFeed-follower-auction.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import test from 'ava';
import { getDetailsMatchingVats } from './vatDetails.js';

test('new auction vat', async t => {
const details = await getDetailsMatchingVats('auctioneer');
// This query matches both the auction and its governor, so 2*2
t.is(Object.keys(details).length, 4);
});
Loading

0 comments on commit c56f0a3

Please sign in to comment.