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

feat: add upgrade-15 (proposal 74) #157

Merged
merged 2 commits into from
May 26, 2024
Merged
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
1 change: 1 addition & 0 deletions proposals/74:upgrade-15/.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/74:upgrade-15/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Proposal to upgrade the chain software to upgrade-15

This software upgrade executes core proposals during the upgrade block, as
defined by the `agoric-upgrade-15` upgrade handler. See `upgrade15Handler` in
`agoric-sdk/golang/cosmos/app/app.go`.
38 changes: 38 additions & 0 deletions proposals/74:upgrade-15/exit-reclaim.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import test from 'ava';
import { $ } from 'execa';
import { execFileSync } from 'node:child_process';
import { makeAgd, waitForBlock } from '@agoric/synthetic-chain';

const offerId = 'bad-invitation-15'; // cf. prepare.sh
const from = 'gov1';

test('exitOffer tool reclaims stuck payment', async t => {
const showAndExec = (file, args, opts) => {
console.log('$', file, ...args);
return execFileSync(file, args, opts);
};

// @ts-expect-error string is not assignable to Buffer
const agd = makeAgd({ execFileSync: showAndExec }).withOpts({
keyringBackend: 'test',
});

const addr = await agd.lookup(from);
t.log(from, 'addr', addr);

const getBalance = async target => {
const { balances } = await agd.query(['bank', 'balances', addr]);
const { amount } = balances.find(({ denom }) => denom === target);
return Number(amount);
};

const before = await getBalance('uist');
t.log('uist balance before:', before);

await $`node ./exitOffer.js --id ${offerId} --from ${from}`;

await waitForBlock(2);
const after = await getBalance('uist');
t.log('uist balance after:', after);
t.true(after > before);
});
97 changes: 97 additions & 0 deletions proposals/74:upgrade-15/exitOffer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Note: limit imports to node modules for portability
import { parseArgs, promisify } from 'node:util';
import { execFile } from 'node:child_process';
import { writeFile, mkdtemp, rm } from 'node:fs/promises';
import { join } from 'node:path';

const options = /** @type {const} */ ({
id: { type: 'string' },
from: { type: 'string' },
bin: { type: 'string', default: '/usr/src/agoric-sdk/node_modules/.bin' },
});

const Usage = `
Try to exit an offer, reclaiming any associated payments.

node exitOffer.js --id ID --from FROM [--bin PATH]

Options:
--id <offer id>
--from <address or key name>

--bin <path to agoric and agd> default: ${options.bin.default}
`;

const badUsage = () => {
const reason = new Error(Usage);
reason.name = 'USAGE';
throw reason;
};

const { stringify: q } = JSON;
// limited to JSON data: no remotables/promises; no undefined.
const toCapData = data => ({ body: `#${q(data)}`, slots: [] });

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

const execP = promisify(execFile);

const showAndRun = (file, args) => {
console.log('$', file, ...args);
return execP(file, args);
};

const withTempFile = async (tail, fn) => {
const tmpDir = await mkdtemp('offers-');
const tmpFile = join(tmpDir, tail);
try {
const result = await fn(tmpFile);
return result;
} finally {
await rm(tmpDir, { recursive: true, force: true }).catch(err =>
console.error(err),
);
}
};

const doAction = async (action, from) => {
await withTempFile('offer.json', async tmpOffer => {
await writeFile(tmpOffer, q(toCapData(action)));

const out = await showAndRun('agoric', [
'wallet',
...flags({ 'keyring-backend': 'test' }),
'send',
...flags({ offer: tmpOffer, from }),
]);
return out.stdout;
});
};

const main = async (argv, env) => {
const { values } = parseArgs({ args: argv.slice(2), options });
const { id: offerId, from, bin } = values;
(offerId && from) || badUsage();

env.PATH = `${bin}:${env.PATH}`;
const action = { method: 'tryExitOffer', offerId };
const out = await doAction(action, from);
console.log(out);
};

main(process.argv, process.env).catch(e => {
if (e.name === 'USAGE' || e.code === 'ERR_PARSE_ARGS_UNKNOWN_OPTION') {
console.error(e.message);
} else {
console.error(e);
}
process.exit(1);
});
16 changes: 16 additions & 0 deletions proposals/74:upgrade-15/initial.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import test from 'ava';

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

const vats = {
walletFactory: { incarnation: 3 },
zoe: { incarnation: 1 },
};

test(`vat details`, async t => {
await null;
for (const [vatName, expected] of Object.entries(vats)) {
const actual = await getVatDetails(vatName);
t.like(actual, expected, `${vatName} details mismatch`);
}
});
28 changes: 28 additions & 0 deletions proposals/74:upgrade-15/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"agoricProposal": {
"releaseNotes": "https://github.com/Agoric/agoric-sdk/releases/tag/agoric-upgrade-15",
"sdkImageTag": "43",
"planName": "agoric-upgrade-15",
"upgradeInfo": {
"binaries": {
"any": "https://github.com/Agoric/agoric-sdk/archive/734e8635002e01b3e27477e93998dda942c7fae8.zip//agoric-sdk-734e8635002e01b3e27477e93998dda942c7fae8?checksum=sha256:2d8ace2ab8b3f998336c63847925de63fa9801bc3b5219129d1b193e2b12270a"
},
"source": "https://github.com/Agoric/agoric-sdk/archive/734e8635002e01b3e27477e93998dda942c7fae8.zip?checksum=sha256:2d8ace2ab8b3f998336c63847925de63fa9801bc3b5219129d1b193e2b12270a"
},
"type": "Software Upgrade Proposal"
},
"type": "module",
"license": "Apache-2.0",
"dependencies": {
"@agoric/synthetic-chain": "^0.1.0",
"ava": "^5.3.1"
},
"ava": {
"concurrency": 1,
"serial": true
},
"scripts": {
"agops": "yarn --cwd /usr/src/agoric-sdk/ --silent agops"
},
"packageManager": "yarn@4.1.1"
}
29 changes: 29 additions & 0 deletions proposals/74:upgrade-15/prepare.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/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.

printISTBalance() {
addr=$(agd keys show -a "$1" --keyring-backend=test)
agd query bank balances "$addr" -o json \
| jq -c '.balances[] | select(.denom=="uist")'

}

echo TEST: Offer with bad invitation
printISTBalance gov1

badInvitationOffer=$(mktemp)
cat > "$badInvitationOffer" << 'EOF'
{"body":"#{\"method\":\"executeOffer\",\"offer\":{\"id\":\"bad-invitation-15\",\"invitationSpec\":{\"callPipe\":[[\"badMethodName\"]],\"instancePath\":[\"reserve\"],\"source\":\"agoricContract\"},\"proposal\":{\"give\":{\"Collateral\":{\"brand\":\"$0.Alleged: IST brand\",\"value\":\"+15000\"}}}}}","slots":["board0257"]}
EOF

PATH=/usr/src/agoric-sdk/node_modules/.bin:$PATH
agops perf satisfaction --keyring-backend=test send --executeOffer "$badInvitationOffer" --from gov1 || true

printISTBalance gov1
6 changes: 6 additions & 0 deletions proposals/74:upgrade-15/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash

# Place here any test that should be executed using the executed proposal.
# The effects of this step are not persisted in further proposal layers.

yarn ava
12 changes: 12 additions & 0 deletions proposals/74:upgrade-15/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"allowJs": true,
"checkJs": true,
"strict": false,
"strictNullChecks": true,
"noImplicitThis": true
}
}
Loading