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: separate genesis from RUN protocol installation #5123

Merged
merged 42 commits into from
Apr 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
72d1f8d
feat(vats): pass options thru runBehaviors
dckc Apr 13, 2022
4306ac9
feat(vats): add evaluateInstallation to bootstrap powers
dckc Apr 13, 2022
c2b2d53
test(vats): evaluateInstallation is available to core eval
dckc Apr 13, 2022
5d010ad
build(vats): devDependencies += bundle-source
dckc Apr 13, 2022
b6dd4fc
fix(core-boot): complain louder if bootstrap fails
michaelfig Apr 14, 2022
50a1156
fix(core-boot): only endow evaluateInstallation` if permitted
michaelfig Apr 14, 2022
6129b38
feat(deploy-script-support): first cut at `writeCoreProposal`
michaelfig Apr 14, 2022
531d367
fix: two isolated cases where a missing argument did not default
michaelfig Apr 14, 2022
88a0cf7
feat(deploy-script-suppport): e2e `writeCoreProposal`
michaelfig Apr 14, 2022
393cd94
refactor(coreProposalBehavior): independent function source maker
michaelfig Apr 14, 2022
7f5d781
chore: move boot contracts out of run-protocol
dckc Apr 15, 2022
5232201
build(vats): bundle-source for core contract bundles with caching
dckc Apr 15, 2022
bb7efda
chore(vats): bundles -> installations in bootstrap
dckc Apr 15, 2022
90a30bc
chore(run-protocol): never mind importedBundles.js
dckc Apr 15, 2022
3dcf031
chore(pegasus): import bundle directly
dckc Apr 15, 2022
f91c0ee
chore(vats): separate RUN protocol contracts from bootstrap
dckc Apr 15, 2022
291f730
chore(run-protocol): move manifest from vats/
dckc Apr 15, 2022
732292a
feat(build-bundles): create source bundles with helper
michaelfig Apr 15, 2022
5707327
build(governance): build bundles with bundle-source CLI tool
dckc Apr 15, 2022
844beb0
build(run-protocol): use bundle-source CLI tool for contracts
dckc Apr 15, 2022
cbfa83c
chore(run-protocol): borrow bundleTool.js from bundle-source
dckc Apr 15, 2022
8587117
style(bundleTool): endo vs agoric-sdk lint config??
dckc Apr 15, 2022
8910516
chore: adapt bundleTool for use in tests
dckc Apr 15, 2022
18e8c88
feat(deploy-script-support): shell out to `bundle-source`
michaelfig Apr 15, 2022
40881a7
build: use `deploy-script-support` for `build-bundles.js`
michaelfig Apr 15, 2022
a3d8231
chore(run-protocol): bundles -> installations for runStake
dckc Apr 15, 2022
9624665
feat(bundleTool): memoize load()
dckc Apr 15, 2022
2d9adbb
chore(run-protocol): bundles -> installation for reserve
dckc Apr 15, 2022
39024af
test(psm): use bundleTool
dckc Apr 15, 2022
d77cea2
fix(bundleTool): harden loaded bundles
dckc Apr 15, 2022
2241fe4
test(vaultFactory): bundles -> installations
dckc Apr 15, 2022
131b2c9
chore(governance): .js suffix fix
dckc Apr 16, 2022
0170c3e
chore(run-protocol): .js suffix fixes
dckc Apr 16, 2022
fce41fd
test(run-protocol): update bundle handling
dckc Apr 16, 2022
6906266
test(amm): bundle handling
dckc Apr 16, 2022
c92c9af
test(runStake): ExecutionContext type
dckc Apr 16, 2022
afa616e
test(amm): bundle handling (cont.)
dckc Apr 16, 2022
5873d41
fix(vats): reserve `centralSupply` and `mintHolder` installs
michaelfig Apr 16, 2022
009b49f
feat(deploy-script-support): more `createBundles` work
michaelfig Apr 16, 2022
8271352
build(vats): include bundles in `npm pack`
michaelfig Apr 16, 2022
1c06bbd
fix(run-protocol): shuffle around to fix types
michaelfig Apr 16, 2022
125e9ba
fix(vats): use `decentral-*-config.json` to bundle Zoe contracts
michaelfig Apr 16, 2022
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
2 changes: 1 addition & 1 deletion packages/cosmic-swingset/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ scenario2-run-client: t1-provision-one-with-powers t1-start-ag-solo

# Provision the ag-solo from an provisionpass-holding address (idempotent).
AGORIC_POWERS = agoric.ALL_THE_POWERS
SOLO_COINS = 13000000ubld,50000000urun
SOLO_COINS = 13000000ubld,1234000000urun
COSMOS_RPC_HOST = localhost
COSMOS_RPC_PORT = 26657
wait-for-cosmos:
Expand Down
1 change: 1 addition & 0 deletions packages/deploy-script-support/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"@endo/base64": "^0.2.25",
"@endo/bundle-source": "^2.2.0",
"@endo/far": "^0.2.3",
"@endo/marshal": "^0.6.7",
"@endo/promise-kit": "^0.2.41",
"@endo/zip": "^0.2.25"
},
Expand Down
86 changes: 86 additions & 0 deletions packages/deploy-script-support/src/coreProposalBehavior.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/**
* Create a behavior for a core-eval proposal.
*
* We rely on directly stringifying this function to leverage our JS toolchain
* for catching bugs. Thus, this maker must not reference any other modules or
* definitions.
*
* @param {object} opts
* @param {string} opts.manifestInstallRef
* @param {[string, ...unknown[]]} opts.getManifestCall
* @param {typeof import('@endo/far').E} opts.E
* @param {(...args: unknown[]) => void} [opts.log]
* @returns {(vatPowers: unknown) => Promise<unknown>}
*/
export const makeCoreProposalBehavior = ({
manifestInstallRef,
getManifestCall,
E,
log = console.info,
}) => {
const { entries, fromEntries } = Object;

// deeplyFulfilled is a bit overkill for what we need.
const shallowlyFulfilled = async obj => {
if (!obj) {
return obj;
}
const ents = await Promise.all(
entries(obj).map(async ([key, valueP]) => {
const value = await valueP;
return [key, value];
}),
);
return fromEntries(ents);
};

const behavior = async allPowers => {
const {
consume: { board },
evaluateInstallation,
installation: { produce: produceInstallations },
modules: {
utils: { runModuleBehaviors },
},
} = allPowers;
const [exportedGetManifest, ...manifestArgs] = getManifestCall;

// Get the on-chain installation containing the manifest and behaviors.
const manifestInstallation = await E(board).getValue(manifestInstallRef);
const behaviors = await evaluateInstallation(manifestInstallation);

const restoreRef = x => E(board).getValue(x);
const {
manifest,
options: rawOptions,
installations: rawInstallations,
} = await behaviors[exportedGetManifest](
harden({ restoreRef }),
...manifestArgs,
);

// Await references in the options or installations.
const [options, installations] = await Promise.all(
[rawOptions, rawInstallations].map(shallowlyFulfilled),
);

// Publish the installations for behavior dependencies.
entries(installations || {}).forEach(([key, value]) => {
produceInstallations[key].resolve(value);
});

// Evaluate the manifest for our behaviors.
return runModuleBehaviors({
allPowers,
behaviors,
manifest,
makeConfig: (name, _permit) => {
log('coreProposal:', name);
return { options };
},
});
};

// Make the behavior the completion value.
return behavior;
};
79 changes: 79 additions & 0 deletions packages/deploy-script-support/src/createBundles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import path from 'path';
import { spawnSync } from 'child_process';
import { createRequire } from 'module';

const BUNDLE_SOURCE_PROGRAM = 'bundle-source';
const require = createRequire(import.meta.url);

export const createBundlesFromAbsolute = async sourceBundles => {
const prog = require.resolve(`.bin/${BUNDLE_SOURCE_PROGRAM}`);

const cacheToArgs = new Map();
for (const [srcPath, bundlePath] of sourceBundles) {
const cache = path.dirname(bundlePath);
const base = path.basename(bundlePath);

const match = base.match(/^bundle-(.*)\.js$/);
assert(match, `${bundlePath} is not 'DIR/bundle-NAME.js'`);
const bundle = match[1];

const args = cacheToArgs.get(cache) || ['--to', cache];
args.push(srcPath, bundle);
cacheToArgs.set(cache, args);
}

for (const args of cacheToArgs.values()) {
console.log(BUNDLE_SOURCE_PROGRAM, ...args);
const { status } = spawnSync(prog, args, { stdio: 'inherit' });
assert.equal(
status,
0,
`${BUNDLE_SOURCE_PROGRAM} failed with status ${status}`,
);
}
};

export const createBundles = async (sourceBundles, dirname = '.') => {
const absBundleSources = sourceBundles.map(([srcPath, bundlePath]) => [
require.resolve(srcPath, { paths: [dirname] }),
path.resolve(dirname, bundlePath),
]);
return createBundlesFromAbsolute(absBundleSources);
};

export const extractProposalBundles = async (
dirProposalBuilder,
dirname = '.',
) => {
const toBundle = new Map();

await Promise.all(
dirProposalBuilder.map(async ([dir, proposalBuilder]) => {
const home = path.resolve(dirname, dir);
const publishRef = x => x;
const install = async (src, bundleName) => {
if (bundleName) {
const bundlePath = path.resolve(home, bundleName);
const srcPath = require.resolve(src, { paths: [home] });
if (toBundle.has(bundlePath)) {
const oldSrc = toBundle.get(bundlePath);
assert.equal(
oldSrc,
srcPath,
`${bundlePath} already installed as ${oldSrc}, also given ${srcPath}`,
);
}
toBundle.set(bundlePath, srcPath);
}
};
return proposalBuilder({ publishRef, install });
}),
);

return createBundlesFromAbsolute(
[...toBundle.entries()].map(([bundlePath, srcPath]) => [
srcPath,
bundlePath,
]),
);
};
9 changes: 9 additions & 0 deletions packages/deploy-script-support/src/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { makeSaveIssuer } from './saveIssuer.js';
import { makeGetBundlerMaker } from './getBundlerMaker.js';
import { assertOfferResult } from './assertOfferResult.js';
import { installInPieces } from './installInPieces.js';
import { makeWriteCoreProposal } from './writeCoreProposal.js';

export * from './createBundles.js';

// These are also hard-coded in lib-wallet.js.
// TODO: Add methods to the wallet to access these without hard-coding
Expand Down Expand Up @@ -56,6 +59,11 @@ export const makeHelpers = async (homePromise, endowments) => {
{ bundleSource, lookup },
);

const writeCoreProposal = makeWriteCoreProposal(homePromise, endowments, {
installInPieces,
getBundlerMaker,
});

return {
install,
startInstance,
Expand All @@ -66,5 +74,6 @@ export const makeHelpers = async (homePromise, endowments) => {
saveIssuer,
depositInvitation,
assertOfferResult,
writeCoreProposal,
};
};
124 changes: 124 additions & 0 deletions packages/deploy-script-support/src/writeCoreProposal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import fs from 'fs';
import { E } from '@endo/far';
import { deeplyFulfilled, makeMarshal } from '@endo/marshal';
import { decodeToJustin } from '@endo/marshal/src/marshal-justin.js';

import { makeCoreProposalBehavior } from './coreProposalBehavior.js';
import { createBundles } from './createBundles.js';

const { serialize } = makeMarshal();
const stringify = (x, pretty = undefined) =>
decodeToJustin(JSON.parse(serialize(harden(x)).body), pretty);

export const makeWriteCoreProposal = (
homeP,
endowments,
{ getBundlerMaker, installInPieces },
) => {
const { board, zoe } = E.get(homeP);
const { bundleSource, pathResolve } = endowments;

let bundlerCache;
const getBundler = () => {
if (!bundlerCache) {
bundlerCache = E(getBundlerMaker()).makeBundler({
zoe,
});
}
return bundlerCache;
};

const mergeProposalPermit = async (proposal, additionalManifest) => {
const {
sourceSpec,
getManifestCall: [exportedGetManifest, ...manifestArgs],
} = proposal;

const manifestNs = await import(pathResolve(sourceSpec));

// We only care about the manifest, not any restoreRef calls.
const { manifest } = await manifestNs[exportedGetManifest](
{ restoreRef: x => `restoreRef:${x}` },
...manifestArgs,
);

// FIXME: later actually merge the manifest with additionalManifest for
// minimalistic permit.
console.log('TODO: would merge', { manifest, additionalManifest });
return true;
};

const writeCoreProposal = async (filePrefix, proposalBuilder) => {
// Install an entrypoint.
const install = async (entrypoint, bundlePath) => {
const bundler = getBundler();
let bundle;
if (bundlePath) {
const bundleCache = pathResolve(bundlePath);
await createBundles([[pathResolve(entrypoint), bundleCache]]);
const ns = await import(bundleCache);
bundle = ns.default;
} else {
bundle = await bundleSource(pathResolve(entrypoint));
}
return installInPieces(bundle, bundler);
};

// Await a reference then publish to the board.
const publishRef = async refP => {
const ref = await refP;
return E(board).getId(ref);
};

// Create the proposal structure.
const proposal = await deeplyFulfilled(
harden(proposalBuilder({ publishRef, install })),
);
const { sourceSpec, getManifestCall } = proposal;

// Extract the top-level permit.
const t = 'writeCoreProposal';
const proposalPermit = await mergeProposalPermit(proposal, {
$writeCoreProposal: {
consume: { board: t },
evaluateInstallation: t,
installation: { produce: t },
modules: { utils: { runModuleBehaviors: t } },
},
});

// Get an install
const manifestInstallRef = await publishRef(install(sourceSpec));

const code = `\
// This is generated by writeCoreProposal; please edit!
/* eslint-disable */

const manifestInstallRef = ${stringify(manifestInstallRef)};
const getManifestCall = harden(${stringify(getManifestCall, true)});

// Make the behavior the completion value.
(${makeCoreProposalBehavior})({ manifestInstallRef, getManifestCall, E });
`;

// end-of-line whitespace disrupts YAML formatting
const trimmed = code.replace(/[\r\t ]+$/gm, '');

const proposalPermitJsonFile = `${filePrefix}-permit.json`;
console.log(`creating ${proposalPermitJsonFile}`);
fs.writeFileSync(proposalPermitJsonFile, JSON.stringify(proposalPermit));

const proposalJsFile = `${filePrefix}.js`;
console.log(`creating ${proposalJsFile}`);
fs.writeFileSync(proposalJsFile, trimmed);

console.log(`\
You can now run a governance submission command like:
agd tx gov submit-proposal swingset-core-eval ${proposalPermitJsonFile} ${proposalJsFile} \\
--title="Enable <something>" --description="Evaluate ${proposalJsFile}" --deposit=1000000ubld \\
--gas=auto --gas-adjustment=1.2
`);
};

return writeCoreProposal;
};
Loading