agoric 0.21.2-other-dev-1f26562.0 → 0.21.2-other-dev-3eb1a1d.0

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,102 @@
1
+ // @ts-check
2
+ /* eslint-env node */
3
+
4
+ import assert from 'node:assert/strict';
5
+ import fs from 'node:fs';
6
+ import { join } from 'node:path';
7
+
8
+ import { ZipReader } from '@endo/zip';
9
+
10
+ /** @import {Bundle} from '@agoric/swingset-vat'; */
11
+ /** @import {CoreEvalPlan} from '@agoric/deploy-script-support/src/writeCoreEvalParts.js' */
12
+
13
+ // exported for testing
14
+ export const PACKAGE_NAME_RE = /^(?:@[^/]+\/)?[^/]+/;
15
+
16
+ /**
17
+ * @typedef {{ name: string, label: string, location: string, modules: Record<string, {compartment: string, module: string}>}} Compartment
18
+ */
19
+
20
+ /**
21
+ * @typedef CompartmentMap
22
+ * @property {string[]} tags
23
+ * @property {{compartment: string, module: string}} entry
24
+ * @property {Record<string, Compartment>} compartments
25
+ */
26
+
27
+ /** @param {Bundle} bundleObj*/
28
+ export const extractBundleInfo = async bundleObj => {
29
+ if (bundleObj.moduleFormat !== 'endoZipBase64') {
30
+ throw Error('only endoZipBase64 is supported');
31
+ }
32
+
33
+ const contents = Buffer.from(bundleObj.endoZipBase64, 'base64');
34
+
35
+ const zipReader = new ZipReader(contents);
36
+ const { files } = zipReader;
37
+
38
+ const cmapEntry = files.get('compartment-map.json');
39
+ /** @type {CompartmentMap} */
40
+ const compartmentMap = JSON.parse(Buffer.from(cmapEntry.content).toString());
41
+
42
+ // XXX mapIter better but requires SES
43
+ const fileSizes = Object.fromEntries(
44
+ Array.from(files.values()).map(f => [
45
+ f.name,
46
+ // bundle contents are not compressed
47
+ f.content.length,
48
+ ]),
49
+ );
50
+
51
+ return { compartmentMap, fileSizes };
52
+ };
53
+
54
+ // UNTIL https://github.com/endojs/endo/issues/1656
55
+ /** @param {string} bundleFilename */
56
+ export const statBundle = async bundleFilename => {
57
+ const bundle = fs.readFileSync(bundleFilename, 'utf8');
58
+ /** @type {Bundle} */
59
+ const bundleObj = JSON.parse(bundle);
60
+ console.log('\nBUNDLE', bundleObj.moduleFormat, bundleFilename);
61
+
62
+ const info = await extractBundleInfo(bundleObj);
63
+ assert(info, 'no bundle info');
64
+
65
+ /** @type {Record<string, number>} */
66
+ const byPackage = {};
67
+ let totalSize = 0;
68
+ for (const [filename, size] of Object.entries(info.fileSizes)) {
69
+ totalSize += size;
70
+ if (filename === 'compartment-map.json') {
71
+ continue;
72
+ }
73
+ const packageName = filename.match(PACKAGE_NAME_RE)?.[0];
74
+ assert(packageName, `invalid filename ${filename}`);
75
+ byPackage[packageName] ||= 0;
76
+ byPackage[packageName] += size;
77
+ }
78
+
79
+ console.log('Sum of file sizes in each package:');
80
+ console.table(byPackage);
81
+
82
+ console.log('total size:', totalSize);
83
+ console.log('\nTo explore the contents:\n');
84
+ console.log(
85
+ ` DIR=$(mktemp -d); cat ${bundleFilename} | jq -r .endoZipBase64 | base64 -d | tar xC $DIR; open $DIR`,
86
+ );
87
+ };
88
+
89
+ /** @param {string} path */
90
+ export const statPlans = async path => {
91
+ const files = await fs.promises.readdir(path);
92
+ const planfiles = files.filter(f => f.endsWith('plan.json'));
93
+
94
+ for (const planfile of planfiles) {
95
+ /** @type {CoreEvalPlan} */
96
+ const plan = JSON.parse(fs.readFileSync(join(path, planfile), 'utf8'));
97
+ console.log('\n**\nPLAN', plan.name);
98
+ for (const bundle of plan.bundles) {
99
+ await statBundle(bundle.fileName);
100
+ }
101
+ }
102
+ };
package/src/lib/chain.js CHANGED
@@ -1,8 +1,12 @@
1
1
  // @ts-check
2
- /* global process */
2
+ /* eslint-env node */
3
3
  import { normalizeBech32 } from '@cosmjs/encoding';
4
4
  import { execFileSync as execFileSyncAmbient } from 'child_process';
5
5
 
6
+ /**
7
+ * @import {MinimalNetworkConfig} from '@agoric/client-utils';
8
+ */
9
+
6
10
  const agdBinary = 'agd';
7
11
 
8
12
  /**
@@ -34,10 +38,42 @@ export const normalizeAddressWithOptions = (
34
38
  };
35
39
  harden(normalizeAddressWithOptions);
36
40
 
41
+ /** @typedef {number | 'auto' | ['auto', adjustment?: number | undefined]} GasLimit */
42
+
43
+ /**
44
+ * @param {GasLimit} limit
45
+ * @returns {string[]}
46
+ */
47
+ const makeGasOpts = limit => {
48
+ if (Number.isFinite(limit) || limit === 'auto') {
49
+ return [`--gas=${limit}`];
50
+ }
51
+ if (Array.isArray(limit) && limit.length >= 1 && limit[0] === 'auto') {
52
+ const gasOpts = ['--gas=auto'];
53
+ if (limit.length > 1) {
54
+ const [adjustment, ...rest] = limit.slice(1);
55
+ const adjustmentIsValid =
56
+ adjustment === undefined ||
57
+ (Number.isFinite(adjustment) && Number(adjustment) > 0);
58
+ if (rest.length !== 0 || !adjustmentIsValid) {
59
+ throw Error('invalid gas input');
60
+ }
61
+ if (adjustment !== undefined) {
62
+ gasOpts.push(`--gas-adjustment=${adjustment}`);
63
+ }
64
+ }
65
+ return gasOpts;
66
+ }
67
+
68
+ throw Error('invalid gas input');
69
+ };
70
+
37
71
  /**
38
72
  * @param {ReadonlyArray<string>} swingsetArgs
39
- * @param {import('./rpc').MinimalNetworkConfig & {
73
+ * @param {MinimalNetworkConfig & {
40
74
  * from: string,
75
+ * fees?: string,
76
+ * gas?: GasLimit,
41
77
  * dryRun?: boolean,
42
78
  * verbose?: boolean,
43
79
  * keyring?: {home?: string, backend: string}
@@ -48,6 +84,8 @@ harden(normalizeAddressWithOptions);
48
84
  export const execSwingsetTransaction = (swingsetArgs, opts) => {
49
85
  const {
50
86
  from,
87
+ fees,
88
+ gas = ['auto', 1.2],
51
89
  dryRun = false,
52
90
  verbose = true,
53
91
  keyring = undefined,
@@ -60,9 +98,12 @@ export const execSwingsetTransaction = (swingsetArgs, opts) => {
60
98
  const backendOpt = keyring?.backend
61
99
  ? [`--keyring-backend=${keyring.backend}`]
62
100
  : [];
101
+ const feeOpt = fees ? ['--fees', fees] : [];
63
102
  const cmd = [`--node=${rpcAddrs[0]}`, `--chain-id=${chainName}`].concat(
64
103
  homeOpt,
65
104
  backendOpt,
105
+ feeOpt,
106
+ makeGasOpts(gas),
66
107
  [`--from=${from}`, 'tx', 'swingset'],
67
108
  swingsetArgs,
68
109
  );
@@ -74,15 +115,27 @@ export const execSwingsetTransaction = (swingsetArgs, opts) => {
74
115
  stdout.write('\n');
75
116
  } else {
76
117
  const yesCmd = cmd.concat(['--yes']);
77
- if (verbose) console.log('Executing ', yesCmd);
78
- return execFileSync(agdBinary, yesCmd, { encoding: 'utf-8' });
118
+ if (verbose) console.log('Executing ', agdBinary, yesCmd);
119
+ const out = execFileSync(agdBinary, yesCmd, { encoding: 'utf-8' });
120
+
121
+ // agd puts this diagnostic on stdout rather than stderr :-/
122
+ // "Default sign-mode 'direct' not supported by Ledger, using sign-mode 'amino-json'.
123
+ if (out.startsWith('Default sign-mode')) {
124
+ const stripDiagnostic = out.replace(/^Default[^\n]+\n/, '');
125
+ return stripDiagnostic;
126
+ }
127
+ return out;
79
128
  }
80
129
  };
81
130
  harden(execSwingsetTransaction);
82
131
 
83
- // xxx rpc should be able to query this by HTTP without shelling out
132
+ /**
133
+ *
134
+ * @param {MinimalNetworkConfig} net
135
+ */
136
+ // TODO fetch by HTTP instead of shelling out https://github.com/Agoric/agoric-sdk/issues/9200
84
137
  export const fetchSwingsetParams = net => {
85
- const { chainName, rpcAddrs, execFileSync = execFileSyncAmbient } = net;
138
+ const { chainName, rpcAddrs } = net;
86
139
  const cmd = [
87
140
  `--node=${rpcAddrs[0]}`,
88
141
  `--chain-id=${chainName}`,
@@ -90,15 +143,15 @@ export const fetchSwingsetParams = net => {
90
143
  'swingset',
91
144
  'params',
92
145
  '--output',
93
- '--json',
146
+ 'json',
94
147
  ];
95
- const buffer = execFileSync(agdBinary, cmd);
148
+ const buffer = execFileSyncAmbient(agdBinary, cmd);
96
149
  return JSON.parse(buffer.toString());
97
150
  };
98
151
  harden(fetchSwingsetParams);
99
152
 
100
153
  /**
101
- * @param {import('./rpc').MinimalNetworkConfig & {
154
+ * @param {MinimalNetworkConfig & {
102
155
  * execFileSync: typeof import('child_process').execFileSync,
103
156
  * delay: (ms: number) => Promise<void>,
104
157
  * period?: number,
@@ -122,7 +175,6 @@ export const pollBlocks = opts => async lookup => {
122
175
  } = status;
123
176
  try {
124
177
  // see await null above
125
- // eslint-disable-next-line @jessie.js/no-nested-await, no-await-in-loop
126
178
  const result = await lookup({ time, height });
127
179
  return result;
128
180
  } catch (_err) {
@@ -132,7 +184,6 @@ export const pollBlocks = opts => async lookup => {
132
184
  height,
133
185
  'retrying...',
134
186
  );
135
- // eslint-disable-next-line @jessie.js/no-nested-await, no-await-in-loop
136
187
  await delay(period);
137
188
  }
138
189
  }
@@ -140,7 +191,7 @@ export const pollBlocks = opts => async lookup => {
140
191
 
141
192
  /**
142
193
  * @param {string} txhash
143
- * @param {import('./rpc').MinimalNetworkConfig & {
194
+ * @param {MinimalNetworkConfig & {
144
195
  * execFileSync: typeof import('child_process').execFileSync,
145
196
  * delay: (ms: number) => Promise<void>,
146
197
  * period?: number,
package/src/lib/format.js CHANGED
@@ -1,25 +1,21 @@
1
- // @ts-check
1
+ import { Fail, q } from '@endo/errors';
2
2
  import { makeBoardRemote } from '@agoric/vats/tools/board-utils.js';
3
- // eslint-disable-next-line no-unused-vars -- typeof below
4
- import { makeAgoricNames } from './rpc.js';
5
3
 
6
- // ambient types
7
- import '@agoric/ertp/src/types-ambient.js';
8
-
9
- /** @typedef {import('@agoric/vats/tools/board-utils.js').BoardRemote} BoardRemote */
4
+ /**
5
+ * @import {Amount, Brand} from '@agoric/ertp'
6
+ * @import {AgoricNamesRemotes, BoardRemote, VBankAssetDetail} from '@agoric/vats/tools/board-utils.js';
7
+ */
10
8
 
9
+ // TODO Move to packages/internal.
11
10
  /**
12
- * Like @endo/nat but coerces
11
+ * Parses the input and returns either a finite number or NaN.
13
12
  *
14
- * @param {string} str
15
- * @returns {bigint}
13
+ * @param {string} input
14
+ * @returns {number}
16
15
  */
17
- export const Natural = str => {
18
- const b = BigInt(str);
19
- if (b < 0) {
20
- throw RangeError(`${b} is negative`);
21
- }
22
- return b;
16
+ export const parseFiniteNumber = input => {
17
+ const result = /[0-9]/.test(input || '') ? Number(input) : NaN;
18
+ return Number.isFinite(result) ? result : NaN;
23
19
  };
24
20
 
25
21
  /**
@@ -30,21 +26,18 @@ export const Natural = str => {
30
26
  */
31
27
  export const bigintReplacer = (k, v) => (typeof v === 'bigint' ? `${v}` : v);
32
28
 
33
- /** @type {import('@agoric/vats/tools/board-utils.js').VBankAssetDetail} */
29
+ /** @type {Partial<VBankAssetDetail>} */
34
30
  // eslint-disable-next-line no-unused-vars
35
31
  const exampleAsset = {
36
- // @ts-expect-error cast
37
32
  brand: makeBoardRemote({ boardId: 'board0425', iface: 'Alleged: BLD brand' }),
38
33
  displayInfo: { assetKind: 'nat', decimalPlaces: 6 },
39
- // @ts-expect-error cast
40
34
  issuer: makeBoardRemote({ boardId: null, iface: undefined }),
41
- petname: 'Agoric staking token',
35
+ proposedName: 'Agoric staking token',
42
36
  };
43
- /** @typedef {import('@agoric/vats/tools/board-utils.js').VBankAssetDetail } AssetDescriptor */
44
37
 
45
38
  /**
46
- * @param {AssetDescriptor[]} assets
47
- * @returns {(a: Amount & { brand: BoardRemote }) => [string, number | any[]]}
39
+ * @param {VBankAssetDetail[]} assets
40
+ * @returns {(a: Amount & { brand: BoardRemote }) => [string | null, number | any[]]}
48
41
  */
49
42
  export const makeAmountFormatter = assets => amt => {
50
43
  const { brand, value } = amt;
@@ -60,7 +53,9 @@ export const makeAmountFormatter = assets => amt => {
60
53
  return [issuerName, Number(value) / 10 ** decimalPlaces];
61
54
  case 'set':
62
55
  assert(Array.isArray(value));
56
+ // @ts-expect-error narrowed
63
57
  if (value[0]?.handle?.iface?.includes('InvitationHandle')) {
58
+ // @ts-expect-error narrowed
64
59
  return [issuerName, value.map(v => v.description)];
65
60
  }
66
61
  return [issuerName, value];
@@ -75,8 +70,6 @@ export const asPercent = ratio => {
75
70
  return (100 * Number(numerator.value)) / Number(denominator.value);
76
71
  };
77
72
 
78
- const { Fail, quote: q } = assert;
79
-
80
73
  const isObject = x => typeof x === 'object' && x !== null;
81
74
 
82
75
  /**
@@ -94,8 +87,8 @@ export const asBoardRemote = x => {
94
87
  /**
95
88
  * Summarize the balances array as user-facing informative tuples
96
89
  *
97
- * @param {import('@agoric/smart-wallet/src/smartWallet').CurrentWalletRecord['purses']} purses
98
- * @param {AssetDescriptor[]} assets
90
+ * @param {import('@agoric/smart-wallet/src/smartWallet.js').CurrentWalletRecord['purses']} purses
91
+ * @param {VBankAssetDetail[]} assets
99
92
  */
100
93
  export const purseBalanceTuples = (purses, assets) => {
101
94
  const fmt = makeAmountFormatter(assets);
@@ -107,14 +100,15 @@ export const purseBalanceTuples = (purses, assets) => {
107
100
  */
108
101
  export const fmtRecordOfLines = record => {
109
102
  const { stringify } = JSON;
103
+ /** @type {Array<[string, string[]]>} */
110
104
  const groups = Object.entries(record).map(([key, items]) => [
111
105
  key,
112
106
  items.map(item => ` ${stringify(item)}`),
113
107
  ]);
114
- const lineEntries = groups.map(
115
- // @ts-expect-error ???
116
- ([key, lines]) => ` ${stringify(key)}: [\n${lines.join(',\n')}\n ]`,
117
- );
108
+ const lineEntries = groups.map(([key, lines]) => {
109
+ const linesStr = lines.length === 0 ? `[]` : `[\n${lines.join(',\n')}\n ]`;
110
+ return ` ${stringify(key)}: ${linesStr}`;
111
+ });
118
112
  return `{\n${lineEntries.join(',\n')}\n}`;
119
113
  };
120
114
 
@@ -122,7 +116,7 @@ export const fmtRecordOfLines = record => {
122
116
  * Summarize the offerStatuses of the state as user-facing informative tuples
123
117
  *
124
118
  * @param {import('@agoric/smart-wallet/src/utils.js').CoalescedWalletState} state
125
- * @param {Awaited<ReturnType<typeof makeAgoricNames>>} agoricNames
119
+ * @param {AgoricNamesRemotes} agoricNames
126
120
  */
127
121
  export const offerStatusTuples = (state, agoricNames) => {
128
122
  const { offerStatuses } = state;
@@ -177,9 +171,9 @@ export const offerStatusTuples = (state, agoricNames) => {
177
171
  };
178
172
 
179
173
  /**
180
- * @param {import('@agoric/smart-wallet/src/smartWallet').CurrentWalletRecord} current
174
+ * @param {import('@agoric/smart-wallet/src/smartWallet.js').CurrentWalletRecord} current
181
175
  * @param {ReturnType<import('@agoric/smart-wallet/src/utils.js').makeWalletStateCoalescer>['state']} coalesced
182
- * @param {Awaited<ReturnType<typeof makeAgoricNames>>} agoricNames
176
+ * @param {AgoricNamesRemotes} agoricNames
183
177
  */
184
178
  export const summarize = (current, coalesced, agoricNames) => {
185
179
  return {
@@ -0,0 +1,41 @@
1
+ import { NonNullish } from '@agoric/internal';
2
+
3
+ /**
4
+ * @import {MinimalNetworkConfig} from '@agoric/client-utils';
5
+ */
6
+
7
+ export const networkConfigUrl = agoricNetSubdomain =>
8
+ `https://${agoricNetSubdomain}.agoric.net/network-config`;
9
+ export const rpcUrl = agoricNetSubdomain =>
10
+ `https://${agoricNetSubdomain}.rpc.agoric.net:443`;
11
+
12
+ /**
13
+ * @param {string} str
14
+ * @param {{ fetch: typeof fetch }} io
15
+ * @returns {Promise<MinimalNetworkConfig>}
16
+ */
17
+ const fromAgoricNet = (str, { fetch }) => {
18
+ const [netName, chainName] = str.split(',');
19
+ if (chainName) {
20
+ return Promise.resolve({ chainName, rpcAddrs: [rpcUrl(netName)] });
21
+ }
22
+ return fetch(networkConfigUrl(netName)).then(res => res.json());
23
+ };
24
+
25
+ /**
26
+ * @param {{ env: typeof process.env, fetch: typeof fetch }} io
27
+ * @returns {Promise<MinimalNetworkConfig>}
28
+ */
29
+ export const getNetworkConfig = async ({ env, fetch }) => {
30
+ if (!('AGORIC_NET' in env) || env.AGORIC_NET === 'local') {
31
+ return { rpcAddrs: ['http://0.0.0.0:26657'], chainName: 'agoriclocal' };
32
+ }
33
+
34
+ return fromAgoricNet(NonNullish(env.AGORIC_NET), { fetch }).catch(err => {
35
+ throw Error(
36
+ `cannot get network config (${env.AGORIC_NET || 'local'}): ${
37
+ err.message
38
+ }`,
39
+ );
40
+ });
41
+ };