Skip to content

Commit

Permalink
chore: get KREAd test running (but failing)
Browse files Browse the repository at this point in the history
 - factor out core-eval-support
  • Loading branch information
dckc committed Nov 7, 2023
1 parent 2f37a92 commit 97335b1
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 186 deletions.
3 changes: 2 additions & 1 deletion proposals/53:kread-start/eval.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ cd /usr/src/upgrade-test-scripts/lib/ && yarn install --frozen-lockfile
cd -

# XXX using Ava serial to script the core-eval
yarn ava upgrade-wf.test.js
yarn ava mn2-start.test.js

220 changes: 35 additions & 185 deletions proposals/53:kread-start/mn2-start.test.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,14 @@
// @ts-check

/**
* @file mainnet-2 contract start test
* @file mainnet-2 contract start test for KREAd
*
* Expects several environment variables:
* If $MN2_PROPOSAL_INFO is not set, no tests are run.
*
* $MN2_PROPOSAL_INFO is a directory with the results of
* running `agoric run xyz-script.js` one or more times.
* Note: include the trailing / in the $MN_PROPOSAL_INFO.
*
* Each time, in addition to any *-permit.json and *.js files,
* stdout is parsed with `parseProposals.js` to produce
* a *-info.json file containing...
* Proposal info is fetched from a release.
*
* @typedef {{
* bundles: string[],
* evals: { permit: string; script: string }[],
* }} ProposalInfo
*
* Only the last path segment of ProposalInfo.bundles is used.
* All bundles must be available in $MN2_PROPOSAL_INFO/bundles/ .
*
* $MN2_INSTANCE is a key in agoricNames.instance
* where the mainnet-2 contract instance is expected to be installed.
* A vstorage node under published by this name is also expected.
*/

import anyTest from 'ava';
Expand All @@ -37,18 +21,24 @@ import dbOpenAmbient from 'better-sqlite3';

// TODO: factor out ambient authority from these
// or at least allow caller to supply authority.
import { mintIST } from '../lib/econHelpers.js';
import { agoric, wellKnownIdentities } from '../lib/cliHelper.js';
import { agoric, wellKnownIdentities } from '../../upgrade-test-scripts/lib/cliHelper.js';
import {
provisionSmartWallet,
voteLatestProposalAndWait,
} from '../lib/commonUpgradeHelpers.js';
} from '../../upgrade-test-scripts/lib/commonUpgradeHelpers.js';

import { makeAgd } from '../lib/agd-lib.js';
import { Far, makeMarshal, makeTranslationTable } from '../lib/unmarshal.js';
import { Fail, NonNullish } from '../lib/assert.js';
import { dbTool } from '../lib/vat-status.js';
import { makeFileRW, makeWebCache, makeWebRd } from '../lib/webAsset.js';
import { makeAgd } from '../../upgrade-test-scripts/lib/agd-lib.js';
import { dbTool } from '../../upgrade-test-scripts/lib/vat-status.js';
import { makeFileRW, makeWebCache, makeWebRd } from '../../upgrade-test-scripts/lib/webAsset.js';
import {
bundleDetail,
ensureISTForInstall,
flags,
getContractInfo,
loadedBundleIds,
testIncludes,
txAbbr,
} from './core-eval-support.js';

/** @typedef {Awaited<ReturnType<typeof makeTestContext>>} TestContext */
/** @type {import('ava').TestFn<TestContext>}} */
Expand All @@ -64,8 +54,12 @@ const test = anyTest;
* voting 2023-09-28 to 2023-10-01
* https://agoric.explorers.guru/proposal/53
*/
const releaseInfo = {
url: 'https://github.com/Kryha/KREAd/releases/tag/KREAd-rc1',
const assetInfo = {
repo:{
release:'https://github.com/Kryha/KREAd/releases/tag/KREAd-rc1',
url:'https://github.com/Kryha/KREAd',
name: 'KREAd',
},
/** @type {Record<string, ProposalInfo>} */
buildAssets: {
'kread-committee-info.json': {
Expand Down Expand Up @@ -96,13 +90,13 @@ const dappAPI = {
};

const staticConfig = {
deposit: '10000000ubld', // 10 BLD
installer: 'gov1', // as in: agd keys show gov1
deposit: '10000000ubld', // 10 BLD. XXX mainnet was 5000 at the time
installer: 'user1', // as in: agd keys show gov1
proposer: 'validator',
collateralPrice: 6, // conservatively low price. TODO: look up
swingstorePath: '~/.agoric/data/agoric/swingstore.sqlite',
releaseAssets: releaseInfo.url.replace('/tag/', '/download/') + '/',
buildInfo: Object.values(releaseInfo.buildAssets),
releaseAssets: assetInfo.repo.release.replace('/tag/', '/download/') + '/',
buildInfo: Object.values(assetInfo.buildAssets),
initialCoins: `20000000ubld`, // enough to provision a smartWallet
accounts: {
krgov1: {
Expand Down Expand Up @@ -147,8 +141,10 @@ const makeTestContext = async (io = {}) => {
tmpName({ prefix: 'assets' }, (err, x) => (err ? reject(err) : resolve(x))),
);
const dest = makeFileRW(td, { fsp, path });
td.teardown(() => assets.remove());
// FIXME Error: `t.teardown()` is not allowed in hooks
// t.teardown(() => assets.remove());
const assets = makeWebCache(src, dest);
console.log(`bundleAssets: ${assets}`)

const config = {
assets,
Expand All @@ -170,126 +166,13 @@ const makeTestContext = async (io = {}) => {

test.before(async t => (t.context = await makeTestContext()));

const testIncludes = (t, needle, haystack, label, sense = true) => {
t.log(needle, sense ? 'in' : 'not in', haystack.length, label, '?');
const check = sense ? t.deepEqual : t.notDeepEqual;
if (sense) {
t.deepEqual(
haystack.filter(c => c === needle),
[needle],
);
} else {
t.deepEqual(
haystack.filter(c => c === needle),
[],
);
}
};

test.serial(`pre-flight: not in agoricNames.instance`, async t => {
const { config, agoric } = t.context;
const { instance: target } = config;
const { instance } = await wellKnownIdentities({ agoric });
testIncludes(t, target, Object.keys(instance), 'instance keys', false);
});

const makeBoardUnmarshal = () => {
const synthesizeRemotable = (_slot, iface) =>
Far(iface.replace(/^Alleged: /, ''), {});

const { convertValToSlot, convertSlotToVal } = makeTranslationTable(
slot => Fail`unknown id: ${slot}`,
synthesizeRemotable,
);

return makeMarshal(convertValToSlot, convertSlotToVal);
};

export const getContractInfo = async (path, io = {}) => {
const m = makeBoardUnmarshal();
const {
agoric: { follow = agoric.follow },
prefix = 'published.',
} = io;
console.log('@@TODO: prevent agoric follow hang', prefix, path);
const txt = await follow('-lF', `:${prefix}${path}`, '-o', 'text');
const { body, slots } = JSON.parse(txt);
return m.fromCapData({ body, slots });
};

// XXX dead code - worth keeping somewhere?
const ensureMintLimit = async (targetNum, manager = 0, unit = 1_000_000) => {
const io = { agoric };
const [{ current }, metrics] = await Promise.all([
getContractInfo(`vaultFactory.managers.manager${manager}.governance`, io),
getContractInfo(`vaultFactory.managers.manager${manager}.metrics`, io),
]);
const { totalDebt } = metrics;
const {
DebtLimit: { value: limit },
} = current;
const nums = {
total: Number(totalDebt.value) / unit,
limit: Number(limit.value) / unit,
target: targetNum,
};
nums.target += nums.total;
console.log(
nums,
nums.limit >= nums.target ? 'limit > target' : 'LIMIT TOO LOW',
);
if (nums.limit >= nums.target) return;
throw Error('raising mint limit not impl');
};

const myISTBalance = async (agd, addr, denom = 'uist', unit = 1_000_000) => {
const coins = await agd.query(['bank', 'balances', addr]);
const coin = coins.balances.find(a => a.denom === denom);
return Number(coin.amount) / unit;
};

const importBundleCost = (bytes, price = 0.002) => {
return bytes * price;
};

/**
* @param {number} myIST
* @param {number} cost
* @param {{
* unit?: number, padding?: number, minInitialDebt?: number,
* collateralPrice: number,
* }} opts
* @returns
*/
const mintCalc = (myIST, cost, opts) => {
const {
unit = 1_000_000,
padding = 1,
minInitialDebt = 6,
collateralPrice,
} = opts;
const { round, max } = Math;
const wantMinted = max(round(cost - myIST + padding), minInitialDebt);
const giveCollateral = round(wantMinted / collateralPrice) + 1;
const sendValue = round(giveCollateral * unit);
return { wantMinted, giveCollateral, sendValue };
};

const loadedBundleIds = swingstore => {
const ids = swingstore`SELECT bundleID FROM bundles`.map(r => r.bundleID);
return ids;
};

/**
* @param {string} cacheFn - e.g. /home/me.agoric/cache/b1-DEADBEEF.json
*/
const bundleDetail = cacheFn => {
const fileName = NonNullish(cacheFn.split('/').at(-1));
const id = fileName.replace(/\.json$/, '');
const hash = id.replace(/^b1-/, '');
return { fileName, endoZipBase64Sha512: hash, id };
};

test.serial('bundles not yet installed', async t => {
const { swingstore, config } = t.context;
const loaded = loadedBundleIds(swingstore);
Expand All @@ -306,7 +189,7 @@ test.serial('bundles not yet installed', async t => {
/** @param {number[]} xs */
const sum = xs => xs.reduce((a, b) => a + b, 0);

/** @param {import('../lib/webAsset.js').WebCache} assets */
/** @param {import('../../upgrade-test-scripts/lib/webAsset.js').WebCache} assets */
const readBundleSizes = async assets => {
const info = staticConfig.buildInfo;
const bundleSizes = await Promise.all(
Expand All @@ -320,39 +203,16 @@ const readBundleSizes = async assets => {
return { bundleSizes, totalSize };
};

const ensureISTForInstall = async (agd, config, { log }) => {
const { proposalDir, installer } = config;
const { bundleSizes, totalSize } = await readBundleSizes(proposalDir);
const cost = importBundleCost(sum(bundleSizes));
log({ bundleSizes, totalSize, cost });

const addr = agd.lookup(installer);
const istBalance = await myISTBalance(agd, addr);

if (istBalance > cost) {
log('balance sufficient', { istBalance, cost });
return;
}
const { sendValue, wantMinted, giveCollateral } = mintCalc(
istBalance,
cost,
config,
);
log({ wantMinted });
await mintIST(addr, sendValue, wantMinted, giveCollateral);
};

test.serial('ensure enough IST to install bundles', async t => {
const { agd, config } = t.context;
await ensureISTForInstall(agd, config, { log: t.log });
const { totalSize } = await readBundleSizes(config.assets);

await ensureISTForInstall(agd, config, totalSize, {
log: t.log,
});
t.pass();
});

const txAbbr = tx => {
const { txhash, code, height, gas_used } = tx;
return { txhash, code, height, gas_used };
};

test.serial('ensure bundles installed', async t => {
const { agd, swingstore, agoric, config, io } = t.context;
const { chainId, assets } = config;
Expand Down Expand Up @@ -392,16 +252,6 @@ test.serial('ensure bundles installed', async t => {
t.is(todo, done);
});

/**
* @param {Record<string, string>} record - e.g. { color: 'blue' }
* @returns {string[]} - e.g. ['--color', 'blue']
*/
const flags = record => {
return Object.entries(record)
.map(([k, v]) => [`--${k}`, v])
.flat();
};

test.serial('core eval prereqs: provision royalty, gov, ...', async t => {
const { agd, config } = t.context;
const { entries } = Object;
Expand Down

0 comments on commit 97335b1

Please sign in to comment.