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: inter-tool bid list #7939

Draft
wants to merge 33 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
e218017
feat(auction): auction publish all bids off-chain
Chris-Hibbert May 4, 2023
0cbe9ab
feat(auction): add timestamp to bid records
Chris-Hibbert Jun 14, 2023
ed21579
refactor: each bid written to a separate node
Chris-Hibbert Jun 27, 2023
223451a
chore: clean up persistence
Chris-Hibbert Jun 30, 2023
6e50dee
Merge branch '7159-auctionBids' into dc-cosmjs-interpose-net
dckc Jul 11, 2023
f0b48ea
chore(inter-protcol): auctionBook type fixes
dckc Jun 20, 2023
3e87dc3
build: inter-cli package / devtools
dckc Jun 16, 2023
302607f
feat(inter-cli): vstorage.proto client, extractStreamCellValue()
dckc Jun 20, 2023
61f69d8
chore(inter-cli): makeTUI - Textual User Interface
dckc Jun 20, 2023
324c085
feat(inter-cli): board client preserving identity from vstorage
dckc Jun 20, 2023
b360219
chore(inter-cli): networkConfig handling with explicit access
dckc Jun 20, 2023
61e9bf8
feat(inter-cli): vbank asset formatters: amount, price, percent
dckc Jun 20, 2023
371182c
chore(inter-cli): lookup(name) -> address in agd keyring
dckc Jun 20, 2023
79c008f
feat(inter-cli): inter auction list-bids command
dckc Jun 20, 2023
30d899f
build(inter-cli): test:c8 (code coverage)
dckc Jul 12, 2023
1bcf8d6
SQUASHME: prune inter-tool auction governance command
dckc Jul 12, 2023
fe02449
SQUASHME: update usage: inter-tool bid list
dckc Jul 12, 2023
5b7f3d7
fixup! chore(inter-cli): networkConfig handling with explicit access
dckc Jul 12, 2023
2193124
SQUASHME: update RPC fixture to match product scenario
dckc Jul 12, 2023
8e58ffe
SQUASHME: update 'bid list' test per product scenario
dckc Jul 12, 2023
791a22e
SQUASHME: format: omit fraction on whole seconds in timestamps
dckc Jul 12, 2023
19d45f0
SQUASHME: no fractional seconds in bid list timestamps
dckc Jul 12, 2023
6e5760d
fixup! SQUASHME: update 'bid list' test per product scenario
dckc Jul 12, 2023
cb8d491
chore: inter-tool help text snapshot tests
dckc Jul 12, 2023
d3e25b8
fixup! build: inter-cli package / devtools
dckc Jul 12, 2023
626d584
fixup! build: inter-cli package / devtools
dckc Jul 12, 2023
85cd54b
SQUASHME: inter-tool: never mind place-bid command
dckc Jul 12, 2023
ac3a316
fixup! feat(inter-cli): board client preserving identity from vstorage
dckc Jul 12, 2023
b43eb92
SQUASHME: prune incomplete bid command
dckc Jul 12, 2023
0938631
SQUASHME: inter-tool: prune left-over signing code
dckc Jul 12, 2023
eb55d15
SQUASHME: inter-tool: tidy / docs
dckc Jul 12, 2023
c6f6ca8
feat(inter-tool): disclaimer (WIP NEEDSTEST)
dckc Jul 13, 2023
6045ee1
SQUASHME: inter-tool: pick among rpcAddrs
dckc Jul 13, 2023
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
3 changes: 2 additions & 1 deletion packages/agoric-cli/src/sdk-package-names.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,6 @@ export default [
"@agoric/xsnap-lockdown",
"@agoric/zoe",
"@agoric/zone",
"agoric"
"agoric",
"inter-cli"
];
10 changes: 10 additions & 0 deletions packages/inter-cli/jsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// This file can contain .js-specific Typescript compiler config.
{
"extends": "../../tsconfig.json",
"include": [
"globals.d.ts",
"scripts/**/*.js",
"src/**/*.js",
"test/**/*.js"
]
}
47 changes: 47 additions & 0 deletions packages/inter-cli/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"name": "inter-cli",
"version": "0.1.0",
"description": "TODO",
"type": "module",
"scripts": {
"build": "exit 0",
"test": "ava",
"test:c8": "c8 $C8_OPTIONS ava --config=ava-nesm.config.js",
"lint-fix": "yarn lint:eslint --fix",
"lint": "run-s --continue-on-error lint:*",
"lint:types": "tsc -p jsconfig.json",
"lint:eslint": "eslint ."
},
"bin": {
"inter-tool": "src/inter-tool.js"
},
"author": "Agoric",
"license": "Apache-2.0",
"dependencies": {
"@agoric/casting": "^0.4.2",
"@agoric/cosmic-proto": "^0.3.0",
"@agoric/ertp": "^0.16.2",
"@agoric/internal": "^0.3.2",
"@cosmjs/encoding": "^0.30.1",
"@cosmjs/stargate": "^0.30.1",
"@cosmjs/tendermint-rpc": "^0.30.1",
"@endo/far": "^0.2.18",
"@endo/init": "^0.5.56",
"@endo/marshal": "^0.8.5",
"@endo/nat": "^4.1.27",
"@endo/patterns": "^0.2.2",
"anylogger": "^0.21.0",
"commander": "^10.0.0",
"jessie.js": "^0.3.2"
},
"devDependencies": {
"@fast-check/ava": "^1.1.5"
},
"ava": {
"files": [
"test/**/test-*.js"
],
"timeout": "20m",
"workerThreads": false
}
}
144 changes: 144 additions & 0 deletions packages/inter-cli/src/commands/auction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// @ts-check
// @jessie-check

import { M, mustMatch } from '@endo/patterns';
import { BrandShape, RatioShape } from '@agoric/ertp/src/typeGuards.js';
import { makeBoardClient } from '../lib/boardClient.js';
import { makeVstorageQueryService } from '../lib/vstorage.js';
import { formatTimestamp, makeAssetFormatters } from '../lib/format.js';

// XXX push down into @agoric/ERTP?
const NatAmountShape = harden({ brand: BrandShape, value: M.nat() });
const TimeStampShape = {
timerBrand: M.remotable('timerBrand'),
absValue: M.nat(),
};

const bidData = harden({
timestamp: TimeStampShape,
sequence: M.nat(),
balance: NatAmountShape,
wanted: NatAmountShape,
exitAfterBuy: M.boolean(),
});

const shapeLeaves = harden({
ScaledBidData: {
bidScaling: RatioShape,
...bidData,
},

PricedBidData: {
price: RatioShape,
...bidData,
},
});

const shape = harden({
...shapeLeaves,
BidDataNotification: M.arrayOf(
M.or(shapeLeaves.ScaledBidData, shapeLeaves.PricedBidData),
),
});

/**
*
* @param {unknown} specimen
* @returns { asserts specimen is BidDataNotification }
*
* XXX contract should export this type:
*
* @typedef {{
* balance: Amount<'nat'>,
* exitAfterBuy: boolean,
* sequence: bigint,
* timestamp: import('@agoric/time').Timestamp,
* wanted: Amount<'nat'>,
* } & ({ price: Ratio } | { bidScaling: Ratio})} BidData1
* @typedef {BidData1[]} BidDataNotification
*/
const assertBidDataNotification = specimen => {
mustMatch(specimen, shape.BidDataNotification);
};

/**
*
* @param {ScaledBidData | PricedBidData} bid
* @param {ReturnType<typeof import('../lib/format').makeAssetFormatters>} fmt
*
* @typedef {import('@agoric/inter-protocol/src/auction/auctionBook.js').ScaledBidData} ScaledBidData
* @typedef {import('@agoric/inter-protocol/src/auction/auctionBook.js').PricedBidData} PricedBidData
*/
const fmtBid = (bid, fmt) => {
const { timestamp, sequence, balance, wanted, exitAfterBuy, ...more } = bid;
const p = 'price' in bid ? { price: fmt.price(bid.price) } : {};
const b = 'bidScaling' in bid ? { bidScaling: fmt.rate(bid.bidScaling) } : {};
const exit = exitAfterBuy ? { exitAfterBuy } : {};
// @ts-expect-error XXX how to do this?
const { price: _p, bidScaling: _b, ...rest } = more;
const info = harden({
// assume timerBrand gives values in seconds
timestamp: formatTimestamp(timestamp.absValue),
sequence: Number(sequence),
...p,
...b,
balance: fmt.amount(balance),
wanted: fmt.amount(wanted),
...exit,
...rest,
});
return info;
};

/**
*
* @param {import('commander').Command} interCmd
* @param {object} io
* @param {import('../lib/tui').TUI} io.tui
* @param {() => Promise<ReturnType<import('../lib/vstorage.js').makeBatchQuery>>} io.getBatchQuery
* @param {() => Promise<import('@cosmjs/tendermint-rpc').RpcClient>} io.makeRpcClient
*
* @typedef {import('@cosmjs/tendermint-rpc').RpcClient} RpcClient
*/
export const addBidCommand = (
interCmd,
{ tui, getBatchQuery, makeRpcClient },
) => {
const bidCmd = interCmd
.command('bid')
.description('Add a Bid command/operation');

const makeBoard = () => getBatchQuery().then(makeBoardClient);

bidCmd
.command('list')
.description('List Bids operation')
.option('--book', 'auction book number', Number, 0)
.action(async (/** @type {{book: number}} */ { book }) => {
const bidsPart = 'schedule'; // XXX something goofy is going on in the contract
const [board, queryService] = await Promise.all([
makeBoard(),
makeRpcClient().then(makeVstorageQueryService),
]);
const agoricNames = await board.provideAgoricNames();

const { children: bidKeys } = await queryService.Children({
path: `published.auction.book${book}.${bidsPart}`,
});
const { length: n } = bidKeys;
const more = n > 3 ? '...' : '';
console.warn('fetching', n, 'bids:', bidKeys.slice(0, 3), more);

console.warn('TODO: pagination');
const bids = await board.readBatch(
bidKeys.map(k => `published.auction.book${book}.${bidsPart}.${k}`),
);
assertBidDataNotification(bids);
const fmt = makeAssetFormatters(agoricNames.vbankAsset);
for (const bid of bids) {
tui.show(fmtBid(bid, fmt));
}
});

return bidCmd;
};
76 changes: 76 additions & 0 deletions packages/inter-cli/src/inter-tool.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/bin/env node
// @ts-check
// @jessie-check
/* global globalThis */

import '@endo/init';
import process from 'process';

import anylogger from 'anylogger';
import { createCommand, CommanderError } from 'commander';

import { makeHttpClient } from '@agoric/casting/src/makeHttpClient.js';

import { makeTUI } from './lib/tui.js';
import { addBidCommand } from './commands/auction.js';
import { getNetworkConfig } from './lib/networkConfig.js';
import { makeBatchQuery } from './lib/vstorage.js';

const DISCLAIMER =
'Source code licensed under Apache 2.0. Use at your own risk.';

/**
* Create and run the inter command,
* portioning out authority as needed.
*/
const main = async () => {
const logger = anylogger('inter');
const tui = makeTUI({ stdout: process.stdout, logger });

const { env } = process;
env.ACK_IST_RISK || tui.warn(DISCLAIMER);

let config;
// NOTE: delay getNetworkConfig() and all other I/O
// until a command .action() is run
const provideConfig = async () => {
await null;
if (config) return config;
config = await getNetworkConfig(env, { fetch: globalThis.fetch });
return config;
};

const pick = xs => xs[Math.floor(Math.random() * xs.length)];

// cosmjs-based RPC client is only used for .Children()
const makeRpcClient = () =>
provideConfig().then(c => {
const rpcAddr = pick(c.rpcAddrs);
return makeHttpClient(rpcAddr, globalThis.fetch);
});

// cosmjs/protobuf tooling doesn't support batch query,
// so we re-implement it using fetch().
const getBatchQuery = () =>
provideConfig().then(({ rpcAddrs }) =>
makeBatchQuery(globalThis.fetch, rpcAddrs),
);

const interCmd = createCommand('inter-tool').description(
'Inter Protocol auction bid query',
);
addBidCommand(interCmd, { tui, getBatchQuery, makeRpcClient });

try {
interCmd.parseAsync(process.argv);
} catch (err) {
if (err instanceof CommanderError) {
console.error(err.message);
} else {
console.error(err); // CRASH! show stack trace
}
process.exit(1);
}
};

main();
96 changes: 96 additions & 0 deletions packages/inter-cli/src/lib/agd-lib.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// @ts-check
// @jessie-check

const { freeze } = Object;

const agdBinary = 'agd';

/** @param {{ execFileSync: typeof import('child_process').execFileSync }} io */
export const makeAgd = ({ execFileSync }) => {
console.warn('XXX is sync IO essential?');

/** @param {{ home?: string, keyringBackend?: string, rpcAddrs?: string[] }} keyringOpts */
const make = ({ home, keyringBackend, rpcAddrs } = {}) => {
const keyringArgs = [
...(home ? ['--home', home] : []),
...(keyringBackend ? [`--keyring-backend`, keyringBackend] : []),
];
console.warn('XXX: rpcAddrs after [0] are ignored');
const nodeArgs = [...(rpcAddrs ? [`--node`, rpcAddrs[0]] : [])];

const l = a => {
console.log(a); // XXX unilateral logging by a library... iffy
return a;
};
/**
* @param {string[]} args
* @param {*} [opts]
*/
const exec = (args, opts) =>
execFileSync(agdBinary, l(args), opts).toString();

const outJson = ['--output', 'json'];

const ro = freeze({
status: async () => JSON.parse(exec([...nodeArgs, 'status'])),
/**
* @param {[kind: 'tx', txhash: string]} qArgs
*/
query: async qArgs => {
const out = await exec(['query', ...qArgs, ...nodeArgs, ...outJson], {
stdio: ['ignore', 'pipe', 'ignore'],
});
return JSON.parse(out);
},
});
const nameHub = freeze({
/**
* @param {string[]} path
* NOTE: synchronous I/O
*/
lookup: (...path) => {
if (!Array.isArray(path)) {
// TODO: use COND || Fail``
throw TypeError();
}
if (path.length !== 1) {
throw Error(`path length limited to 1: ${path.length}`);
}
const [name] = path;
const txt = exec(['keys', 'show', `--address`, name, ...keyringArgs]);
return txt.trim();
},
});
const rw = freeze({
/**
* TODO: gas
*
* @param {string[]} txArgs
* @param {{ chainId: string, from: string, yes?: boolean }} opts
*/
tx: async (txArgs, { chainId, from, yes }) => {
const yesArg = yes ? ['--yes'] : [];
const args = [
...nodeArgs,
...[`--chain-id`, chainId],
...keyringArgs,
...[`--from`, from],
'tx',
...txArgs,
...['--broadcast-mode', 'block'],
...yesArg,
...outJson,
];
const out = exec(args);
return JSON.parse(out);
},
...ro,
...nameHub,
readOnly: () => ro,
nameHub: () => nameHub,
withOpts: opts => make({ home, keyringBackend, rpcAddrs, ...opts }),
});
return rw;
};
return make();
};
Loading
Loading