agoric 0.22.0-u18.4 → 0.22.0-u18.6

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.
package/src/lib/wallet.js CHANGED
@@ -1,14 +1,17 @@
1
1
  // @ts-check
2
2
  /* eslint-env node */
3
3
 
4
- import { Fail } from '@endo/errors';
5
4
  import { iterateReverse } from '@agoric/casting';
5
+ import { boardSlottingMarshaller } from '@agoric/client-utils';
6
6
  import { makeWalletStateCoalescer } from '@agoric/smart-wallet/src/utils.js';
7
- import { execSwingsetTransaction, pollBlocks, pollTx } from './chain.js';
8
- import { boardSlottingMarshaller, makeRpcUtils } from './rpc.js';
7
+ import { Fail } from '@endo/errors';
8
+ import { execSwingsetTransaction, pollTx } from './chain.js';
9
9
 
10
- /** @import {CurrentWalletRecord} from '@agoric/smart-wallet/src/smartWallet.js' */
11
- /** @import {AgoricNamesRemotes} from '@agoric/vats/tools/board-utils.js' */
10
+ /**
11
+ * @import {CurrentWalletRecord} from '@agoric/smart-wallet/src/smartWallet.js';
12
+ * @import {AgoricNamesRemotes} from '@agoric/vats/tools/board-utils.js';
13
+ * @import {MinimalNetworkConfig, VstorageKit} from '@agoric/client-utils';
14
+ */
12
15
 
13
16
  const marshaller = boardSlottingMarshaller();
14
17
 
@@ -22,15 +25,15 @@ const emptyCurrentRecord = {
22
25
 
23
26
  /**
24
27
  * @param {string} addr
25
- * @param {Pick<import('./rpc.js').RpcUtils, 'readLatestHead'>} io
28
+ * @param {Pick<VstorageKit, 'readPublished'>} io
26
29
  * @returns {Promise<import('@agoric/smart-wallet/src/smartWallet.js').CurrentWalletRecord>}
27
30
  */
28
- export const getCurrent = async (addr, { readLatestHead }) => {
31
+ export const getCurrent = async (addr, { readPublished }) => {
29
32
  // Partial because older writes may not have had all properties
30
33
  // NB: assumes changes are only additions
31
34
  let current =
32
35
  /** @type {Partial<import('@agoric/smart-wallet/src/smartWallet.js').CurrentWalletRecord> | undefined} */ (
33
- await readLatestHead(`published.wallet.${addr}.current`)
36
+ await readPublished(`wallet.${addr}.current`)
34
37
  );
35
38
  if (current === undefined) {
36
39
  throw Error(`undefined current node for ${addr}`);
@@ -57,12 +60,11 @@ export const getCurrent = async (addr, { readLatestHead }) => {
57
60
 
58
61
  /**
59
62
  * @param {string} addr
60
- * @param {Pick<import('./rpc.js').RpcUtils, 'readLatestHead'>} io
63
+ * @param {Pick<VstorageKit, 'readPublished'>} io
61
64
  * @returns {Promise<import('@agoric/smart-wallet/src/smartWallet.js').UpdateRecord>}
62
65
  */
63
- export const getLastUpdate = (addr, { readLatestHead }) => {
64
- // @ts-expect-error cast
65
- return readLatestHead(`published.wallet.${addr}`);
66
+ export const getLastUpdate = (addr, { readPublished }) => {
67
+ return readPublished(`wallet.${addr}`);
66
68
  };
67
69
 
68
70
  /**
@@ -142,12 +144,12 @@ export const coalesceWalletState = async (follower, invitationBrand) => {
142
144
  *
143
145
  * @throws { Error & { code: number } } if transaction fails
144
146
  * @param {import('@agoric/smart-wallet/src/smartWallet.js').BridgeAction} bridgeAction
145
- * @param {import('./rpc.js').MinimalNetworkConfig & {
147
+ * @param {MinimalNetworkConfig & {
146
148
  * from: string,
147
149
  * fees?: string,
148
150
  * verbose?: boolean,
149
151
  * keyring?: {home?: string, backend: string},
150
- * stdout: Pick<import('stream').Writable, 'write'>,
152
+ * stdout?: Pick<import('stream').Writable, 'write'>,
151
153
  * execFileSync: typeof import('child_process').execFileSync,
152
154
  * delay: (ms: number) => Promise<void>,
153
155
  * dryRun?: boolean,
@@ -211,76 +213,3 @@ export const findContinuingIds = (current, agoricNames) => {
211
213
  }
212
214
  return found;
213
215
  };
214
-
215
- export const makeWalletUtils = async (
216
- { fetch, execFileSync, delay },
217
- networkConfig,
218
- ) => {
219
- const { agoricNames, fromBoard, readLatestHead, vstorage } =
220
- await makeRpcUtils({ fetch }, networkConfig);
221
- /**
222
- * @param {string} from
223
- * @param {number|string} [minHeight]
224
- */
225
- const storedWalletState = async (from, minHeight = undefined) => {
226
- const m = boardSlottingMarshaller(fromBoard.convertSlotToVal);
227
-
228
- const history = await vstorage.readFully(
229
- `published.wallet.${from}`,
230
- minHeight,
231
- );
232
-
233
- /** @type {{ Invitation: Brand<'set'> }} */
234
- // @ts-expect-error XXX how to narrow AssetKind to set?
235
- const { Invitation } = agoricNames.brand;
236
- const coalescer = makeWalletStateCoalescer(Invitation);
237
- // update with oldest first
238
- for (const txt of history.reverse()) {
239
- const { body, slots } = JSON.parse(txt);
240
- const record = m.fromCapData({ body, slots });
241
- coalescer.update(record);
242
- }
243
- const coalesced = coalescer.state;
244
- harden(coalesced);
245
- return coalesced;
246
- };
247
-
248
- /**
249
- * Get OfferStatus by id, polling until available.
250
- *
251
- * @param {string} from
252
- * @param {string|number} id
253
- * @param {number|string} minHeight
254
- * @param {boolean} [untilNumWantsSatisfied]
255
- */
256
- const pollOffer = async (
257
- from,
258
- id,
259
- minHeight,
260
- untilNumWantsSatisfied = false,
261
- ) => {
262
- const lookup = async () => {
263
- const { offerStatuses } = await storedWalletState(from, minHeight);
264
- const offerStatus = [...offerStatuses.values()].find(s => s.id === id);
265
- if (!offerStatus) throw Error('retry');
266
- harden(offerStatus);
267
- if (untilNumWantsSatisfied && !('numWantsSatisfied' in offerStatus)) {
268
- throw Error('retry (no numWantsSatisfied yet)');
269
- }
270
- return offerStatus;
271
- };
272
- const retryMessage = 'offer not in wallet at block';
273
- const opts = { ...networkConfig, execFileSync, delay, retryMessage };
274
- return pollBlocks(opts)(lookup);
275
- };
276
-
277
- return {
278
- networkConfig,
279
- agoricNames,
280
- fromBoard,
281
- vstorage,
282
- readLatestHead,
283
- storedWalletState,
284
- pollOffer,
285
- };
286
- };
package/src/main.js CHANGED
@@ -66,7 +66,9 @@ const main = async (progname, rawArgs, powers) => {
66
66
  'verbosity that can be increased',
67
67
  (_value, _previous) => (cmdOpts.verbose += 1),
68
68
  );
69
- const baseCmd = (...args) => addCmdOpts(program.command(...args));
69
+ /** @type {typeof program.command} */
70
+ const baseCmd = (nameAndParams, ...rest) =>
71
+ addCmdOpts(program.command(nameAndParams, ...rest));
70
72
 
71
73
  addCmdOpts(
72
74
  program
package/src/scripts.js CHANGED
@@ -56,7 +56,7 @@ export const makeLookup =
56
56
 
57
57
  /**
58
58
  * @param {string[]} scripts
59
- * @param {{ allowUnsafePlugins: boolean, progname: string, rawArgs: string[], endowments?: Record<string, any> }} opts
59
+ * @param {{ allowUnsafePlugins?: boolean, progname: string, rawArgs: string[], endowments?: Record<string, any> }} opts
60
60
  * @param {{ fs: import('fs/promises'), console: Console }} powers
61
61
  */
62
62
  export const makeScriptLoader =
@@ -9,6 +9,7 @@ export default [
9
9
  "@agoric/builders",
10
10
  "@agoric/cache",
11
11
  "@agoric/casting",
12
+ "@agoric/client-utils",
12
13
  "@agoric/cosmic-proto",
13
14
  "@agoric/cosmic-swingset",
14
15
  "@agoric/cosmos",
@@ -17,6 +18,7 @@ export default [
17
18
  "@agoric/deployment",
18
19
  "@agoric/ertp",
19
20
  "@agoric/eslint-config",
21
+ "@agoric/fast-usdc",
20
22
  "@agoric/governance",
21
23
  "@agoric/import-manager",
22
24
  "@agoric/inter-protocol",
@@ -49,6 +51,5 @@ export default [
49
51
  "@agoric/xsnap-lockdown",
50
52
  "@agoric/zoe",
51
53
  "@agoric/zone",
52
- "agoric",
53
- "fast-usdc"
54
+ "agoric"
54
55
  ];
package/src/start.js CHANGED
@@ -276,13 +276,13 @@ export default async function startMain(progname, rawArgs, powers, opts) {
276
276
  await rmVerbose(serverDir);
277
277
  }
278
278
 
279
+ /** @type {(args: string[], spawnOpts?: Parameters<typeof pspawn>[2], dockerArgs?: string[]) => ReturnType<pspawn>} */
279
280
  let chainSpawn;
280
281
  if (!popts.dockerTag) {
281
- chainSpawn = (args, spawnOpts = undefined) => {
282
- return pspawn(cosmosChain, [...args, `--home=${serverDir}`], spawnOpts);
283
- };
282
+ chainSpawn = (args, spawnOpts) =>
283
+ pspawn(cosmosChain, [...args, `--home=${serverDir}`], spawnOpts);
284
284
  } else {
285
- chainSpawn = (args, spawnOpts = undefined, dockerArgs = []) =>
285
+ chainSpawn = (args, spawnOpts, dockerArgs = []) =>
286
286
  pspawn(
287
287
  'docker',
288
288
  [
@@ -482,12 +482,12 @@ export default async function startMain(progname, rawArgs, powers, opts) {
482
482
  await rmVerbose(serverDir);
483
483
  }
484
484
 
485
+ /** @type {(args: string[], spawnOpts?: Parameters<typeof pspawn>[2], dockerArgs?: string[]) => ReturnType<pspawn>} */
485
486
  let soloSpawn;
486
487
  if (!popts.dockerTag) {
487
- soloSpawn = (args, spawnOpts = undefined) =>
488
- pspawn(agSolo, args, spawnOpts);
488
+ soloSpawn = (args, spawnOpts) => pspawn(agSolo, args, spawnOpts);
489
489
  } else {
490
- soloSpawn = (args, spawnOpts = undefined, dockerArgs = []) =>
490
+ soloSpawn = (args, spawnOpts, dockerArgs = []) =>
491
491
  pspawn(
492
492
  'docker',
493
493
  [
@@ -62,6 +62,13 @@ const getLatestBlockHeight = url =>
62
62
  req.end();
63
63
  });
64
64
 
65
+ /**
66
+ * Test the "getting started" workflow. Note that this function may be imported
67
+ * by external repositories.
68
+ *
69
+ * @param {import('ava').ExecutionContext} t
70
+ * @param {{ init?: string[], install?: string[] }} [options]
71
+ */
65
72
  export const gettingStartedWorkflowTest = async (t, options = {}) => {
66
73
  const { init: initOptions = [], install: installOptions = [] } = options;
67
74
  const pspawn = makePspawn({ spawn });
package/src/lib/rpc.js DELETED
@@ -1,285 +0,0 @@
1
- // @ts-check
2
- /* eslint-env node */
3
-
4
- import { NonNullish } from '@agoric/internal';
5
- import {
6
- boardSlottingMarshaller,
7
- makeBoardRemote,
8
- } from '@agoric/vats/tools/board-utils.js';
9
-
10
- export { boardSlottingMarshaller };
11
-
12
- export const networkConfigUrl = agoricNetSubdomain =>
13
- `https://${agoricNetSubdomain}.agoric.net/network-config`;
14
- export const rpcUrl = agoricNetSubdomain =>
15
- `https://${agoricNetSubdomain}.rpc.agoric.net:443`;
16
-
17
- /**
18
- * @typedef {{ rpcAddrs: string[], chainName: string }} MinimalNetworkConfig
19
- */
20
-
21
- /**
22
- * @param {string} str
23
- * @returns {Promise<MinimalNetworkConfig>}
24
- */
25
- const fromAgoricNet = str => {
26
- const [netName, chainName] = str.split(',');
27
- if (chainName) {
28
- return Promise.resolve({ chainName, rpcAddrs: [rpcUrl(netName)] });
29
- }
30
- return fetch(networkConfigUrl(netName)).then(res => res.json());
31
- };
32
-
33
- /**
34
- * @param {typeof process.env} env
35
- * @returns {Promise<MinimalNetworkConfig>}
36
- */
37
- export const getNetworkConfig = async env => {
38
- if (!('AGORIC_NET' in env) || env.AGORIC_NET === 'local') {
39
- return { rpcAddrs: ['http://0.0.0.0:26657'], chainName: 'agoriclocal' };
40
- }
41
-
42
- return fromAgoricNet(NonNullish(env.AGORIC_NET)).catch(err => {
43
- throw Error(
44
- `cannot get network config (${env.AGORIC_NET || 'local'}): ${
45
- err.message
46
- }`,
47
- );
48
- });
49
- };
50
-
51
- /** @type {MinimalNetworkConfig} */
52
- const networkConfig = await getNetworkConfig(process.env);
53
- export { networkConfig };
54
- // console.warn('networkConfig', networkConfig);
55
-
56
- /**
57
- * @param {object} powers
58
- * @param {typeof window.fetch} powers.fetch
59
- * @param {MinimalNetworkConfig} config
60
- */
61
- export const makeVStorage = (powers, config = networkConfig) => {
62
- /** @param {string} path */
63
- const getJSON = path => {
64
- const url = config.rpcAddrs[0] + path;
65
- // console.warn('fetching', url);
66
- return powers.fetch(url, { keepalive: true }).then(res => res.json());
67
- };
68
- // height=0 is the same as omitting height and implies the highest block
69
- const url = (path = 'published', { kind = 'children', height = 0 } = {}) =>
70
- `/abci_query?path=%22/custom/vstorage/${kind}/${path}%22&height=${height}`;
71
-
72
- const readStorage = (path = 'published', { kind = 'children', height = 0 }) =>
73
- getJSON(url(path, { kind, height }))
74
- .catch(err => {
75
- throw Error(`cannot read ${kind} of ${path}: ${err.message}`);
76
- })
77
- .then(data => {
78
- const {
79
- result: { response },
80
- } = data;
81
- if (response?.code !== 0) {
82
- /** @type {any} */
83
- const err = Error(
84
- `error code ${response?.code} reading ${kind} of ${path}: ${response.log}`,
85
- );
86
- err.code = response?.code;
87
- err.codespace = response?.codespace;
88
- throw err;
89
- }
90
- return data;
91
- });
92
-
93
- return {
94
- url,
95
- decode({ result: { response } }) {
96
- const { code } = response;
97
- if (code !== 0) {
98
- throw response;
99
- }
100
- const { value } = response;
101
- return Buffer.from(value, 'base64').toString();
102
- },
103
- /**
104
- *
105
- * @param {string} path
106
- * @returns {Promise<string>} latest vstorage value at path
107
- */
108
- async readLatest(path = 'published') {
109
- const raw = await readStorage(path, { kind: 'data' });
110
- return this.decode(raw);
111
- },
112
- async keys(path = 'published') {
113
- const raw = await readStorage(path, { kind: 'children' });
114
- return JSON.parse(this.decode(raw)).children;
115
- },
116
- /**
117
- * @param {string} path
118
- * @param {number} [height] default is highest
119
- * @returns {Promise<{blockHeight: number, values: string[]}>}
120
- */
121
- async readAt(path, height = undefined) {
122
- const raw = await readStorage(path, { kind: 'data', height });
123
- const txt = this.decode(raw);
124
- /** @type {{ value: string }} */
125
- const { value } = JSON.parse(txt);
126
- return JSON.parse(value);
127
- },
128
- /**
129
- * Read values going back as far as available
130
- *
131
- * @param {string} path
132
- * @param {number | string} [minHeight]
133
- * @returns {Promise<string[]>}
134
- */
135
- async readFully(path, minHeight = undefined) {
136
- const parts = [];
137
- // undefined the first iteration, to query at the highest
138
- let blockHeight;
139
- await null;
140
- do {
141
- // console.debug('READING', { blockHeight });
142
- let values;
143
- try {
144
- ({ blockHeight, values } = await this.readAt(
145
- path,
146
- blockHeight && Number(blockHeight) - 1,
147
- ));
148
- // console.debug('readAt returned', { blockHeight });
149
- } catch (err) {
150
- if (
151
- // CosmosSDK ErrInvalidRequest with particular message text;
152
- // misrepresentation of pruned data
153
- // TODO replace after incorporating a fix to
154
- // https://github.com/cosmos/cosmos-sdk/issues/19992
155
- err.codespace === 'sdk' &&
156
- err.code === 18 &&
157
- err.message.match(/pruned/)
158
- ) {
159
- // console.error(err);
160
- break;
161
- }
162
- throw err;
163
- }
164
- parts.push(values);
165
- // console.debug('PUSHED', values);
166
- // console.debug('NEW', { blockHeight, minHeight });
167
- if (minHeight && Number(blockHeight) <= Number(minHeight)) break;
168
- } while (blockHeight > 0);
169
- return parts.flat();
170
- },
171
- };
172
- };
173
- /** @typedef {ReturnType<typeof makeVStorage>} VStorage */
174
-
175
- export const makeFromBoard = () => {
176
- const cache = new Map();
177
- const convertSlotToVal = (boardId, iface) => {
178
- if (cache.has(boardId)) {
179
- return cache.get(boardId);
180
- }
181
- const val = makeBoardRemote({ boardId, iface });
182
- cache.set(boardId, val);
183
- return val;
184
- };
185
- return harden({ convertSlotToVal });
186
- };
187
- /** @typedef {ReturnType<typeof makeFromBoard>} IdMap */
188
-
189
- export const storageHelper = {
190
- /** @param { string } txt */
191
- parseCapData: txt => {
192
- assert(typeof txt === 'string', typeof txt);
193
- /** @type {{ value: string }} */
194
- const { value } = JSON.parse(txt);
195
- const specimen = JSON.parse(value);
196
- const { blockHeight, values } = specimen;
197
- assert(values, `empty values in specimen ${value}`);
198
- const capDatas = storageHelper.parseMany(values);
199
- return { blockHeight, capDatas };
200
- },
201
- /**
202
- * @param {string} txt
203
- * @param {IdMap} ctx
204
- */
205
- unserializeTxt: (txt, ctx) => {
206
- const { capDatas } = storageHelper.parseCapData(txt);
207
- return capDatas.map(capData =>
208
- boardSlottingMarshaller(ctx.convertSlotToVal).fromCapData(capData),
209
- );
210
- },
211
- /** @param {string[]} capDataStrings array of stringified capData */
212
- parseMany: capDataStrings => {
213
- assert(capDataStrings && capDataStrings.length);
214
- /** @type {{ body: string, slots: string[] }[]} */
215
- const capDatas = capDataStrings.map(s => JSON.parse(s));
216
- for (const capData of capDatas) {
217
- assert(typeof capData === 'object' && capData !== null);
218
- assert('body' in capData && 'slots' in capData);
219
- assert(typeof capData.body === 'string');
220
- assert(Array.isArray(capData.slots));
221
- }
222
- return capDatas;
223
- },
224
- };
225
- harden(storageHelper);
226
-
227
- /**
228
- * @param {IdMap} ctx
229
- * @param {VStorage} vstorage
230
- * @returns {Promise<import('@agoric/vats/tools/board-utils.js').AgoricNamesRemotes>}
231
- */
232
- export const makeAgoricNames = async (ctx, vstorage) => {
233
- const reverse = {};
234
- const entries = await Promise.all(
235
- ['brand', 'instance', 'vbankAsset'].map(async kind => {
236
- const content = await vstorage.readLatest(
237
- `published.agoricNames.${kind}`,
238
- );
239
- /** @type {Array<[string, import('@agoric/vats/tools/board-utils.js').BoardRemote]>} */
240
- const parts = storageHelper.unserializeTxt(content, ctx).at(-1);
241
- for (const [name, remote] of parts) {
242
- if ('getBoardId' in remote) {
243
- reverse[remote.getBoardId()] = name;
244
- }
245
- }
246
- return [kind, Object.fromEntries(parts)];
247
- }),
248
- );
249
- return { ...Object.fromEntries(entries), reverse };
250
- };
251
-
252
- /**
253
- * @param {{ fetch: typeof window.fetch }} io
254
- * @param {MinimalNetworkConfig} config
255
- */
256
- export const makeRpcUtils = async ({ fetch }, config = networkConfig) => {
257
- await null;
258
- try {
259
- const vstorage = makeVStorage({ fetch }, config);
260
- const fromBoard = makeFromBoard();
261
- const agoricNames = await makeAgoricNames(fromBoard, vstorage);
262
-
263
- const unserializer = boardSlottingMarshaller(fromBoard.convertSlotToVal);
264
-
265
- /** @type {(txt: string) => unknown} */
266
- const unserializeHead = txt =>
267
- storageHelper.unserializeTxt(txt, fromBoard).at(-1);
268
-
269
- /** @type {(path: string) => Promise<unknown>} */
270
- const readLatestHead = path =>
271
- vstorage.readLatest(path).then(unserializeHead);
272
-
273
- return {
274
- agoricNames,
275
- fromBoard,
276
- readLatestHead,
277
- unserializeHead,
278
- unserializer,
279
- vstorage,
280
- };
281
- } catch (err) {
282
- throw Error(`RPC failure (${config.rpcAddrs}): ${err.message}`);
283
- }
284
- };
285
- /** @typedef {Awaited<ReturnType<typeof makeRpcUtils>>} RpcUtils */