@agoric/casting 0.3.5-pismo-dev-0d5327f.0 → 0.4.1-dev-bf4b59f.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.
- package/CHANGELOG.md +34 -0
- package/README.md +1 -1
- package/jsconfig.build.json +12 -0
- package/jsconfig.json +1 -15
- package/node-fetch-shim.d.ts +2 -0
- package/node-fetch-shim.d.ts.map +1 -0
- package/node-fetch-shim.js +3 -0
- package/package.json +22 -17
- package/src/casting-spec.d.ts +16 -0
- package/src/casting-spec.d.ts.map +1 -0
- package/src/casting-spec.js +39 -6
- package/src/change-follower.d.ts +2 -0
- package/src/change-follower.d.ts.map +1 -0
- package/src/change-follower.js +0 -1
- package/src/defaults.d.ts +21 -0
- package/src/defaults.d.ts.map +1 -0
- package/src/defaults.js +8 -3
- package/src/follower-cosmjs.d.ts +12 -0
- package/src/follower-cosmjs.d.ts.map +1 -0
- package/src/follower-cosmjs.js +114 -143
- package/src/follower.d.ts +2 -0
- package/src/follower.d.ts.map +1 -0
- package/src/follower.js +7 -7
- package/src/iterable.d.ts +24 -0
- package/src/iterable.d.ts.map +1 -0
- package/src/iterable.js +3 -45
- package/src/leader-netconfig.d.ts +4 -0
- package/src/leader-netconfig.d.ts.map +1 -0
- package/src/leader-netconfig.js +7 -6
- package/src/leader.d.ts +2 -0
- package/src/leader.d.ts.map +1 -0
- package/src/leader.js +0 -1
- package/src/main.d.ts +9 -0
- package/src/main.d.ts.map +1 -0
- package/src/main.js +2 -0
- package/src/netconfig.d.ts +47 -0
- package/src/netconfig.d.ts.map +1 -0
- package/src/netconfig.js +40 -0
- package/src/shuffle.d.ts +2 -0
- package/src/shuffle.d.ts.map +1 -0
- package/src/shuffle.js +0 -2
- package/src/types.d.ts +54 -0
- package/src/types.d.ts.map +1 -0
- package/src/types.js +3 -3
- package/test/deploy.js +1 -2
- package/test/fake-rpc-server.js +6 -3
- package/test/test-mvp.js +1 -1
- package/test/test-netconfig.js +48 -0
package/src/follower-cosmjs.js
CHANGED
|
@@ -1,38 +1,33 @@
|
|
|
1
|
-
// @ts-check
|
|
2
1
|
/// <reference types="ses"/>
|
|
3
|
-
/* eslint-disable no-await-in-loop,
|
|
2
|
+
/* eslint-disable no-await-in-loop, @jessie.js/no-nested-await */
|
|
4
3
|
|
|
5
4
|
import { E, Far } from '@endo/far';
|
|
6
|
-
import * as
|
|
5
|
+
import * as tendermint34 from '@cosmjs/tendermint-rpc';
|
|
7
6
|
import * as stargateStar from '@cosmjs/stargate';
|
|
8
7
|
|
|
8
|
+
import { isStreamCell } from '@agoric/internal/src/lib-chainStorage.js';
|
|
9
|
+
|
|
9
10
|
import { MAKE_DEFAULT_DECODER, MAKE_DEFAULT_UNSERIALIZER } from './defaults.js';
|
|
10
11
|
import { makeCastingSpec } from './casting-spec.js';
|
|
11
12
|
import { makeLeader as defaultMakeLeader } from './leader-netconfig.js';
|
|
12
13
|
|
|
13
14
|
const { QueryClient } = stargateStar;
|
|
14
|
-
const { Tendermint34Client } =
|
|
15
|
-
const { details: X, quote: q } = assert;
|
|
15
|
+
const { Tendermint34Client } = tendermint34;
|
|
16
|
+
const { details: X, quote: q, Fail } = assert;
|
|
16
17
|
const textDecoder = new TextDecoder();
|
|
17
18
|
|
|
18
|
-
/** @template T @typedef {import('./types.js').
|
|
19
|
-
/** @template T @typedef {import('./types.js').ValueFollowerElement<T>} ValueFollowerElement */
|
|
20
|
-
/** @template T @typedef {import('./types.js').Follower<ValueFollowerElement<T>>} ValueFollower */
|
|
19
|
+
/** @template T @typedef {import('./types.js').Follower<import('./types.js').ValueFollowerElement<T>>} ValueFollower */
|
|
21
20
|
|
|
21
|
+
// Copied from https://github.com/cosmos/cosmjs/pull/1328/files until release
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
23
|
+
* @typedef {{
|
|
24
|
+
* readonly value: Uint8Array;
|
|
25
|
+
* readonly height: number;
|
|
26
|
+
* }} QueryStoreResponse
|
|
27
|
+
* The response of an ABCI query to Tendermint.
|
|
28
|
+
* This is a subset of `tendermint34.AbciQueryResponse` in order
|
|
29
|
+
* to abstract away Tendermint versions.
|
|
29
30
|
*/
|
|
30
|
-
const isStreamCell = cell =>
|
|
31
|
-
cell &&
|
|
32
|
-
typeof cell === 'object' &&
|
|
33
|
-
Array.isArray(cell.values) &&
|
|
34
|
-
typeof cell.blockHeight === 'string' &&
|
|
35
|
-
/^0$|^[1-9][0-9]*$/.test(cell.blockHeight);
|
|
36
31
|
|
|
37
32
|
/**
|
|
38
33
|
* @param {Uint8Array} a
|
|
@@ -52,6 +47,22 @@ const arrayEqual = (a, b) => {
|
|
|
52
47
|
|
|
53
48
|
const defaultDataPrefixBytes = new Uint8Array();
|
|
54
49
|
|
|
50
|
+
/**
|
|
51
|
+
* @param {Uint8Array} prefixedData
|
|
52
|
+
* @param {Uint8Array} prefix
|
|
53
|
+
*/
|
|
54
|
+
const stripPrefix = (prefixedData, prefix) => {
|
|
55
|
+
assert(
|
|
56
|
+
prefixedData.length >= prefix.length,
|
|
57
|
+
X`result too short for data prefix ${prefix}`,
|
|
58
|
+
);
|
|
59
|
+
assert(
|
|
60
|
+
arrayEqual(prefixedData.subarray(0, prefix.length), prefix),
|
|
61
|
+
X`${prefixedData} doesn't start with data prefix ${prefix}`,
|
|
62
|
+
);
|
|
63
|
+
return prefixedData.slice(prefix.length);
|
|
64
|
+
};
|
|
65
|
+
|
|
55
66
|
/**
|
|
56
67
|
* @template T
|
|
57
68
|
* @param {Iterable<T>} values
|
|
@@ -103,12 +114,11 @@ export const makeCosmjsFollower = (
|
|
|
103
114
|
if (crasher) {
|
|
104
115
|
E(crasher)
|
|
105
116
|
.crash(`PROOF VERIFICATION FAILURE; crashing follower`, err)
|
|
106
|
-
.catch(e =>
|
|
117
|
+
.catch(e => Fail`crashing follower failed: ${e}`);
|
|
107
118
|
}
|
|
108
119
|
throw err;
|
|
109
120
|
};
|
|
110
|
-
|
|
111
|
-
assert(proofs.includes(proof), X`unrecognized follower proof mode ${proof}`);
|
|
121
|
+
proofs.includes(proof) || Fail`unrecognized follower proof mode ${proof}`;
|
|
112
122
|
|
|
113
123
|
const where = 'CosmJS follower';
|
|
114
124
|
const castingSpecP = makeCastingSpec(sourceP);
|
|
@@ -119,7 +129,7 @@ export const makeCosmjsFollower = (
|
|
|
119
129
|
const tendermintClientPs = new Map();
|
|
120
130
|
/**
|
|
121
131
|
* @param {string} endpoint
|
|
122
|
-
* @returns {
|
|
132
|
+
* @returns {tendermint34.Tendermint34Client}
|
|
123
133
|
*/
|
|
124
134
|
const provideTendermintClient = endpoint => {
|
|
125
135
|
let clientP = tendermintClientPs.get(endpoint);
|
|
@@ -130,17 +140,6 @@ export const makeCosmjsFollower = (
|
|
|
130
140
|
return clientP;
|
|
131
141
|
};
|
|
132
142
|
|
|
133
|
-
const getBlockHeight = async () => {
|
|
134
|
-
const values = await E(leader).mapEndpoints(where, async endpoint => {
|
|
135
|
-
const client = await provideTendermintClient(endpoint);
|
|
136
|
-
const info = await client.abciInfo();
|
|
137
|
-
const { lastBlockHeight } = info;
|
|
138
|
-
assert.typeof(lastBlockHeight, 'number');
|
|
139
|
-
return lastBlockHeight;
|
|
140
|
-
});
|
|
141
|
-
return collectSingle(values);
|
|
142
|
-
};
|
|
143
|
-
|
|
144
143
|
/** @type {Map<string, import('@cosmjs/stargate').QueryClient>} */
|
|
145
144
|
const endpointToQueryClient = new Map();
|
|
146
145
|
|
|
@@ -162,24 +161,23 @@ export const makeCosmjsFollower = (
|
|
|
162
161
|
};
|
|
163
162
|
|
|
164
163
|
/**
|
|
165
|
-
* @param {(endpoint: string, storeName: string, storeSubkey: Uint8Array) => Promise<
|
|
164
|
+
* @param {(endpoint: string, storeName: string, storeSubkey: Uint8Array) => Promise<QueryStoreResponse>} tryGetPrefixedData
|
|
165
|
+
* @returns {Promise<QueryStoreResponse>}
|
|
166
166
|
*/
|
|
167
|
-
const
|
|
167
|
+
const retryGetPrefixedData = async tryGetPrefixedData => {
|
|
168
168
|
const {
|
|
169
169
|
storeName,
|
|
170
170
|
storeSubkey,
|
|
171
171
|
dataPrefixBytes = defaultDataPrefixBytes,
|
|
172
|
+
noDataValue,
|
|
172
173
|
} = await castingSpecP;
|
|
173
174
|
|
|
174
|
-
|
|
175
|
-
storeName,
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
storeSubkey,
|
|
181
|
-
X`storeSubkey must be a Uint8Array, got ${storeSubkey}`,
|
|
182
|
-
);
|
|
175
|
+
if (typeof storeName !== 'string') {
|
|
176
|
+
throw Fail`storeName must be a string, got ${storeName}`;
|
|
177
|
+
}
|
|
178
|
+
if (!storeSubkey) {
|
|
179
|
+
throw Fail`storeSubkey must be a Uint8Array, got ${storeSubkey}`;
|
|
180
|
+
}
|
|
183
181
|
|
|
184
182
|
// mapEndpoints is our retry loop.
|
|
185
183
|
const values = await E(leader).mapEndpoints(where, async endpoint =>
|
|
@@ -199,60 +197,47 @@ export const makeCosmjsFollower = (
|
|
|
199
197
|
}
|
|
200
198
|
assert(result);
|
|
201
199
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
200
|
+
/** @type {Uint8Array} */
|
|
201
|
+
let value;
|
|
202
|
+
if (result.value.length === 0) {
|
|
203
|
+
value = result.value;
|
|
204
|
+
} else if (noDataValue && arrayEqual(result.value, noDataValue)) {
|
|
205
|
+
value = new Uint8Array();
|
|
206
|
+
} else {
|
|
207
|
+
value = stripPrefix(result.value, dataPrefixBytes);
|
|
205
208
|
}
|
|
206
|
-
|
|
207
|
-
// Handle the data prefix if any.
|
|
208
|
-
assert(
|
|
209
|
-
result.length >= dataPrefixBytes.length,
|
|
210
|
-
X`result too short for data prefix ${dataPrefixBytes}`,
|
|
211
|
-
);
|
|
212
|
-
assert(
|
|
213
|
-
arrayEqual(result.subarray(0, dataPrefixBytes.length), dataPrefixBytes),
|
|
214
|
-
X`${result} doesn't start with data prefix ${dataPrefixBytes}`,
|
|
215
|
-
);
|
|
216
|
-
return result.slice(dataPrefixBytes.length);
|
|
209
|
+
return { value, height: result.height };
|
|
217
210
|
};
|
|
218
211
|
|
|
219
212
|
/**
|
|
220
213
|
* @param {number} [height]
|
|
221
|
-
* @returns {Promise<
|
|
214
|
+
* @returns {Promise<QueryStoreResponse>}
|
|
222
215
|
*/
|
|
223
216
|
const getProvenDataAtHeight = async height => {
|
|
224
|
-
return
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
},
|
|
229
|
-
);
|
|
217
|
+
return retryGetPrefixedData(async (endpoint, storeName, storeSubkey) => {
|
|
218
|
+
const queryClient = await provideQueryClient(endpoint);
|
|
219
|
+
return E(queryClient).queryStoreVerified(storeName, storeSubkey, height);
|
|
220
|
+
});
|
|
230
221
|
};
|
|
231
222
|
|
|
232
223
|
/**
|
|
233
|
-
* @param {number} height
|
|
224
|
+
* @param {number} [height] desired height, or the latest height if not set
|
|
225
|
+
* @returns {Promise<QueryStoreResponse>}
|
|
234
226
|
*/
|
|
235
227
|
const getUnprovenDataAtHeight = async height => {
|
|
236
|
-
return
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
});
|
|
245
|
-
if (response.code !== 0) {
|
|
246
|
-
throw new Error(`Tendermint ABCI query failed: ${response.log}`);
|
|
247
|
-
}
|
|
248
|
-
const { value } = response;
|
|
249
|
-
return value;
|
|
250
|
-
},
|
|
251
|
-
);
|
|
228
|
+
return retryGetPrefixedData(async (endpoint, storeName, storeSubkey) => {
|
|
229
|
+
const queryClient = await provideQueryClient(endpoint);
|
|
230
|
+
return E(queryClient).queryAbci(
|
|
231
|
+
`store/${storeName}/key`,
|
|
232
|
+
storeSubkey,
|
|
233
|
+
height,
|
|
234
|
+
);
|
|
235
|
+
});
|
|
252
236
|
};
|
|
253
237
|
|
|
254
238
|
/**
|
|
255
|
-
* @param {number} blockHeight
|
|
239
|
+
* @param {number} [blockHeight] desired height, or the latest height if not set
|
|
240
|
+
* @returns {Promise<QueryStoreResponse>}
|
|
256
241
|
*/
|
|
257
242
|
const tryGetDataAtHeight = async blockHeight => {
|
|
258
243
|
if (proof === 'strict') {
|
|
@@ -262,36 +247,33 @@ export const makeCosmjsFollower = (
|
|
|
262
247
|
// Fast and loose.
|
|
263
248
|
return getUnprovenDataAtHeight(blockHeight);
|
|
264
249
|
} else if (proof === 'optimistic') {
|
|
265
|
-
const
|
|
250
|
+
const alleged = await getUnprovenDataAtHeight(blockHeight);
|
|
266
251
|
|
|
267
252
|
// Prove later, since it may take time we say we can't afford.
|
|
268
|
-
getProvenDataAtHeight(
|
|
269
|
-
if (arrayEqual(
|
|
253
|
+
getProvenDataAtHeight(alleged.height).then(proven => {
|
|
254
|
+
if (arrayEqual(proven.value, alleged.value)) {
|
|
270
255
|
return;
|
|
271
256
|
}
|
|
272
257
|
crash(
|
|
273
258
|
assert.error(
|
|
274
|
-
X`Alleged value ${
|
|
259
|
+
X`Alleged value ${alleged.value} did not match proof ${proven.value}`,
|
|
275
260
|
),
|
|
276
261
|
);
|
|
277
262
|
}, crash);
|
|
278
263
|
|
|
279
264
|
// Speculate that we got the right value.
|
|
280
|
-
return
|
|
265
|
+
return alleged;
|
|
281
266
|
}
|
|
282
267
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
)}, must be one of strict, none, or optimistic`,
|
|
287
|
-
);
|
|
268
|
+
throw Fail`Unrecognized proof option ${q(
|
|
269
|
+
proof,
|
|
270
|
+
)}, must be one of strict, none, or optimistic`;
|
|
288
271
|
};
|
|
289
272
|
|
|
290
273
|
/**
|
|
291
|
-
* @param {number} blockHeight
|
|
274
|
+
* @param {number} [blockHeight] desired height, or the latest height if not set
|
|
292
275
|
*/
|
|
293
276
|
const getDataAtHeight = async blockHeight => {
|
|
294
|
-
assert.typeof(blockHeight, 'number');
|
|
295
277
|
for (let attempt = 0; ; attempt += 1) {
|
|
296
278
|
try {
|
|
297
279
|
// AWAIT
|
|
@@ -341,7 +323,7 @@ export const makeCosmjsFollower = (
|
|
|
341
323
|
* @param {any} data
|
|
342
324
|
* @param {number} blockHeight
|
|
343
325
|
* @param {number} currentBlockHeight
|
|
344
|
-
* @returns {Promise<ValueFollowerElement<T>>}
|
|
326
|
+
* @returns {Promise<import('./types.js').ValueFollowerElement<T>>}
|
|
345
327
|
*/
|
|
346
328
|
const followerElementFromStreamCellValue = async (
|
|
347
329
|
data,
|
|
@@ -350,13 +332,13 @@ export const makeCosmjsFollower = (
|
|
|
350
332
|
) => {
|
|
351
333
|
// AWAIT
|
|
352
334
|
const value = await /** @type {T} */ (
|
|
353
|
-
unserializer ? E(unserializer).
|
|
335
|
+
unserializer ? E(unserializer).fromCapData(data) : data
|
|
354
336
|
);
|
|
355
337
|
return { value, blockHeight, currentBlockHeight };
|
|
356
338
|
};
|
|
357
339
|
|
|
358
340
|
/**
|
|
359
|
-
* @param {StreamCell<T>} streamCell
|
|
341
|
+
* @param {import('./types.js').StreamCell<T>} streamCell
|
|
360
342
|
* @param {number} currentBlockHeight
|
|
361
343
|
* @yields {ValueFollowerElement<T>}
|
|
362
344
|
*/
|
|
@@ -371,7 +353,7 @@ export const makeCosmjsFollower = (
|
|
|
371
353
|
}
|
|
372
354
|
|
|
373
355
|
/**
|
|
374
|
-
* @param {StreamCell<T>} streamCell
|
|
356
|
+
* @param {import('./types.js').StreamCell<T>} streamCell
|
|
375
357
|
* @param {number} currentBlockHeight
|
|
376
358
|
* @yields {ValueFollowerElement<T>}
|
|
377
359
|
*/
|
|
@@ -386,7 +368,7 @@ export const makeCosmjsFollower = (
|
|
|
386
368
|
}
|
|
387
369
|
|
|
388
370
|
/**
|
|
389
|
-
* @param {StreamCell<T>} streamCell
|
|
371
|
+
* @param {import('./types.js').StreamCell<T>} streamCell
|
|
390
372
|
* @param {number} currentBlockHeight
|
|
391
373
|
* @yields {ValueFollowerElement<T>}
|
|
392
374
|
*/
|
|
@@ -406,53 +388,51 @@ export const makeCosmjsFollower = (
|
|
|
406
388
|
* @yields {ValueFollowerElement<T>}
|
|
407
389
|
*/
|
|
408
390
|
async function* getLatestIterable() {
|
|
409
|
-
|
|
410
|
-
let
|
|
391
|
+
/** @type {number | undefined} the last known latest height */
|
|
392
|
+
let lastHeight;
|
|
393
|
+
let lastValue;
|
|
411
394
|
for (;;) {
|
|
412
|
-
const
|
|
413
|
-
if (
|
|
395
|
+
const latest = await getDataAtHeight();
|
|
396
|
+
if (lastHeight && latest.height <= lastHeight) {
|
|
397
|
+
// Wait for a fresh block
|
|
414
398
|
// TODO Long-poll for next block
|
|
415
399
|
// https://github.com/Agoric/agoric-sdk/issues/6154
|
|
416
400
|
await E(leader).jitter(where);
|
|
417
401
|
continue;
|
|
418
402
|
}
|
|
419
403
|
|
|
420
|
-
|
|
421
|
-
|
|
404
|
+
if (latest.value.length === 0) {
|
|
405
|
+
// No value, so try again
|
|
422
406
|
// TODO Long-poll for block data change
|
|
423
407
|
// https://github.com/Agoric/agoric-sdk/issues/6154
|
|
424
408
|
await E(leader).jitter(where);
|
|
425
409
|
continue;
|
|
426
410
|
}
|
|
427
|
-
const currentStreamCell = streamCellForData(
|
|
428
|
-
currentBlockHeight,
|
|
429
|
-
currentData,
|
|
430
|
-
);
|
|
411
|
+
const currentStreamCell = streamCellForData(latest.height, latest.value);
|
|
431
412
|
|
|
432
|
-
|
|
413
|
+
lastHeight = latest.height;
|
|
433
414
|
|
|
434
415
|
// Ignore adjacent duplicates.
|
|
435
416
|
// This can only occur for legacy cells.
|
|
436
417
|
// It is possible that the data changed from and back to the last
|
|
437
418
|
// sampled data, but ignoring intermediate changes is consistent with
|
|
438
419
|
// the semantics of getLatestIterable.
|
|
439
|
-
if (
|
|
420
|
+
if (lastValue !== undefined && arrayEqual(lastValue, latest.value)) {
|
|
440
421
|
continue;
|
|
441
422
|
}
|
|
442
423
|
// However, streamCells that vacillate will reemit, since each iteration
|
|
443
424
|
// at a unique block height is considered distinct.
|
|
444
425
|
|
|
445
|
-
yield* lastValueFromCell(currentStreamCell,
|
|
446
|
-
|
|
426
|
+
yield* lastValueFromCell(currentStreamCell, latest.height);
|
|
427
|
+
lastValue = latest.value;
|
|
447
428
|
}
|
|
448
429
|
}
|
|
449
430
|
|
|
450
431
|
/**
|
|
451
|
-
* @param {number} cursorBlockHeight
|
|
432
|
+
* @param {number} [cursorBlockHeight]
|
|
452
433
|
* @yields {ValueFollowerElement<T>}
|
|
453
434
|
*/
|
|
454
435
|
async function* getEachIterableAtHeight(cursorBlockHeight) {
|
|
455
|
-
assert.typeof(cursorBlockHeight, 'number');
|
|
456
436
|
// Track the data for the last emitted cell (the cell at the
|
|
457
437
|
// cursorBlockHeight) so we know not to emit duplicates
|
|
458
438
|
// of that cell.
|
|
@@ -462,7 +442,9 @@ export const makeCosmjsFollower = (
|
|
|
462
442
|
// If the block has no corresponding data, wait for the first block to
|
|
463
443
|
// contain data.
|
|
464
444
|
for (;;) {
|
|
465
|
-
cursorData = await getDataAtHeight(
|
|
445
|
+
({ value: cursorData, height: cursorBlockHeight } = await getDataAtHeight(
|
|
446
|
+
cursorBlockHeight,
|
|
447
|
+
));
|
|
466
448
|
if (cursorData.length !== 0) {
|
|
467
449
|
const cursorStreamCell = streamCellForData(
|
|
468
450
|
cursorBlockHeight,
|
|
@@ -474,21 +456,11 @@ export const makeCosmjsFollower = (
|
|
|
474
456
|
// TODO Long-poll for next block
|
|
475
457
|
// https://github.com/Agoric/agoric-sdk/issues/6154
|
|
476
458
|
await E(leader).jitter(where);
|
|
477
|
-
cursorBlockHeight = await getBlockHeight();
|
|
478
459
|
}
|
|
479
460
|
|
|
480
461
|
// For each subsequent iteration, yield every value that has been
|
|
481
462
|
// published since the last iteration and advance the cursor.
|
|
482
463
|
for (;;) {
|
|
483
|
-
const currentBlockHeight = await getBlockHeight();
|
|
484
|
-
// Wait until the chain has added at least one block.
|
|
485
|
-
if (currentBlockHeight <= cursorBlockHeight) {
|
|
486
|
-
// TODO Long-poll for next block
|
|
487
|
-
// https://github.com/Agoric/agoric-sdk/issues/6154
|
|
488
|
-
await E(leader).jitter(where);
|
|
489
|
-
continue;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
464
|
// Scan backward for all changes since the last observed block and yield
|
|
493
465
|
// them in forward order.
|
|
494
466
|
// Stream cells allow us to skip blocks that did not change.
|
|
@@ -496,24 +468,27 @@ export const makeCosmjsFollower = (
|
|
|
496
468
|
// the value for cells that changed.
|
|
497
469
|
// This does imply accumulating a potentially large number of values if
|
|
498
470
|
// the eachIterable gets sampled infrequently.
|
|
499
|
-
let rightBlockHeight =
|
|
500
|
-
|
|
501
|
-
if (rightData.length === 0) {
|
|
471
|
+
let { value: rightData, height: rightBlockHeight } =
|
|
472
|
+
await getDataAtHeight();
|
|
473
|
+
if (rightBlockHeight <= cursorBlockHeight || rightData.length === 0) {
|
|
502
474
|
// TODO Long-poll for next block
|
|
503
475
|
// https://github.com/Agoric/agoric-sdk/issues/6154
|
|
504
476
|
await E(leader).jitter(where);
|
|
505
477
|
continue;
|
|
506
478
|
}
|
|
479
|
+
|
|
507
480
|
let rightStreamCell = streamCellForData(rightBlockHeight, rightData);
|
|
508
481
|
|
|
509
482
|
// Compare block cell data pairwise (left, right) and accumulate
|
|
510
483
|
// a stack of each cell we encounter.
|
|
511
484
|
const currentData = rightData;
|
|
485
|
+
const currentBlockHeight = rightBlockHeight;
|
|
486
|
+
|
|
512
487
|
const cells = [];
|
|
513
488
|
while (rightBlockHeight > cursorBlockHeight) {
|
|
514
489
|
if (rightStreamCell.blockHeight > rightBlockHeight) {
|
|
515
490
|
const { storeName, storeSubkey } = await castingSpecP;
|
|
516
|
-
throw
|
|
491
|
+
throw Error(
|
|
517
492
|
`Corrupt storage cell for ${storeName} under key ${storeSubkey} at block-height ${rightBlockHeight} claims to being published at a later block height ${rightStreamCell.blockHeight}`,
|
|
518
493
|
);
|
|
519
494
|
}
|
|
@@ -522,7 +497,7 @@ export const makeCosmjsFollower = (
|
|
|
522
497
|
if (leftBlockHeight <= cursorBlockHeight) {
|
|
523
498
|
break;
|
|
524
499
|
}
|
|
525
|
-
const leftData = await getDataAtHeight(leftBlockHeight);
|
|
500
|
+
const leftData = (await getDataAtHeight(leftBlockHeight)).value;
|
|
526
501
|
// Do not scan behind a cell with no data.
|
|
527
502
|
// This should not happen but can be tolerated.
|
|
528
503
|
if (leftData.length === 0) {
|
|
@@ -570,7 +545,7 @@ export const makeCosmjsFollower = (
|
|
|
570
545
|
}
|
|
571
546
|
|
|
572
547
|
/**
|
|
573
|
-
* @param {number} cursorBlockHeight
|
|
548
|
+
* @param {number} [cursorBlockHeight]
|
|
574
549
|
* @yields {ValueFollowerElement<T>}
|
|
575
550
|
*/
|
|
576
551
|
async function* getReverseIterableAtHeight(cursorBlockHeight) {
|
|
@@ -578,8 +553,10 @@ export const makeCosmjsFollower = (
|
|
|
578
553
|
// cursorBlockHeight) so we know not to emit duplicates
|
|
579
554
|
// of that cell.
|
|
580
555
|
let cursorData;
|
|
581
|
-
while (cursorBlockHeight > 0) {
|
|
582
|
-
cursorData = await getDataAtHeight(
|
|
556
|
+
while (cursorBlockHeight === undefined || cursorBlockHeight > 0) {
|
|
557
|
+
({ value: cursorData, height: cursorBlockHeight } = await getDataAtHeight(
|
|
558
|
+
cursorBlockHeight,
|
|
559
|
+
));
|
|
583
560
|
if (cursorData.length === 0) {
|
|
584
561
|
// No data at the cursor height, so signal beginning of stream.
|
|
585
562
|
return;
|
|
@@ -596,15 +573,9 @@ export const makeCosmjsFollower = (
|
|
|
596
573
|
return getLatestIterable();
|
|
597
574
|
},
|
|
598
575
|
async getEachIterable({ height = undefined } = {}) {
|
|
599
|
-
if (height === undefined) {
|
|
600
|
-
height = await getBlockHeight();
|
|
601
|
-
}
|
|
602
576
|
return getEachIterableAtHeight(height);
|
|
603
577
|
},
|
|
604
578
|
async getReverseIterable({ height = undefined } = {}) {
|
|
605
|
-
if (height === undefined) {
|
|
606
|
-
height = await getBlockHeight();
|
|
607
|
-
}
|
|
608
579
|
return getReverseIterableAtHeight(height);
|
|
609
580
|
},
|
|
610
581
|
});
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export function makeFollower<T>(specP: ERef<import('./types').CastingSpec> | string, leaderOrMaker?: import("./types").LeaderOrMaker | undefined, options?: import("./types").FollowerOptions | undefined): Promise<import("./follower-cosmjs.js").ValueFollower<T>>;
|
|
2
|
+
//# sourceMappingURL=follower.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"follower.d.ts","sourceRoot":"","sources":["follower.js"],"names":[],"mappings":"AA4DO,uCALI,KAAK,OAAO,SAAS,EAAE,WAAW,CAAC,GAAG,MAAM,kLAYtD"}
|
package/src/follower.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
// @
|
|
1
|
+
// @jessie-check
|
|
2
2
|
|
|
3
3
|
import { Far } from '@endo/far';
|
|
4
4
|
import {
|
|
5
5
|
mapAsyncIterable,
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
subscribeEach,
|
|
7
|
+
subscribeLatest,
|
|
8
8
|
} from './iterable.js';
|
|
9
9
|
import { makeCosmjsFollower } from './follower-cosmjs.js';
|
|
10
10
|
import { makeCastingSpec } from './casting-spec.js';
|
|
@@ -22,10 +22,10 @@ const makeSubscriptionFollower = spec => {
|
|
|
22
22
|
const { notifier, subscription } = await spec;
|
|
23
23
|
let ai;
|
|
24
24
|
if (notifier) {
|
|
25
|
-
ai =
|
|
25
|
+
ai = subscribeLatest(notifier);
|
|
26
26
|
} else {
|
|
27
27
|
assert(subscription);
|
|
28
|
-
ai =
|
|
28
|
+
ai = subscribeEach(subscription);
|
|
29
29
|
}
|
|
30
30
|
return mapAsyncIterable(ai, transform);
|
|
31
31
|
},
|
|
@@ -34,10 +34,10 @@ const makeSubscriptionFollower = spec => {
|
|
|
34
34
|
const { notifier, subscription } = await spec;
|
|
35
35
|
let ai;
|
|
36
36
|
if (subscription) {
|
|
37
|
-
ai =
|
|
37
|
+
ai = subscribeEach(subscription);
|
|
38
38
|
} else {
|
|
39
39
|
assert(notifier);
|
|
40
|
-
ai =
|
|
40
|
+
ai = subscribeLatest(notifier);
|
|
41
41
|
}
|
|
42
42
|
return mapAsyncIterable(ai, transform);
|
|
43
43
|
},
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function mapAsyncIterable<TIn, TOut>(iterable: AsyncIterable<TIn>, transform: (value: TIn) => TOut): AsyncIterable<TOut>;
|
|
2
|
+
export function iterateLatest<T>(follower: ERef<import("./types").Follower<T>>): {
|
|
3
|
+
/** @returns {AsyncIterator<T>} */
|
|
4
|
+
[Symbol.asyncIterator]: () => AsyncIterator<T, any, undefined>;
|
|
5
|
+
} & import("@endo/eventual-send").RemotableBrand<{}, {
|
|
6
|
+
/** @returns {AsyncIterator<T>} */
|
|
7
|
+
[Symbol.asyncIterator]: () => AsyncIterator<T, any, undefined>;
|
|
8
|
+
}>;
|
|
9
|
+
export function iterateEach<T>(follower: ERef<import("./types").Follower<T>>, options?: import("./types").IterateEachOptions | undefined): {
|
|
10
|
+
/** @returns {AsyncIterator<T>} */
|
|
11
|
+
[Symbol.asyncIterator]: () => AsyncIterator<T, any, undefined>;
|
|
12
|
+
} & import("@endo/eventual-send").RemotableBrand<{}, {
|
|
13
|
+
/** @returns {AsyncIterator<T>} */
|
|
14
|
+
[Symbol.asyncIterator]: () => AsyncIterator<T, any, undefined>;
|
|
15
|
+
}>;
|
|
16
|
+
export function iterateReverse<T>(follower: ERef<import("./types").Follower<T>>, options?: import("./types").IterateEachOptions | undefined): {
|
|
17
|
+
/** @returns {AsyncIterator<T>} */
|
|
18
|
+
[Symbol.asyncIterator]: () => AsyncIterator<T, any, undefined>;
|
|
19
|
+
} & import("@endo/eventual-send").RemotableBrand<{}, {
|
|
20
|
+
/** @returns {AsyncIterator<T>} */
|
|
21
|
+
[Symbol.asyncIterator]: () => AsyncIterator<T, any, undefined>;
|
|
22
|
+
}>;
|
|
23
|
+
export { subscribeEach, subscribeLatest } from "@agoric/notifier/subscribe.js";
|
|
24
|
+
//# sourceMappingURL=iterable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"iterable.d.ts","sourceRoot":"","sources":["iterable.js"],"names":[],"mappings":"AAWO,gIAON;AAQM;IAGH,kCAAkC;;;IAAlC,kCAAkC;;GAQlC;AASG;IAGH,kCAAkC;;;IAAlC,kCAAkC;;GAQlC;AAOG;IAGH,kCAAkC;;;IAAlC,kCAAkC;;GAQlC"}
|
package/src/iterable.js
CHANGED
|
@@ -1,48 +1,6 @@
|
|
|
1
|
-
// @ts-check
|
|
2
1
|
import { E, Far } from '@endo/far';
|
|
3
|
-
import { makeNotifier } from '@agoric/notifier';
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
* @template T
|
|
7
|
-
* @param {ERef<Notifier<T>>} notifier
|
|
8
|
-
* @returns {ConsistentAsyncIterable<T>}
|
|
9
|
-
*/
|
|
10
|
-
export const makeNotifierIterable = notifier =>
|
|
11
|
-
makeNotifier(E(notifier).getSharableNotifierInternals());
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* TODO: Remove this function when we have `makePublisherKit`.
|
|
15
|
-
*
|
|
16
|
-
* @template T
|
|
17
|
-
* @param {ERef<PublicationRecord<T>>} tailP
|
|
18
|
-
* @returns {AsyncIterator<T>}
|
|
19
|
-
*/
|
|
20
|
-
const makeSubscriptionIterator = tailP => {
|
|
21
|
-
// To understand the implementation, start with
|
|
22
|
-
// https://web.archive.org/web/20160404122250/http://wiki.ecmascript.org/doku.php?id=strawman:concurrency#infinite_queue
|
|
23
|
-
return Far('SubscriptionIterator', {
|
|
24
|
-
next: async () => {
|
|
25
|
-
const resultP = E.get(tailP).head;
|
|
26
|
-
tailP = E.get(tailP).tail;
|
|
27
|
-
return resultP;
|
|
28
|
-
},
|
|
29
|
-
});
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* TODO: Remove this function when we have `makePublisherKit`.
|
|
34
|
-
*
|
|
35
|
-
* @template T
|
|
36
|
-
* @param {ERef<Subscription<T>>} subscription
|
|
37
|
-
* @returns {ConsistentAsyncIterable<T>}
|
|
38
|
-
*/
|
|
39
|
-
export const makeSubscriptionIterable = subscription =>
|
|
40
|
-
harden({
|
|
41
|
-
[Symbol.asyncIterator]: () =>
|
|
42
|
-
makeSubscriptionIterator(
|
|
43
|
-
E(subscription).getSharableSubscriptionInternals(),
|
|
44
|
-
),
|
|
45
|
-
});
|
|
3
|
+
export { subscribeEach, subscribeLatest } from '@agoric/notifier/subscribe.js';
|
|
46
4
|
|
|
47
5
|
/**
|
|
48
6
|
* @template TIn
|
|
@@ -61,7 +19,7 @@ export const mapAsyncIterable = (iterable, transform) => {
|
|
|
61
19
|
};
|
|
62
20
|
|
|
63
21
|
/**
|
|
64
|
-
* TODO: Remove this function when we have
|
|
22
|
+
* TODO: Remove this function when we have an @endo/publish-kit that suppports pull topics
|
|
65
23
|
*
|
|
66
24
|
* @template T
|
|
67
25
|
* @param {ERef<import('./types').Follower<T>>} follower
|
|
@@ -80,7 +38,7 @@ export const iterateLatest = follower =>
|
|
|
80
38
|
});
|
|
81
39
|
|
|
82
40
|
/**
|
|
83
|
-
* TODO: Remove this function when we have
|
|
41
|
+
* TODO: Remove this function when we have an @endo/publish-kit that suppports pull topics
|
|
84
42
|
*
|
|
85
43
|
* @template T
|
|
86
44
|
* @param {ERef<import('./types.js').Follower<T>>} follower
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export function makeLeaderFromRpcAddresses(rpcAddrs: string[], leaderOptions?: import("./types.js").LeaderOptions | undefined): import('./types.js').Leader;
|
|
2
|
+
export function makeLeaderFromNetworkConfig(netconfigURL: string, options?: import("./types.js").LeaderOptions | undefined): Promise<any>;
|
|
3
|
+
export function makeLeader(bootstrap?: string | undefined, options?: import("./types.js").LeaderOptions | undefined): ERef<import('./types.js').Leader>;
|
|
4
|
+
//# sourceMappingURL=leader-netconfig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leader-netconfig.d.ts","sourceRoot":"","sources":["leader-netconfig.js"],"names":[],"mappings":"AAgBO,qDAJI,MAAM,EAAE,mEAEN,OAAO,YAAY,EAAE,MAAM,CAevC;AAMM,0DAHI,MAAM,0EAoChB;AAOM,sHAFM,KAAK,OAAO,YAAY,EAAE,MAAM,CAAC,CAO7C"}
|