@agoric/deploy-script-support 0.10.4-other-dev-8f8782b.0 → 0.10.4-other-dev-fbe72e7.0.fbe72e7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,168 @@
1
+ /**
2
+ * @file support for Contract Deployment Process
3
+ *
4
+ * 1. `agoric run XYZ.build.js` produces:
5
+ * - `b1-123.json` bundle x 2: contract, aux
6
+ * - `XYZ-permit.json`, `XYZ.js` script
7
+ * 2. Install bundles
8
+ * - permissionless with per-byte fee in IST
9
+ * 3. Submit CoreEval proposal to BLD stakers
10
+ * - `XYZ-permit.json`, `XYZ.js`
11
+ *
12
+ * @see {runBuilder}
13
+ * @see {installBundles}
14
+ * @see {submitCoreEval}
15
+ */
16
+ import { toCLIOptions } from '@agoric/internal/src/cli-utils.js';
17
+
18
+ /**
19
+ * @import {CmdRunner} from '@agoric/pola-io';
20
+ * @import {FileRd} from '@agoric/pola-io';
21
+ */
22
+
23
+ /**
24
+ *
25
+ * TODO: builder should be a FileRd
26
+ * TODO: parameterize dest dir
27
+ *
28
+ * @example
29
+ * to use npx to find `agoric` in node_modules/.bin:
30
+ * const execP = promisify(childProcess.execFile)
31
+ * const agoric = makeCmdRunner('npx', { execFile: execP }).subCommand('agoric');
32
+ *
33
+ * XXX use a different name from execFile since the meaning is different
34
+ *
35
+ * @param {CmdRunner} agoric
36
+ * @param {FileRd} builder
37
+ * @param {string[]} [builderOpts]
38
+ * @param {{cwd?: FileRd}} [io]
39
+ *
40
+ * @returns {Promise<Plan>}
41
+ *
42
+ * @typedef {{
43
+ * name: string,
44
+ * script: string,
45
+ * permit: string,
46
+ * bundles: { entrypoint:string, bundleID:string, fileName:string}[];
47
+ * }} Plan
48
+ */
49
+ export const runBuilder = async (
50
+ agoric,
51
+ builder,
52
+ builderOpts = [],
53
+ { cwd = builder.join('../../') } = {},
54
+ ) => {
55
+ const cmd = agoric.withFlags(...builderOpts);
56
+ const { stdout } = await cmd.exec(['run', String(builder)]);
57
+ const match = stdout?.match(/ (?<name>[-\w]+)-permit.json/);
58
+ if (!(match && match.groups)) {
59
+ throw Error('no permit found');
60
+ }
61
+ /** @type {Plan} */
62
+ const plan = await cwd.join(`${match.groups.name}-plan.json`).readJSON();
63
+ return plan;
64
+ };
65
+
66
+ export const txFlags = ({
67
+ node,
68
+ from,
69
+ chainId,
70
+ keyringBackend = 'test',
71
+ broadcastMode = 'block',
72
+ }) => ({
73
+ node,
74
+ from,
75
+ 'chain-id': chainId,
76
+ 'keyring-backend': keyringBackend,
77
+ 'broadcast-mode': broadcastMode,
78
+ // TODO: parameterize these?
79
+ gas: 'auto',
80
+ 'gas-adjustment': '1.4',
81
+ });
82
+
83
+ /**
84
+ * @param {CmdRunner} agd
85
+ * @param {number} n
86
+ */
87
+ export const waitForBlock = async (agd, n = 1) => {
88
+ const getHeight = async () => {
89
+ const { stdout } = await agd.exec(['status']);
90
+ const status = JSON.parse(stdout);
91
+ const { latest_block_height: height } = status.sync_info || status.SyncInfo;
92
+ return height;
93
+ };
94
+ const initialHeight = await getHeight();
95
+ const SEC = 1000;
96
+ let currentHeight;
97
+ do {
98
+ await new Promise(resolve => setTimeout(resolve, 1 * SEC)); // XXX ambient
99
+ currentHeight = await getHeight();
100
+ } while (currentHeight - initialHeight < n);
101
+ console.log('block height:', initialHeight, currentHeight);
102
+ };
103
+
104
+ /**
105
+ * @param {CmdRunner} agd - agd with --from etc.
106
+ * @param {string[]} txArgs
107
+ */
108
+ export const runTx = async (agd, txArgs) => {
109
+ const { stdout } = await agd.withFlags('-o', 'json').exec(['tx', ...txArgs]);
110
+ const result = JSON.parse(stdout);
111
+ if (result.code !== 0) {
112
+ throw Object.assign(Error(result.raw_log), result);
113
+ }
114
+ return result;
115
+ };
116
+
117
+ /**
118
+ * @param {CmdRunner} agd
119
+ * @param {FileRd} bundle
120
+ */
121
+ export const installBundle = async (agd, bundle) =>
122
+ runTx(agd, ['swingset', 'install-bundle', `@${bundle}`]);
123
+
124
+ export const txAbbr = tx => {
125
+ const { txhash, code, height, gas_used: g } = tx;
126
+
127
+ return { txhash, code, height, gas_used: g };
128
+ };
129
+
130
+ /**
131
+ * @param {CmdRunner} agd
132
+ * @param {Plan['bundles']} bundles
133
+ * @param {FileRd} files
134
+ */
135
+ export const installBundles = (agd, bundles, files) => {
136
+ const ps = bundles.map(b =>
137
+ installBundle(agd, files.join(files.relative(b.fileName))),
138
+ );
139
+ return Promise.all(ps);
140
+ };
141
+
142
+ /**
143
+ * @param {CmdRunner} agd
144
+ * @param {Pick<Plan, 'permit' | 'script'>[]} evals
145
+ * @param {object} [opts]
146
+ * @param {string} [opts.title]
147
+ * @param {string} [opts.description]
148
+ * @param {object} [opts.depositOpts]
149
+ * @param {string} [opts.depositOpts.denom]
150
+ * @param {number} [opts.depositOpts.unit]
151
+ * @param {number} [opts.depositOpts.qty]
152
+ * @param {string} [opts.deposit]
153
+ */
154
+ export const submitCoreEval = async (
155
+ agd,
156
+ evals,
157
+ {
158
+ title = evals[0].script,
159
+ description = title,
160
+ depositOpts: { denom = 'ubld', unit = 1_000_000, qty = 10 } = {},
161
+ deposit = `${qty * unit}${denom}`,
162
+ } = {},
163
+ ) =>
164
+ runTx(agd, [
165
+ ...'gov submit-proposal swingset-core-eval'.split(' '),
166
+ ...evals.map(e => [e.permit, e.script]).flat(),
167
+ ...toCLIOptions({ title, description, deposit }),
168
+ ]);
package/src/saveIssuer.js CHANGED
@@ -1,12 +1,15 @@
1
1
  // @ts-check
2
2
  import { E } from '@endo/far';
3
3
 
4
- /** @typedef {import('@agoric/deploy-script-support/src/externalTypes').Petname} Petname */
4
+ /**
5
+ * @import {Petname} from '@agoric/deploy-script-support/src/externalTypes.js';
6
+ * @import {Amount, Brand, Issuer, Payment} from '@agoric/ertp';
7
+ */
5
8
 
6
9
  /**
7
10
  * @param {ERef<any>} walletAdmin - an internal type of the
8
11
  * wallet, not defined here
9
- * @param {ERef<import('./startInstance').IssuerManager>} issuerManager
12
+ * @param {ERef<import('./startInstance.js').IssuerManager>} issuerManager
10
13
  */
11
14
  export const makeSaveIssuer = (walletAdmin, issuerManager) => {
12
15
  /**
@@ -1,8 +1,12 @@
1
1
  // @ts-check
2
- import { assert } from '@agoric/assert';
2
+ import { assert } from '@endo/errors';
3
3
  import { E, passStyleOf } from '@endo/far';
4
4
 
5
- /** @typedef {import('@agoric/deploy-script-support/src/externalTypes').Petname} Petname */
5
+ /**
6
+ * @import {Petname} from '@agoric/deploy-script-support/src/externalTypes.js';
7
+ * @import {Amount, Brand, Issuer, Payment, Purse} from '@agoric/ertp';
8
+ * @import {IssuerKeywordRecord, Keyword} from '@agoric/zoe';
9
+ */
6
10
 
7
11
  /**
8
12
  * @template T
@@ -100,9 +104,8 @@ export const makeStartInstance = (
100
104
  `creatorInvitation must be defined to be deposited`,
101
105
  );
102
106
  console.log(`-- Adding Invitation for: ${instancePetname}`);
103
- const invitationAmount = await E(zoeInvitationPurse).deposit(
104
- creatorInvitation,
105
- );
107
+ const invitationAmount =
108
+ await E(zoeInvitationPurse).deposit(creatorInvitation);
106
109
  console.log(`- Created Contract Instance: ${instancePetname}`);
107
110
 
108
111
  const creatorInvitationDetails = invitationAmount.value[0];
@@ -0,0 +1,228 @@
1
+ // @ts-check
2
+ import fs from 'fs';
3
+ import { E } from '@endo/far';
4
+ import { deeplyFulfilled } from '@endo/marshal';
5
+
6
+ import { createBundles } from '@agoric/internal/src/node/createBundles.js';
7
+ import { defangAndTrim, mergePermits, stringify } from './code-gen.js';
8
+ import {
9
+ makeCoreProposalBehavior,
10
+ permits as defaultPermits,
11
+ } from './coreProposalBehavior.js';
12
+
13
+ /**
14
+ * @import {BundleSource, BundleSourceResult} from '@endo/bundle-source';
15
+ * @import {AgSoloHome, CanonicalHome, CommonHome, CoreEvalBuilder, CoreEvalDescriptor, ManifestBundleRef} from './externalTypes.js';
16
+ */
17
+
18
+ /**
19
+ * @typedef CoreEvalPlan
20
+ * @property {string} name
21
+ * @property {string} permit
22
+ * @property {string} script
23
+ * @property {{entrypoint: string, bundleID: string, fileName: string}[]} bundles
24
+ */
25
+
26
+ /**
27
+ * @callback WriteCoreEval write to disk the files needed for a CoreEval (js code to`${filePrefix}.js`, permits to `${filePrefix}-permit.json`, an overall
28
+ * summary to `${filePrefix}-plan.json), plus whatever bundles bundles the code loads)
29
+ * see CoreEval in {@link '/golang/cosmos/x/swingset/types/swingset.pb.go'}
30
+ * @param {string} filePrefix name on disk
31
+ * @param {CoreEvalBuilder} builder
32
+ * @returns {Promise<CoreEvalPlan>}
33
+ */
34
+
35
+ /**
36
+ *
37
+ * @param {Promise<CommonHome | AgSoloHome>} homeP
38
+ * @param {{
39
+ * bundleSource: BundleSource,
40
+ * pathResolve: (path: string) => string,
41
+ * }} endowments
42
+ * @param {{
43
+ * getBundlerMaker: () => Promise<import('./getBundlerMaker.js').BundleMaker>,
44
+ * getBundleSpec: (bundle: Promise<BundleSourceResult<'endoZipBase64'>>, getBundle: () => import('./getBundlerMaker.js').Bundler, opts?: any) => Promise<ManifestBundleRef>,
45
+ * log?: typeof console.log,
46
+ * writeFile?: typeof fs.promises.writeFile
47
+ * }} io
48
+ * @returns {WriteCoreEval}
49
+ */
50
+ export const makeWriteCoreEval = (
51
+ homeP,
52
+ endowments,
53
+ {
54
+ getBundlerMaker,
55
+ getBundleSpec,
56
+ log = console.log,
57
+ writeFile = fs.promises.writeFile,
58
+ },
59
+ ) => {
60
+ const { bundleSource, pathResolve } = endowments;
61
+
62
+ let bundlerCache;
63
+ /** @returns {import('./getBundlerMaker.js').Bundler} */
64
+ const getBundler = () => {
65
+ if (!bundlerCache) {
66
+ bundlerCache = E(getBundlerMaker()).makeBundler({
67
+ // @ts-expect-error lazily resolved for AgSoloHome
68
+ zoe: E.get(homeP).zoe,
69
+ });
70
+ }
71
+ return bundlerCache;
72
+ };
73
+
74
+ /**
75
+ *
76
+ * @param {CoreEvalDescriptor} coreEval
77
+ * @param {*} additionalPermits
78
+ */
79
+ const mergeEvalPermit = async (coreEval, additionalPermits) => {
80
+ const {
81
+ sourceSpec,
82
+ getManifestCall: [manifestGetterName, ...manifestGetterArgs],
83
+ } = coreEval;
84
+
85
+ const moduleNamespace = await import(pathResolve(sourceSpec));
86
+
87
+ // We only care about the manifest, not any restoreRef calls.
88
+ const { manifest } = await moduleNamespace[manifestGetterName](
89
+ harden({ restoreRef: x => `restoreRef:${x}` }),
90
+ ...manifestGetterArgs,
91
+ );
92
+
93
+ const mergedPermits = mergePermits(manifest);
94
+ return {
95
+ manifest,
96
+ permits: mergePermits({ mergedPermits, additionalPermits }),
97
+ };
98
+ };
99
+
100
+ let mutex = /** @type {Promise<ManifestBundleRef | undefined>} */ (
101
+ Promise.resolve()
102
+ );
103
+ /** @type {WriteCoreEval} */
104
+ const writeCoreEval = async (filePrefix, builder) => {
105
+ /**
106
+ *
107
+ * @param {string} entrypoint
108
+ * @param {string} [bundlePath]
109
+ * @returns {Promise<BundleSourceResult<'endoZipBase64'>>}
110
+ */
111
+ const getBundle = async (entrypoint, bundlePath) => {
112
+ if (!bundlePath) {
113
+ return bundleSource(pathResolve(entrypoint));
114
+ }
115
+ const bundleCache = pathResolve(bundlePath);
116
+ await createBundles([[pathResolve(entrypoint), bundleCache]]);
117
+ const ns = await import(bundleCache);
118
+ return ns.default;
119
+ };
120
+
121
+ const bundles = [];
122
+
123
+ /**
124
+ * Install an entrypoint.
125
+ *
126
+ * @param {string} entrypoint
127
+ * @param {string} [bundlePath]
128
+ * @param {unknown} [opts]
129
+ * @returns {Promise<ManifestBundleRef>}
130
+ */
131
+ const install = async (entrypoint, bundlePath, opts) => {
132
+ const bundle = getBundle(entrypoint, bundlePath);
133
+
134
+ // Serialise the installations.
135
+ mutex = E.when(mutex, async () => {
136
+ // console.log('installing', { filePrefix, entrypoint, bundlePath });
137
+ const spec = await getBundleSpec(bundle, getBundler, opts);
138
+ bundles.push({
139
+ entrypoint,
140
+ ...spec,
141
+ });
142
+ return spec;
143
+ });
144
+ // @ts-expect-error xxx mutex type narrowing
145
+ return mutex;
146
+ };
147
+
148
+ // Await a reference then publish to the board.
149
+ const cmds = [];
150
+ /** @param {Promise<ManifestBundleRef>} refP */
151
+ const publishRef = async refP => {
152
+ const { fileName, ...ref } = await refP;
153
+ if (fileName) {
154
+ cmds.push(`agd tx swingset install-bundle @${fileName}`);
155
+ }
156
+
157
+ return harden(ref);
158
+ };
159
+
160
+ // Create the eval structure.
161
+ const evalDescriptor = await deeplyFulfilled(
162
+ harden(builder({ publishRef, install })),
163
+ );
164
+ const { sourceSpec, getManifestCall } = evalDescriptor;
165
+ // console.log('created', { filePrefix, sourceSpec, getManifestCall });
166
+
167
+ // Extract the top-level permit.
168
+ const { permits: evalPermits, manifest: customManifest } =
169
+ await mergeEvalPermit(evalDescriptor, defaultPermits);
170
+
171
+ // Get an install
172
+ const manifestBundleRef = await publishRef(install(sourceSpec));
173
+
174
+ // console.log('writing', { filePrefix, manifestBundleRef, sourceSpec });
175
+ const code = `\
176
+ // This is generated by writeCoreEval; please edit!
177
+ /* eslint-disable */
178
+
179
+ const manifestBundleRef = ${stringify(manifestBundleRef)};
180
+ const getManifestCall = harden(${stringify(getManifestCall, true)});
181
+ const customManifest = ${stringify(customManifest, true)};
182
+
183
+ // Make a behavior function and "export" it by way of script completion value.
184
+ // It is constructed by an anonymous invocation to ensure the absence of a global binding
185
+ // for makeCoreProposalBehavior, which may not be necessary but preserves behavior pre-dating
186
+ // https://github.com/Agoric/agoric-sdk/pull/8712 .
187
+ const behavior = (${makeCoreProposalBehavior})({ manifestBundleRef, getManifestCall, customManifest, E });
188
+ behavior;
189
+ `;
190
+
191
+ const trimmed = defangAndTrim(code);
192
+
193
+ const permitFile = `${filePrefix}-permit.json`;
194
+ log(`creating ${permitFile}`);
195
+ await writeFile(permitFile, JSON.stringify(evalPermits, null, 2));
196
+
197
+ const codeFile = `${filePrefix}.js`;
198
+ log(`creating ${codeFile}`);
199
+ await writeFile(codeFile, trimmed);
200
+
201
+ /** @type {CoreEvalPlan} */
202
+ const plan = {
203
+ name: filePrefix,
204
+ script: codeFile,
205
+ permit: permitFile,
206
+ bundles,
207
+ };
208
+
209
+ await writeFile(
210
+ `${filePrefix}-plan.json`,
211
+ `${JSON.stringify(plan, null, 2)}\n`,
212
+ );
213
+
214
+ log(`\
215
+ You can now run a governance submission command like:
216
+ agd tx gov submit-proposal swingset-core-eval ${permitFile} ${codeFile} \\
217
+ --title="Enable <something>" --description="Evaluate ${codeFile}" --deposit=1000000ubld \\
218
+ --gas=auto --gas-adjustment=1.2
219
+ Remember to install bundles before submitting the proposal:
220
+ ${cmds.join('\n ')}
221
+ `);
222
+ return plan;
223
+ };
224
+
225
+ return writeCoreEval;
226
+ };
227
+ /** @deprecated use makeWriteCoreEval */
228
+ export const makeWriteCoreProposal = makeWriteCoreEval;