Skip to content

Commit

Permalink
consolidate lib
Browse files Browse the repository at this point in the history
  • Loading branch information
Dan Connoly authored and turadg committed Nov 1, 2023
1 parent a8e7e91 commit b76aace
Show file tree
Hide file tree
Showing 10 changed files with 683 additions and 12 deletions.
119 changes: 119 additions & 0 deletions upgrade-test-scripts/lib/agd-lib.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// @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]] : [])];

// TODO: verbose option
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, args, opts).toString();

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

const ro = freeze({
status: async () => JSON.parse(exec([...nodeArgs, 'status'])),
/**
* @param {
* | [kind: 'tx', txhash: string]
* | [mod: 'vstorage', kind: 'data' | 'children', path: string]
* } qArgs
*/
query: async qArgs => {
const out = await exec(['query', ...qArgs, ...nodeArgs, ...outJson], {
stdio: ['ignore', 'pipe', 'ignore'],
});
try {
return JSON.parse(out);
} catch (e) {
console.error(e);
console.info('output:', 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);
try {
return JSON.parse(out);
} catch (e) {
console.error(e);
console.info('output:', out);
}
},
...ro,
...nameHub,
readOnly: () => ro,
nameHub: () => nameHub,
keys: {
add: (name, mnemonic) => {
return execFileSync(
agdBinary,
[...keyringArgs, 'keys', 'add', name, '--recover'],
{ input: mnemonic },
).toString();
},
},
withOpts: opts => make({ home, keyringBackend, rpcAddrs, ...opts }),
});
return rw;
};
return make();
};
21 changes: 21 additions & 0 deletions upgrade-test-scripts/lib/assert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export const Fail = (template, ...args) => {
throw Error(String.raw(template, ...args.map(val => String(val))));
};

export const assert = (cond, msg = 'check failed') => {
if (!cond) {
throw Error(msg);
}
};

assert.typeof = (val, type) => {
if (typeof val !== type) {
throw Error(`expected ${type}, got ${typeof val}`);
}
};

/** @type {<T>(val: T | undefined) => T} */
export const NonNullish = val => {
if (!val) throw Error('required');
return val;
};
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,54 @@ export const bundleSource = async (filePath, bundleName) => {
console.log(output.stderr);
return `/tmp/bundle-${bundleName}.json`;
};

export const wellKnownIdentities = async (io = {}) => {
const { agoric: { follow = agoric.follow } = {} } = io;
const zip = (xs, ys) => xs.map((x, i) => [x, ys[i]]);
const fromSmallCapsEntries = txt => {
const { body, slots } = JSON.parse(txt);
const theEntries = zip(JSON.parse(body.slice(1)), slots).map(
([[name, ref], boardID]) => {
const iface = ref.replace(/^\$\d+\./, '');
return [name, { iface, boardID }];
},
);
return Object.fromEntries(theEntries);
};

const installation = fromSmallCapsEntries(
await follow('-lF', ':published.agoricNames.installation', '-o', 'text'),
);

const instance = fromSmallCapsEntries(
await follow('-lF', ':published.agoricNames.instance', '-o', 'text'),
);

const brand = fromSmallCapsEntries(
await follow('-lF', ':published.agoricNames.brand', '-o', 'text'),
);

return { brand, installation, instance };
};

export const smallCapsContext = () => {
const slots = []; // XXX global mutable state
const smallCaps = {
Nat: n => `+${n}`,
// XXX mutates obj
ref: obj => {
if (obj.ix) return obj.ix;
const ix = slots.length;
slots.push(obj.boardID);
obj.ix = `$${ix}.Alleged: ${obj.iface}`;
return obj.ix;
},
};

const toCapData = body => {
const capData = { body: `#${JSON.stringify(body)}`, slots };
return JSON.stringify(capData);
};

return { smallCaps, toCapData };
};
Original file line number Diff line number Diff line change
Expand Up @@ -177,16 +177,19 @@ export const voteLatestProposalAndWait = async () => {
'test',
);

let status = '';

do {
const proposalData = await agd.query('gov', 'proposal', lastProposalId);
status = proposalData.status;
console.log(`Waiting for proposal to pass (status=${status})`);
} while (
status !== 'PROPOSAL_STATUS_REJECTED' &&
status !== 'PROPOSAL_STATUS_PASSED'
);
let info = {};
for (
;
info.status !== 'PROPOSAL_STATUS_REJECTED' &&
info.status !== 'PROPOSAL_STATUS_PASSED';
await waitForBlock()
) {
info = await agd.query('gov', 'proposal', lastProposalId);
console.log(
`Waiting for proposal ${lastProposalId} to pass (status=${info.status})`,
);
}
return info;
};

const Fail = (template, ...args) => {
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const closeVault = (address, vaultId, mint) => {
);
};

export const mintIST = async (addr, sendValue, giveCollateral, wantMinted) => {
export const mintIST = async (addr, sendValue, wantMinted, giveCollateral) => {
await agd.tx(
'bank',
'send',
Expand All @@ -69,5 +69,5 @@ export const mintIST = async (addr, sendValue, giveCollateral, wantMinted) => {
'test',
'--yes',
);
await openVault(addr, giveCollateral, wantMinted);
await openVault(addr, wantMinted, giveCollateral);
};
143 changes: 143 additions & 0 deletions upgrade-test-scripts/lib/unmarshal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// @ts-check
'use strict';

const {
create,
entries,
fromEntries,
freeze,
keys,
setPrototypeOf,
prototype: objectPrototype,
} = Object;
const { isArray } = Array;

const sigilDoc = {
'!': 'escaped string',
'+': `non-negative bigint`,
'-': `negative bigint`,
'#': `manifest constant`,
'%': `symbol`,
$: `remotable`,
'&': `promise`,
};
const sigils = keys(sigilDoc).join('');

/** @type {<V, U>(obj: Record<string, V>, f: (v: V) => U) => Record<string, U>} */
const objMap = (obj, f) =>
fromEntries(entries(obj).map(([p, v]) => [f(p), f(v)]));

const { freeze: harden } = Object; // XXX

const makeMarshal = (_v2s, convertSlotToVal = (s, _i) => s) => {
const fromCapData = ({ body, slots }) => {
const recur = v => {
switch (typeof v) {
case 'boolean':
case 'number':
return v;
case 'string':
if (v === '') return v;
const sigil = v.slice(0, 1);
if (!sigils.includes(sigil)) return v;
switch (sigil) {
case '!':
return v.slice(1);
case '+':
return BigInt(v.slice(1));
case '-':
return -BigInt(v.slice(1));
case '$': {
const [ix, iface] = v.slice(1).split('.');
return convertSlotToVal(slots[Number(ix)], iface);
}
case '#':
switch (v) {
case '#undefined':
return undefined;
case '#Infinity':
return Infinity;
case '#NaN':
return Infinity;
default:
throw RangeError(`Unexpected constant ${v}`);
}
case '%':
// TODO: @@asyncIterator
return Symbol.for(v.slice(1));
default:
throw RangeError(`Unexpected sigil ${sigil}`);
}
case 'object':
if (v === null) return v;
if (isArray(v)) {
return freeze(v.map(recur));
}
return freeze(objMap(v, recur));
default:
throw RangeError(`Unexpected value type ${typeof v}`);
}
};
const encoding = JSON.parse(body.replace(/^#/, ''));
return recur(encoding);
};

const toCapData = () => {
throw Error('not implemented');
};

return harden({
fromCapData,
unserialize: fromCapData,
toCapData,
serialize: toCapData,
});
};

const PASS_STYLE = Symbol.for('passStyle');
export const Far = (iface, methods) => {
const proto = freeze(
create(objectPrototype, {
[PASS_STYLE]: { value: 'remotable' },
[Symbol.toStringTag]: { value: iface },
}),
);
setPrototypeOf(methods, proto);
freeze(methods);
return methods;
};

// #region marshal-table
const makeSlot1 = (val, serial) => {
const prefix = Promise.resolve(val) === val ? 'promise' : 'object';
return `${prefix}${serial}`;
};

const makeTranslationTable = (makeSlot, makeVal) => {
const valToSlot = new Map();
const slotToVal = new Map();

const convertValToSlot = val => {
if (valToSlot.has(val)) return valToSlot.get(val);
const slot = makeSlot(val, valToSlot.size);
valToSlot.set(val, slot);
slotToVal.set(slot, val);
return slot;
};

const convertSlotToVal = (slot, iface) => {
if (slotToVal.has(slot)) return slotToVal.get(slot);
if (makeVal) {
const val = makeVal(slot, iface);
valToSlot.set(val, slot);
slotToVal.set(slot, val);
return val;
}
throw Error(`no such ${iface}: ${slot}`);
};

return harden({ convertValToSlot, convertSlotToVal });
};
// #endregion marshal-table

export { makeMarshal, makeTranslationTable };
Loading

0 comments on commit b76aace

Please sign in to comment.