agoric 0.21.2-other-dev-8f8782b.0 → 0.21.2-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.
- package/README.md +0 -73
- package/package.json +60 -43
- package/src/anylogger-agoric.js +31 -16
- package/src/bin-agops.js +8 -12
- package/src/chain-config.js +42 -16
- package/src/commands/auction.js +22 -19
- package/src/commands/gov.js +475 -0
- package/src/commands/inter.js +44 -62
- package/src/commands/oracle.js +144 -87
- package/src/commands/perf.js +20 -16
- package/src/commands/psm.js +29 -26
- package/src/commands/reserve.js +13 -6
- package/src/commands/test-upgrade.js +15 -8
- package/src/commands/vaults.js +31 -18
- package/src/commands/wallet.js +121 -48
- package/src/cosmos.js +3 -3
- package/src/deploy.js +10 -5
- package/src/entrypoint.js +2 -5
- package/src/follow.js +14 -10
- package/src/helpers.js +10 -5
- package/src/init.js +2 -9
- package/src/install.js +18 -25
- package/src/lib/bundles.js +102 -0
- package/src/lib/chain.js +71 -35
- package/src/lib/format.js +28 -34
- package/src/lib/index.js +7 -0
- package/src/lib/packageManager.js +24 -0
- package/src/lib/wallet.js +44 -144
- package/src/main-publish.js +2 -3
- package/src/main.js +95 -125
- package/src/open.js +8 -10
- package/src/publish.js +4 -9
- package/src/scripts.js +14 -32
- package/src/sdk-package-names.js +22 -9
- package/src/set-defaults.js +2 -1
- package/src/start.js +59 -68
- package/tools/getting-started.js +272 -0
- package/tools/resm-plugin/deploy.js +18 -0
- package/tools/resm-plugin/package.json +12 -0
- package/tools/resm-plugin/src/output.js +1 -0
- package/tools/resm-plugin/src/plugin.js +17 -0
- package/CHANGELOG.md +0 -1069
- package/src/commands/ec.js +0 -314
- package/src/lib/rpc.js +0 -272
package/src/commands/ec.js
DELETED
|
@@ -1,314 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @jessie.js/no-nested-await */
|
|
2
|
-
// @ts-check
|
|
3
|
-
/* eslint-disable func-names */
|
|
4
|
-
/* global globalThis, process, setTimeout */
|
|
5
|
-
import { execFileSync as execFileSyncAmbient } from 'child_process';
|
|
6
|
-
import { Command, CommanderError } from 'commander';
|
|
7
|
-
import { normalizeAddressWithOptions, pollBlocks } from '../lib/chain.js';
|
|
8
|
-
import { getNetworkConfig, makeRpcUtils } from '../lib/rpc.js';
|
|
9
|
-
import {
|
|
10
|
-
findContinuingIds,
|
|
11
|
-
getCurrent,
|
|
12
|
-
getLastUpdate,
|
|
13
|
-
outputActionAndHint,
|
|
14
|
-
sendAction,
|
|
15
|
-
} from '../lib/wallet.js';
|
|
16
|
-
|
|
17
|
-
/** @typedef {import('@agoric/smart-wallet/src/offers.js').OfferSpec} OfferSpec */
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* @param {import('anylogger').Logger} _logger
|
|
21
|
-
* @param {{
|
|
22
|
-
* env?: Record<string, string|undefined>,
|
|
23
|
-
* fetch?: typeof window.fetch,
|
|
24
|
-
* stdout?: Pick<import('stream').Writable, 'write'>,
|
|
25
|
-
* stderr?: Pick<import('stream').Writable, 'write'>,
|
|
26
|
-
* execFileSync?: typeof execFileSyncAmbient,
|
|
27
|
-
* delay?: (ms: number) => Promise<void>,
|
|
28
|
-
* }} [io]
|
|
29
|
-
*/
|
|
30
|
-
export const makeEconomicCommiteeCommand = (_logger, io = {}) => {
|
|
31
|
-
const {
|
|
32
|
-
// Allow caller to provide access explicitly, but
|
|
33
|
-
// default to conventional ambient IO facilities.
|
|
34
|
-
env = process.env,
|
|
35
|
-
stdout = process.stdout,
|
|
36
|
-
stderr = process.stderr,
|
|
37
|
-
fetch = globalThis.fetch,
|
|
38
|
-
execFileSync = execFileSyncAmbient,
|
|
39
|
-
delay = ms => new Promise(resolve => setTimeout(resolve, ms)),
|
|
40
|
-
} = io;
|
|
41
|
-
|
|
42
|
-
const ec = new Command('ec').description('Economic Committee commands');
|
|
43
|
-
|
|
44
|
-
/** @param {string} literalOrName */
|
|
45
|
-
const normalizeAddress = literalOrName =>
|
|
46
|
-
normalizeAddressWithOptions(literalOrName, { keyringBackend: 'test' });
|
|
47
|
-
|
|
48
|
-
/** @type {(info: unknown, indent?: unknown) => boolean } */
|
|
49
|
-
const show = (info, indent) =>
|
|
50
|
-
stdout.write(`${JSON.stringify(info, null, indent ? 2 : undefined)}\n`);
|
|
51
|
-
|
|
52
|
-
const abortIfSeen = (instanceName, found) => {
|
|
53
|
-
const done = found.filter(it => it.instanceName === instanceName);
|
|
54
|
-
if (done.length > 0) {
|
|
55
|
-
console.warn(`invitation to ${instanceName} already accepted`, done);
|
|
56
|
-
throw new CommanderError(1, 'EALREADY', `already accepted`);
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Make an offer from agoricNames, wallet status; sign and broadcast it,
|
|
62
|
-
* given a sendFrom address; else print it.
|
|
63
|
-
*
|
|
64
|
-
* @param {{
|
|
65
|
-
* toOffer: (agoricNames: *, current: import('@agoric/smart-wallet/src/smartWallet').CurrentWalletRecord | undefined) => OfferSpec,
|
|
66
|
-
* sendFrom?: string | undefined,
|
|
67
|
-
* instanceName?: string,
|
|
68
|
-
* }} detail
|
|
69
|
-
* @param {Awaited<ReturnType<makeRpcUtils>>} [optUtils]
|
|
70
|
-
*/
|
|
71
|
-
const processOffer = async function ({ toOffer, sendFrom }, optUtils) {
|
|
72
|
-
const networkConfig = await getNetworkConfig(env);
|
|
73
|
-
const utils = await (optUtils || makeRpcUtils({ fetch }));
|
|
74
|
-
const { agoricNames, readLatestHead } = utils;
|
|
75
|
-
|
|
76
|
-
let current;
|
|
77
|
-
if (sendFrom) {
|
|
78
|
-
current = await getCurrent(sendFrom, { readLatestHead });
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const offer = toOffer(agoricNames, current);
|
|
82
|
-
if (!sendFrom) {
|
|
83
|
-
outputActionAndHint(
|
|
84
|
-
{ method: 'executeOffer', offer },
|
|
85
|
-
{ stdout, stderr },
|
|
86
|
-
);
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const result = await sendAction(
|
|
91
|
-
{ method: 'executeOffer', offer },
|
|
92
|
-
{
|
|
93
|
-
keyring: { backend: 'test' }, // XXX
|
|
94
|
-
from: sendFrom,
|
|
95
|
-
verbose: false,
|
|
96
|
-
...networkConfig,
|
|
97
|
-
execFileSync,
|
|
98
|
-
stdout,
|
|
99
|
-
delay,
|
|
100
|
-
},
|
|
101
|
-
);
|
|
102
|
-
assert(result); // not dry-run
|
|
103
|
-
const { timestamp, txhash, height } = result;
|
|
104
|
-
console.error('wallet action is broadcast:');
|
|
105
|
-
show({ timestamp, height, offerId: offer.id, txhash });
|
|
106
|
-
const checkInWallet = async blockInfo => {
|
|
107
|
-
const [state, update] = await Promise.all([
|
|
108
|
-
getCurrent(sendFrom, { readLatestHead }),
|
|
109
|
-
getLastUpdate(sendFrom, { readLatestHead }),
|
|
110
|
-
readLatestHead(`published.wallet.${sendFrom}`),
|
|
111
|
-
]);
|
|
112
|
-
if (update.updated === 'offerStatus' && update.status.id === offer.id) {
|
|
113
|
-
return blockInfo;
|
|
114
|
-
}
|
|
115
|
-
const info = await findContinuingIds(state, agoricNames);
|
|
116
|
-
const done = info.filter(it => it.offerId === offer.id);
|
|
117
|
-
if (!(done.length > 0)) throw Error('retry');
|
|
118
|
-
return blockInfo;
|
|
119
|
-
};
|
|
120
|
-
const blockInfo = await pollBlocks({
|
|
121
|
-
retryMessage: 'offer not yet in block',
|
|
122
|
-
...networkConfig,
|
|
123
|
-
execFileSync,
|
|
124
|
-
delay,
|
|
125
|
-
})(checkInWallet);
|
|
126
|
-
console.error('offer accepted in block');
|
|
127
|
-
show(blockInfo);
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
ec.command('committee')
|
|
131
|
-
.description('accept invitation to join the economic committee')
|
|
132
|
-
.option('--voter <number>', 'Voter number', Number, 0)
|
|
133
|
-
.option(
|
|
134
|
-
'--offerId <string>',
|
|
135
|
-
'Offer id',
|
|
136
|
-
String,
|
|
137
|
-
`ecCommittee-${Date.now()}`,
|
|
138
|
-
)
|
|
139
|
-
.option(
|
|
140
|
-
'--send-from <name-or-address>',
|
|
141
|
-
'Send from address',
|
|
142
|
-
normalizeAddress,
|
|
143
|
-
)
|
|
144
|
-
.action(async function (opts) {
|
|
145
|
-
/** @type {Parameters<typeof processOffer>[0]['toOffer']} */
|
|
146
|
-
const toOffer = (agoricNames, current) => {
|
|
147
|
-
const instance = agoricNames.instance.economicCommittee;
|
|
148
|
-
assert(instance, `missing economicCommittee`);
|
|
149
|
-
|
|
150
|
-
if (current) {
|
|
151
|
-
const found = findContinuingIds(current, agoricNames);
|
|
152
|
-
abortIfSeen('economicCommittee', found);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return {
|
|
156
|
-
id: opts.offerId,
|
|
157
|
-
invitationSpec: {
|
|
158
|
-
source: 'purse',
|
|
159
|
-
instance,
|
|
160
|
-
description: `Voter${opts.voter}`,
|
|
161
|
-
},
|
|
162
|
-
proposal: {},
|
|
163
|
-
};
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
await processOffer({
|
|
167
|
-
toOffer,
|
|
168
|
-
instanceName: 'economicCommittee',
|
|
169
|
-
...opts,
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
ec.command('charter')
|
|
174
|
-
.description('accept the charter invitation')
|
|
175
|
-
.option('--offerId <string>', 'Offer id', String, `ecCharter-${Date.now()}`)
|
|
176
|
-
.option(
|
|
177
|
-
'--send-from <name-or-address>',
|
|
178
|
-
'Send from address',
|
|
179
|
-
normalizeAddress,
|
|
180
|
-
)
|
|
181
|
-
.action(async function (opts) {
|
|
182
|
-
/** @type {Parameters<typeof processOffer>[0]['toOffer']} */
|
|
183
|
-
const toOffer = (agoricNames, current) => {
|
|
184
|
-
const instance = agoricNames.instance.econCommitteeCharter;
|
|
185
|
-
assert(instance, `missing econCommitteeCharter`);
|
|
186
|
-
|
|
187
|
-
if (current) {
|
|
188
|
-
const found = findContinuingIds(current, agoricNames);
|
|
189
|
-
abortIfSeen('econCommitteeCharter', found);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return {
|
|
193
|
-
id: opts.offerId,
|
|
194
|
-
invitationSpec: {
|
|
195
|
-
source: 'purse',
|
|
196
|
-
instance,
|
|
197
|
-
description: 'charter member invitation',
|
|
198
|
-
},
|
|
199
|
-
proposal: {},
|
|
200
|
-
};
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
await processOffer({
|
|
204
|
-
toOffer,
|
|
205
|
-
instanceName: 'econCommitteeCharter',
|
|
206
|
-
...opts,
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
ec.command('find-continuing-id')
|
|
211
|
-
.description('print id of specified voting continuing invitation')
|
|
212
|
-
.requiredOption(
|
|
213
|
-
'--from <name-or-address>',
|
|
214
|
-
'from address',
|
|
215
|
-
normalizeAddress,
|
|
216
|
-
)
|
|
217
|
-
.requiredOption('--for <string>', 'description of the invitation')
|
|
218
|
-
.action(async opts => {
|
|
219
|
-
const { agoricNames, readLatestHead } = await makeRpcUtils({ fetch });
|
|
220
|
-
const current = await getCurrent(opts.from, { readLatestHead });
|
|
221
|
-
|
|
222
|
-
const known = findContinuingIds(current, agoricNames);
|
|
223
|
-
if (!known) {
|
|
224
|
-
console.error('No continuing ids found');
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
const match = known.find(r => r.description === opts.for);
|
|
228
|
-
if (!match) {
|
|
229
|
-
console.error(`No match found for '${opts.for}'`);
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
console.log(match.offerId);
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
ec.command('find-continuing-ids')
|
|
237
|
-
.description('print records of voting continuing invitations')
|
|
238
|
-
.requiredOption(
|
|
239
|
-
'--from <name-or-address>',
|
|
240
|
-
'from address',
|
|
241
|
-
normalizeAddress,
|
|
242
|
-
)
|
|
243
|
-
.action(async opts => {
|
|
244
|
-
const { agoricNames, readLatestHead } = await makeRpcUtils({ fetch });
|
|
245
|
-
const current = await getCurrent(opts.from, { readLatestHead });
|
|
246
|
-
|
|
247
|
-
const found = findContinuingIds(current, agoricNames);
|
|
248
|
-
found.forEach(it => show({ ...it, address: opts.from }));
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
ec.command('vote')
|
|
252
|
-
.description('vote on a question (hard-coded for now))')
|
|
253
|
-
.option('--offerId <number>', 'Offer id', String, `ecVote-${Date.now()}`)
|
|
254
|
-
.requiredOption(
|
|
255
|
-
'--forPosition <number>',
|
|
256
|
-
'index of one position to vote for (within the question description.positions); ',
|
|
257
|
-
Number,
|
|
258
|
-
)
|
|
259
|
-
.requiredOption(
|
|
260
|
-
'--send-from <name-or-address>',
|
|
261
|
-
'Send from address',
|
|
262
|
-
normalizeAddress,
|
|
263
|
-
)
|
|
264
|
-
.action(async function (opts) {
|
|
265
|
-
const utils = await makeRpcUtils({ fetch });
|
|
266
|
-
const { readLatestHead } = utils;
|
|
267
|
-
|
|
268
|
-
const info = await readLatestHead(
|
|
269
|
-
'published.committees.Economic_Committee.latestQuestion',
|
|
270
|
-
).catch(err => {
|
|
271
|
-
throw new CommanderError(1, 'VSTORAGE_FAILURE', err.message);
|
|
272
|
-
});
|
|
273
|
-
// XXX runtime shape-check
|
|
274
|
-
const questionDesc = /** @type {any} */ (info);
|
|
275
|
-
|
|
276
|
-
// TODO support multiple position arguments
|
|
277
|
-
const chosenPositions = [questionDesc.positions[opts.forPosition]];
|
|
278
|
-
assert(chosenPositions, `undefined position index ${opts.forPosition}`);
|
|
279
|
-
|
|
280
|
-
/** @type {Parameters<typeof processOffer>[0]['toOffer']} */
|
|
281
|
-
const toOffer = (agoricNames, current) => {
|
|
282
|
-
const cont = current ? findContinuingIds(current, agoricNames) : [];
|
|
283
|
-
const votingRight = cont.find(
|
|
284
|
-
it => it.instance === agoricNames.instance.economicCommittee,
|
|
285
|
-
);
|
|
286
|
-
if (!votingRight) {
|
|
287
|
-
console.debug('continuing ids', cont, 'for', current);
|
|
288
|
-
throw new CommanderError(
|
|
289
|
-
1,
|
|
290
|
-
'NO_INVITATION',
|
|
291
|
-
'first, try: agops ec committee ...',
|
|
292
|
-
);
|
|
293
|
-
}
|
|
294
|
-
return {
|
|
295
|
-
id: opts.offerId,
|
|
296
|
-
invitationSpec: {
|
|
297
|
-
source: 'continuing',
|
|
298
|
-
previousOffer: votingRight.offerId,
|
|
299
|
-
invitationMakerName: 'makeVoteInvitation',
|
|
300
|
-
// (positionList, questionHandle)
|
|
301
|
-
invitationArgs: harden([
|
|
302
|
-
chosenPositions,
|
|
303
|
-
questionDesc.questionHandle,
|
|
304
|
-
]),
|
|
305
|
-
},
|
|
306
|
-
proposal: {},
|
|
307
|
-
};
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
await processOffer({ toOffer, sendFrom: opts.sendFrom }, utils);
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
return ec;
|
|
314
|
-
};
|
package/src/lib/rpc.js
DELETED
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
/* eslint-disable @jessie.js/no-nested-await */
|
|
3
|
-
/* global Buffer, fetch, process */
|
|
4
|
-
|
|
5
|
-
import { NonNullish } from '@agoric/assert';
|
|
6
|
-
import {
|
|
7
|
-
boardSlottingMarshaller,
|
|
8
|
-
makeBoardRemote,
|
|
9
|
-
} from '@agoric/vats/tools/board-utils.js';
|
|
10
|
-
|
|
11
|
-
export { boardSlottingMarshaller };
|
|
12
|
-
|
|
13
|
-
export const networkConfigUrl = agoricNetSubdomain =>
|
|
14
|
-
`https://${agoricNetSubdomain}.agoric.net/network-config`;
|
|
15
|
-
export const rpcUrl = agoricNetSubdomain =>
|
|
16
|
-
`https://${agoricNetSubdomain}.rpc.agoric.net:443`;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* @typedef {{ rpcAddrs: string[], chainName: string }} MinimalNetworkConfig
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* @param {string} str
|
|
24
|
-
* @returns {Promise<MinimalNetworkConfig>}
|
|
25
|
-
*/
|
|
26
|
-
const fromAgoricNet = str => {
|
|
27
|
-
const [netName, chainName] = str.split(',');
|
|
28
|
-
if (chainName) {
|
|
29
|
-
return Promise.resolve({ chainName, rpcAddrs: [rpcUrl(netName)] });
|
|
30
|
-
}
|
|
31
|
-
return fetch(networkConfigUrl(netName)).then(res => res.json());
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* @param {typeof process.env} env
|
|
36
|
-
* @returns {Promise<MinimalNetworkConfig>}
|
|
37
|
-
*/
|
|
38
|
-
export const getNetworkConfig = async env => {
|
|
39
|
-
if (!('AGORIC_NET' in env) || env.AGORIC_NET === 'local') {
|
|
40
|
-
return { rpcAddrs: ['http://0.0.0.0:26657'], chainName: 'agoriclocal' };
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return fromAgoricNet(NonNullish(env.AGORIC_NET)).catch(err => {
|
|
44
|
-
throw Error(
|
|
45
|
-
`cannot get network config (${env.AGORIC_NET || 'local'}): ${
|
|
46
|
-
err.message
|
|
47
|
-
}`,
|
|
48
|
-
);
|
|
49
|
-
});
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
/** @type {MinimalNetworkConfig} */
|
|
53
|
-
export const networkConfig = await getNetworkConfig(process.env);
|
|
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
|
-
throw Error(
|
|
83
|
-
`error code ${response?.code} reading ${kind} of ${path}: ${response.log}`,
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
return data;
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
return {
|
|
90
|
-
url,
|
|
91
|
-
decode({ result: { response } }) {
|
|
92
|
-
const { code } = response;
|
|
93
|
-
if (code !== 0) {
|
|
94
|
-
throw response;
|
|
95
|
-
}
|
|
96
|
-
const { value } = response;
|
|
97
|
-
return Buffer.from(value, 'base64').toString();
|
|
98
|
-
},
|
|
99
|
-
/**
|
|
100
|
-
*
|
|
101
|
-
* @param {string} path
|
|
102
|
-
* @returns {Promise<string>} latest vstorage value at path
|
|
103
|
-
*/
|
|
104
|
-
async readLatest(path = 'published') {
|
|
105
|
-
const raw = await readStorage(path, { kind: 'data' });
|
|
106
|
-
return this.decode(raw);
|
|
107
|
-
},
|
|
108
|
-
async keys(path = 'published') {
|
|
109
|
-
const raw = await readStorage(path, { kind: 'children' });
|
|
110
|
-
return JSON.parse(this.decode(raw)).children;
|
|
111
|
-
},
|
|
112
|
-
/**
|
|
113
|
-
* @param {string} path
|
|
114
|
-
* @param {number} [height] default is highest
|
|
115
|
-
* @returns {Promise<{blockHeight: number, values: string[]}>}
|
|
116
|
-
*/
|
|
117
|
-
async readAt(path, height = undefined) {
|
|
118
|
-
const raw = await readStorage(path, { kind: 'data', height });
|
|
119
|
-
const txt = this.decode(raw);
|
|
120
|
-
/** @type {{ value: string }} */
|
|
121
|
-
const { value } = JSON.parse(txt);
|
|
122
|
-
return JSON.parse(value);
|
|
123
|
-
},
|
|
124
|
-
/**
|
|
125
|
-
* Read values going back as far as available
|
|
126
|
-
*
|
|
127
|
-
* @param {string} path
|
|
128
|
-
* @param {number | string} [minHeight]
|
|
129
|
-
* @returns {Promise<string[]>}
|
|
130
|
-
*/
|
|
131
|
-
async readFully(path, minHeight = undefined) {
|
|
132
|
-
const parts = [];
|
|
133
|
-
// undefined the first iteration, to query at the highest
|
|
134
|
-
let blockHeight;
|
|
135
|
-
do {
|
|
136
|
-
// console.debug('READING', { blockHeight });
|
|
137
|
-
let values;
|
|
138
|
-
try {
|
|
139
|
-
// eslint-disable-next-line no-await-in-loop
|
|
140
|
-
({ blockHeight, values } = await this.readAt(
|
|
141
|
-
path,
|
|
142
|
-
blockHeight && Number(blockHeight) - 1,
|
|
143
|
-
));
|
|
144
|
-
// console.debug('readAt returned', { blockHeight });
|
|
145
|
-
} catch (err) {
|
|
146
|
-
if (err.message.match(/unknown request/)) {
|
|
147
|
-
// console.error(err);
|
|
148
|
-
break;
|
|
149
|
-
}
|
|
150
|
-
throw err;
|
|
151
|
-
}
|
|
152
|
-
parts.push(values);
|
|
153
|
-
// console.debug('PUSHED', values);
|
|
154
|
-
// console.debug('NEW', { blockHeight, minHeight });
|
|
155
|
-
if (minHeight && Number(blockHeight) <= Number(minHeight)) break;
|
|
156
|
-
} while (blockHeight > 0);
|
|
157
|
-
return parts.flat();
|
|
158
|
-
},
|
|
159
|
-
};
|
|
160
|
-
};
|
|
161
|
-
/** @typedef {ReturnType<typeof makeVStorage>} VStorage */
|
|
162
|
-
|
|
163
|
-
export const makeFromBoard = () => {
|
|
164
|
-
const cache = new Map();
|
|
165
|
-
const convertSlotToVal = (boardId, iface) => {
|
|
166
|
-
if (cache.has(boardId)) {
|
|
167
|
-
return cache.get(boardId);
|
|
168
|
-
}
|
|
169
|
-
const val = makeBoardRemote({ boardId, iface });
|
|
170
|
-
cache.set(boardId, val);
|
|
171
|
-
return val;
|
|
172
|
-
};
|
|
173
|
-
return harden({ convertSlotToVal });
|
|
174
|
-
};
|
|
175
|
-
/** @typedef {ReturnType<typeof makeFromBoard>} IdMap */
|
|
176
|
-
|
|
177
|
-
export const storageHelper = {
|
|
178
|
-
/** @param { string } txt */
|
|
179
|
-
parseCapData: txt => {
|
|
180
|
-
assert(typeof txt === 'string', typeof txt);
|
|
181
|
-
/** @type {{ value: string }} */
|
|
182
|
-
const { value } = JSON.parse(txt);
|
|
183
|
-
const specimen = JSON.parse(value);
|
|
184
|
-
const { blockHeight, values } = specimen;
|
|
185
|
-
assert(values, `empty values in specimen ${value}`);
|
|
186
|
-
const capDatas = storageHelper.parseMany(values);
|
|
187
|
-
return { blockHeight, capDatas };
|
|
188
|
-
},
|
|
189
|
-
/**
|
|
190
|
-
* @param {string} txt
|
|
191
|
-
* @param {IdMap} ctx
|
|
192
|
-
*/
|
|
193
|
-
unserializeTxt: (txt, ctx) => {
|
|
194
|
-
const { capDatas } = storageHelper.parseCapData(txt);
|
|
195
|
-
return capDatas.map(capData =>
|
|
196
|
-
boardSlottingMarshaller(ctx.convertSlotToVal).fromCapData(capData),
|
|
197
|
-
);
|
|
198
|
-
},
|
|
199
|
-
/** @param {string[]} capDataStrings array of stringified capData */
|
|
200
|
-
parseMany: capDataStrings => {
|
|
201
|
-
assert(capDataStrings && capDataStrings.length);
|
|
202
|
-
/** @type {{ body: string, slots: string[] }[]} */
|
|
203
|
-
const capDatas = capDataStrings.map(s => JSON.parse(s));
|
|
204
|
-
for (const capData of capDatas) {
|
|
205
|
-
assert(typeof capData === 'object' && capData !== null);
|
|
206
|
-
assert('body' in capData && 'slots' in capData);
|
|
207
|
-
assert(typeof capData.body === 'string');
|
|
208
|
-
assert(Array.isArray(capData.slots));
|
|
209
|
-
}
|
|
210
|
-
return capDatas;
|
|
211
|
-
},
|
|
212
|
-
};
|
|
213
|
-
harden(storageHelper);
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* @param {IdMap} ctx
|
|
217
|
-
* @param {VStorage} vstorage
|
|
218
|
-
* @returns {Promise<import('@agoric/vats/tools/board-utils.js').AgoricNamesRemotes>}
|
|
219
|
-
*/
|
|
220
|
-
export const makeAgoricNames = async (ctx, vstorage) => {
|
|
221
|
-
const reverse = {};
|
|
222
|
-
const entries = await Promise.all(
|
|
223
|
-
['brand', 'instance', 'vbankAsset'].map(async kind => {
|
|
224
|
-
const content = await vstorage.readLatest(
|
|
225
|
-
`published.agoricNames.${kind}`,
|
|
226
|
-
);
|
|
227
|
-
/** @type {Array<[string, import('@agoric/vats/tools/board-utils.js').BoardRemote]>} */
|
|
228
|
-
const parts = storageHelper.unserializeTxt(content, ctx).at(-1);
|
|
229
|
-
for (const [name, remote] of parts) {
|
|
230
|
-
if ('getBoardId' in remote) {
|
|
231
|
-
reverse[remote.getBoardId()] = name;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
return [kind, Object.fromEntries(parts)];
|
|
235
|
-
}),
|
|
236
|
-
);
|
|
237
|
-
return { ...Object.fromEntries(entries), reverse };
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* @param {{ fetch: typeof window.fetch }} io
|
|
242
|
-
* @param {MinimalNetworkConfig} config
|
|
243
|
-
*/
|
|
244
|
-
export const makeRpcUtils = async ({ fetch }, config = networkConfig) => {
|
|
245
|
-
try {
|
|
246
|
-
const vstorage = makeVStorage({ fetch }, config);
|
|
247
|
-
const fromBoard = makeFromBoard();
|
|
248
|
-
const agoricNames = await makeAgoricNames(fromBoard, vstorage);
|
|
249
|
-
|
|
250
|
-
const unserializer = boardSlottingMarshaller(fromBoard.convertSlotToVal);
|
|
251
|
-
|
|
252
|
-
/** @type {(txt: string) => unknown} */
|
|
253
|
-
const unserializeHead = txt =>
|
|
254
|
-
storageHelper.unserializeTxt(txt, fromBoard).at(-1);
|
|
255
|
-
|
|
256
|
-
/** @type {(path: string) => Promise<unknown>} */
|
|
257
|
-
const readLatestHead = path =>
|
|
258
|
-
vstorage.readLatest(path).then(unserializeHead);
|
|
259
|
-
|
|
260
|
-
return {
|
|
261
|
-
agoricNames,
|
|
262
|
-
fromBoard,
|
|
263
|
-
readLatestHead,
|
|
264
|
-
unserializeHead,
|
|
265
|
-
unserializer,
|
|
266
|
-
vstorage,
|
|
267
|
-
};
|
|
268
|
-
} catch (err) {
|
|
269
|
-
throw Error(`RPC failure (${config.rpcAddrs}): ${err.message}`);
|
|
270
|
-
}
|
|
271
|
-
};
|
|
272
|
-
/** @typedef {Awaited<ReturnType<typeof makeRpcUtils>>} RpcUtils */
|