univo 0.2.18 → 0.2.19
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/dist/cli.mjs +2 -2
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +214 -98
- package/dist/realtime.d.mts +0 -1
- package/dist/realtime.d.ts +0 -1
- package/dist/realtime.mjs +9 -7
- package/dist/rpc.d.mts +249 -3
- package/dist/rpc.d.ts +249 -3
- package/dist/shared/{univo.GuW0GiSl.mjs → univo.D1x0MkrS.mjs} +61 -36
- package/dist/shared/{univo.TYA11b8i.mjs → univo.bwpFiVa7.mjs} +9 -4
- package/dist/transport.d.mts +4 -1
- package/dist/transport.d.ts +4 -1
- package/dist/transport.mjs +2 -2
- package/package.json +1 -1
- package/dist/shared/univo.BNqa7KED.d.mts +0 -219
- package/dist/shared/univo.BNqa7KED.d.ts +0 -219
package/dist/cli.mjs
CHANGED
|
@@ -4,8 +4,8 @@ import open from 'open';
|
|
|
4
4
|
import { config } from 'dotenv';
|
|
5
5
|
import { WebSocket } from 'partysocket';
|
|
6
6
|
import { defineCommand, runMain } from 'citty';
|
|
7
|
-
import { h as http } from './shared/univo.
|
|
8
|
-
import { c as createLogger, v as version, b as name } from './shared/univo.
|
|
7
|
+
import { h as http } from './shared/univo.D1x0MkrS.mjs';
|
|
8
|
+
import { c as createLogger, v as version, b as name } from './shared/univo.bwpFiVa7.mjs';
|
|
9
9
|
|
|
10
10
|
const log = createLogger({ quiet: false });
|
|
11
11
|
const dev = defineCommand({
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { B as Block, E as Event, a as Filter, H as Head, I as Indexer, M as Metadata, R as Result, i as indexer } from './shared/univo.BNqa7KED.mjs';
|
|
2
1
|
import '@storagesdk/core';
|
|
2
|
+
export { B as Block, E as Event, F as Filter, H as Head, I as Indexer, M as Metadata, R as Result, i as indexer } from './rpc.mjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { B as Block, E as Event, a as Filter, H as Head, I as Indexer, M as Metadata, R as Result, i as indexer } from './shared/univo.BNqa7KED.js';
|
|
2
1
|
import '@storagesdk/core';
|
|
2
|
+
export { B as Block, E as Event, F as Filter, H as Head, I as Indexer, M as Metadata, R as Result, i as indexer } from './rpc.js';
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { StorageError } from '@storagesdk/core';
|
|
2
|
-
import { g as getException, c as createException, a as catchException, l as local } from './shared/univo.
|
|
3
|
-
import { c as createLogger, d as decompress, e as decoder, r as retry, v as version, h as hexToNumber, a as isHexEqual, f as normalizeHex, g as compress } from './shared/univo.
|
|
2
|
+
import { g as getException, c as createException, a as catchException, l as local } from './shared/univo.D1x0MkrS.mjs';
|
|
3
|
+
import { c as createLogger, d as decompress, e as decoder, r as retry, v as version, h as hexToNumber, a as isHexEqual, n as numberToHex, f as normalizeHex, g as compress } from './shared/univo.bwpFiVa7.mjs';
|
|
4
4
|
import 'ws';
|
|
5
5
|
import 'partysocket';
|
|
6
6
|
|
|
@@ -136,36 +136,31 @@ function indexer(opts) {
|
|
|
136
136
|
const log = createLogger({ quiet: opts.quiet ?? false, prefix: "[indexer]" });
|
|
137
137
|
const all_events = [];
|
|
138
138
|
const events_grouped_by_storage_map = /* @__PURE__ */ new Map();
|
|
139
|
+
const all_actions = [];
|
|
139
140
|
const metadata = {
|
|
141
|
+
// The blocks store essentially operates as our write-ahead-log (WAL) for unfinalized blocks. Before we
|
|
142
|
+
// commit any events to storage we first record the block input in our metadata layer. This allows our
|
|
143
|
+
// finalization handler to always have access to the input data in the case we need to remove events
|
|
144
|
+
// because of chain reorganisations
|
|
140
145
|
blocks: {
|
|
141
|
-
async
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if (key === void 0) {
|
|
146
|
-
return null;
|
|
146
|
+
async list(chain, number) {
|
|
147
|
+
let prefix = `blocks/v1/${normalizeHex(chain)}`;
|
|
148
|
+
if (typeof number === "string") {
|
|
149
|
+
prefix += `/${normalizeHex(number, 16)}`;
|
|
147
150
|
}
|
|
148
|
-
const [_, __, ___, number, hash] = key.path.split("/");
|
|
149
|
-
return { chain, number, hash };
|
|
150
|
-
},
|
|
151
|
-
async getBlocksByNumber(chain, number) {
|
|
152
|
-
const prefix = `blocks/v1/${normalizeHex(chain)}/${normalizeHex(number, 16)}`;
|
|
153
151
|
const keys = await opts.metadataStorage.list({ prefix });
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const blob = await opts.metadataStorage.download(item.path, { as: "blob" });
|
|
160
|
-
const block = await decompress(blob);
|
|
161
|
-
const parsed = JSON.parse(block);
|
|
162
|
-
return parsed;
|
|
163
|
-
})
|
|
164
|
-
);
|
|
165
|
-
return blocks;
|
|
152
|
+
const mapped = keys.items.map((key) => {
|
|
153
|
+
const [_, __, ___, number2, hash, parent_hash] = key.path.split("/");
|
|
154
|
+
return { chain, number: number2, hash, parent_hash };
|
|
155
|
+
});
|
|
156
|
+
return mapped;
|
|
166
157
|
},
|
|
167
|
-
async
|
|
168
|
-
const
|
|
158
|
+
async get(head) {
|
|
159
|
+
const chain = normalizeHex(head.chain);
|
|
160
|
+
const number = normalizeHex(head.number, 16);
|
|
161
|
+
const hash = normalizeHex(head.hash);
|
|
162
|
+
const parentHash = normalizeHex(head.parent_hash);
|
|
163
|
+
const prefix = `blocks/v1/${chain}/${number}/${hash}/${parentHash}`;
|
|
169
164
|
const blob = await opts.metadataStorage.download(prefix, { as: "blob" }).catch((error) => {
|
|
170
165
|
if (error instanceof StorageError) {
|
|
171
166
|
if (error.code === "NotFound") {
|
|
@@ -188,9 +183,10 @@ function indexer(opts) {
|
|
|
188
183
|
await Promise.all(
|
|
189
184
|
blocks.map(async (block) => {
|
|
190
185
|
const chain = normalizeHex(block.eth_chainId);
|
|
191
|
-
const hash = normalizeHex(block.eth_getBlockByNumber.hash);
|
|
192
186
|
const number = normalizeHex(block.eth_getBlockByNumber.number, 16);
|
|
193
|
-
const
|
|
187
|
+
const hash = normalizeHex(block.eth_getBlockByNumber.hash);
|
|
188
|
+
const parentHash = normalizeHex(block.eth_getBlockByNumber.parentHash);
|
|
189
|
+
const prefix = `blocks/v1/${chain}/${number}/${hash}/${parentHash}`;
|
|
194
190
|
const compressed = await compress(JSON.stringify(block));
|
|
195
191
|
await opts.metadataStorage.upload(prefix, compressed);
|
|
196
192
|
})
|
|
@@ -202,14 +198,75 @@ function indexer(opts) {
|
|
|
202
198
|
}
|
|
203
199
|
await Promise.all(
|
|
204
200
|
blocks.map(async (block) => {
|
|
205
|
-
const chain = normalizeHex(block.
|
|
206
|
-
const
|
|
207
|
-
const
|
|
208
|
-
const
|
|
201
|
+
const chain = normalizeHex(block.chain);
|
|
202
|
+
const number = normalizeHex(block.number, 16);
|
|
203
|
+
const hash = normalizeHex(block.hash);
|
|
204
|
+
const parentHash = normalizeHex(block.parent_hash);
|
|
205
|
+
const prefix = `blocks/v1/${chain}/${number}/${hash}/${parentHash}`;
|
|
206
|
+
await opts.metadataStorage.delete(prefix);
|
|
207
|
+
})
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
// When unfinalized events are upserted we commit a given block number and block hash to indicate
|
|
212
|
+
// that those events were successfully recorded. This enables an optimisation in our finalized
|
|
213
|
+
// handler that allows it to skip processing blocks that were successfully processed
|
|
214
|
+
commits: {
|
|
215
|
+
async list(chain, number) {
|
|
216
|
+
let prefix = `commits/v1/${normalizeHex(chain)}`;
|
|
217
|
+
if (typeof number === "string") {
|
|
218
|
+
prefix += `/${normalizeHex(number, 16)}`;
|
|
219
|
+
}
|
|
220
|
+
const keys = await opts.metadataStorage.list({ prefix });
|
|
221
|
+
const mapped = keys.items.map((key) => {
|
|
222
|
+
const [_, __, ___, number2, hash, parent_hash] = key.path.split("/");
|
|
223
|
+
return { chain, number: number2, hash, parent_hash };
|
|
224
|
+
});
|
|
225
|
+
return mapped;
|
|
226
|
+
},
|
|
227
|
+
async upsert(head) {
|
|
228
|
+
const chain = normalizeHex(head.chain);
|
|
229
|
+
const number = normalizeHex(head.number, 16);
|
|
230
|
+
const hash = normalizeHex(head.hash);
|
|
231
|
+
const parentHash = normalizeHex(head.parent_hash);
|
|
232
|
+
const prefix = `commits/v1/${chain}/${number}/${hash}/${parentHash}`;
|
|
233
|
+
const body = JSON.stringify({ hello: "world" });
|
|
234
|
+
await opts.metadataStorage.upload(prefix, body);
|
|
235
|
+
},
|
|
236
|
+
async delete(commits) {
|
|
237
|
+
await Promise.all(
|
|
238
|
+
commits.map(async (commit) => {
|
|
239
|
+
const chain = normalizeHex(commit.chain);
|
|
240
|
+
const number = normalizeHex(commit.number, 16);
|
|
241
|
+
const hash = normalizeHex(commit.hash);
|
|
242
|
+
const parentHash = normalizeHex(commit.parent_hash);
|
|
243
|
+
const prefix = `commits/v1/${chain}/${number}/${hash}/${parentHash}`;
|
|
209
244
|
await opts.metadataStorage.delete(prefix);
|
|
210
245
|
})
|
|
211
246
|
);
|
|
212
247
|
}
|
|
248
|
+
},
|
|
249
|
+
// Our indexer always lags behind the canonical chain. The heights table is used as a durable record
|
|
250
|
+
// to track its own finalized height. As the chain finalizes, we process newly finalized blocks and
|
|
251
|
+
// then update the heights here to reflect to that outside world what height the indexer has currently
|
|
252
|
+
// finalized up to. This information is stored in a separate table so that we can `list` keys and
|
|
253
|
+
// immediately know which blocks were finalized correctly without ever having to load the entire block
|
|
254
|
+
// values themselves reducing memory usage and increasing finalizing throughput
|
|
255
|
+
heights: {
|
|
256
|
+
async list(chain) {
|
|
257
|
+
const prefix = `heights/v1/${normalizeHex(chain)}`;
|
|
258
|
+
const keys = await opts.metadataStorage.list({ prefix });
|
|
259
|
+
const mapped = keys.items.map((key) => {
|
|
260
|
+
const [_, __, ___, number] = key.path.split("/");
|
|
261
|
+
return { chain, number };
|
|
262
|
+
});
|
|
263
|
+
return mapped;
|
|
264
|
+
},
|
|
265
|
+
async upsert(chain, number) {
|
|
266
|
+
const prefix = `heights/v1/${normalizeHex(chain)}/${normalizeHex(number, 16)}`;
|
|
267
|
+
const body = JSON.stringify({ hello: "world" });
|
|
268
|
+
await opts.metadataStorage.upload(prefix, body);
|
|
269
|
+
}
|
|
213
270
|
}
|
|
214
271
|
};
|
|
215
272
|
async function getBlock(head) {
|
|
@@ -237,21 +294,18 @@ function indexer(opts) {
|
|
|
237
294
|
}
|
|
238
295
|
const GetBlockError = createException("Failed to load block from the provided `getBlock` function");
|
|
239
296
|
const public_getFinalizedHeight = async (chain) => {
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const finalizedHeight = hexToNumber(finalizedBlock.eth_getBlockByNumber.number);
|
|
250
|
-
if (stored === null) {
|
|
251
|
-
return finalizedHeight;
|
|
297
|
+
const heights = await metadata.heights.list(chain);
|
|
298
|
+
if (heights.length === 0) {
|
|
299
|
+
const block = await getBlock({ chain, number: "finalized" });
|
|
300
|
+
if (block === null) {
|
|
301
|
+
log.debug("Failed to fetch finalized block and unable to determine finalized height, aborting...");
|
|
302
|
+
throw new Error(GetBlockError);
|
|
303
|
+
}
|
|
304
|
+
await metadata.heights.upsert(chain, block.eth_getBlockByNumber.number);
|
|
305
|
+
return hexToNumber(block.eth_getBlockByNumber.number);
|
|
252
306
|
}
|
|
253
|
-
const
|
|
254
|
-
return
|
|
307
|
+
const height = Math.max(...heights.map((height2) => hexToNumber(height2.number)));
|
|
308
|
+
return height;
|
|
255
309
|
};
|
|
256
310
|
const public_writeUnfinalizedHead = async (head) => {
|
|
257
311
|
log.debug("Received unfinalized head...");
|
|
@@ -276,7 +330,7 @@ function indexer(opts) {
|
|
|
276
330
|
await metadata.blocks.upsert([block]);
|
|
277
331
|
log.debug(`Persisted block to metadata in ${Date.now() - metadata_start}ms`);
|
|
278
332
|
const events_start = Date.now();
|
|
279
|
-
const
|
|
333
|
+
const events = events_grouped_by_storage_map.entries().map(async ([storage, grouped_events]) => {
|
|
280
334
|
const batch = [];
|
|
281
335
|
for (const event2 of grouped_events) {
|
|
282
336
|
try {
|
|
@@ -284,8 +338,8 @@ function indexer(opts) {
|
|
|
284
338
|
log.debug(`Block matches no filters for event ${event2.id}`);
|
|
285
339
|
continue;
|
|
286
340
|
}
|
|
287
|
-
const
|
|
288
|
-
for (const event3 of
|
|
341
|
+
const events2 = event2.handler(block);
|
|
342
|
+
for (const event3 of events2) {
|
|
289
343
|
batch.push(event3);
|
|
290
344
|
}
|
|
291
345
|
} catch (error) {
|
|
@@ -306,14 +360,28 @@ function indexer(opts) {
|
|
|
306
360
|
}
|
|
307
361
|
}
|
|
308
362
|
});
|
|
309
|
-
await Promise.all(
|
|
310
|
-
log.debug(`Wrote
|
|
363
|
+
await Promise.all(events);
|
|
364
|
+
log.debug(`Wrote events in ${Date.now() - events_start}ms`);
|
|
365
|
+
await metadata.commits.upsert(head);
|
|
366
|
+
const actions = all_actions.flatMap((action2) => {
|
|
367
|
+
if (typeof action2.handler.latest === "undefined") {
|
|
368
|
+
return [];
|
|
369
|
+
}
|
|
370
|
+
return action2.event.handler(block).map(async (event2) => {
|
|
371
|
+
await retry(() => action2.handler.latest(event2), 2).catch((error) => {
|
|
372
|
+
log.error(`Failed to execute 'latest' action ${action2.id}`);
|
|
373
|
+
throw error;
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
await Promise.allSettled(actions);
|
|
311
378
|
};
|
|
312
379
|
const public_deleteReorganisedHead = async (head) => {
|
|
313
380
|
log.debug(`Received reorganised head ${hexToNumber(head.number)}`);
|
|
314
|
-
const [
|
|
315
|
-
|
|
316
|
-
|
|
381
|
+
const [stored_block, canonical_block] = await Promise.all([
|
|
382
|
+
metadata.blocks.get(head),
|
|
383
|
+
//
|
|
384
|
+
getBlock({ chain: head.chain, number: head.number })
|
|
317
385
|
]);
|
|
318
386
|
if (stored_block === null) {
|
|
319
387
|
return log.debug("Reorganised block never/already processed");
|
|
@@ -373,7 +441,7 @@ function indexer(opts) {
|
|
|
373
441
|
};
|
|
374
442
|
const public_writeFinalizedHeads = async (heads) => {
|
|
375
443
|
if (heads.length === 0) {
|
|
376
|
-
|
|
444
|
+
throw new Error("No heads received, aborting...");
|
|
377
445
|
}
|
|
378
446
|
log.debug(`Received ${heads.length} finalized heads...`);
|
|
379
447
|
let chain = void 0;
|
|
@@ -402,8 +470,8 @@ function indexer(opts) {
|
|
|
402
470
|
}
|
|
403
471
|
previousHead = head;
|
|
404
472
|
}
|
|
405
|
-
const [
|
|
406
|
-
metadata.
|
|
473
|
+
const [heights, finalizedBlock] = await Promise.all([
|
|
474
|
+
metadata.heights.list(chain),
|
|
407
475
|
//
|
|
408
476
|
getBlock({ chain, number: "finalized" })
|
|
409
477
|
]);
|
|
@@ -411,56 +479,82 @@ function indexer(opts) {
|
|
|
411
479
|
log.debug("Failed to load chain finalized block. Request should be retried");
|
|
412
480
|
throw new Error(GetBlockError);
|
|
413
481
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
throw new Error("Received head greater than chain finalized height");
|
|
418
|
-
}
|
|
482
|
+
if (heights.length === 0) {
|
|
483
|
+
await metadata.heights.upsert(chain, finalizedBlock.eth_getBlockByNumber.number);
|
|
484
|
+
heights.push({ chain, number: finalizedBlock.eth_getBlockByNumber.number });
|
|
419
485
|
}
|
|
420
|
-
|
|
421
|
-
|
|
486
|
+
const indexerHeight = Math.max(...heights.map((height) => hexToNumber(height.number)));
|
|
487
|
+
const finalizedHeight = hexToNumber(finalizedBlock.eth_getBlockByNumber.number);
|
|
488
|
+
const lastHead = heads[heads.length - 1];
|
|
489
|
+
if (lastHead === void 0 || hexToNumber(lastHead.number) !== finalizedHeight) {
|
|
490
|
+
throw new Error("Client has stalled. Chain has finalized greater than the heads received");
|
|
422
491
|
}
|
|
423
|
-
const indexerHeight = hexToNumber(unfinalizedMetadataBlock.number) - 1;
|
|
424
492
|
log.debug(`Indexer height ${indexerHeight}, finalized height ${finalizedHeight}`);
|
|
425
493
|
if (indexerHeight === finalizedHeight) {
|
|
426
|
-
|
|
494
|
+
log.debug("No heads to finalize, returning...");
|
|
495
|
+
return await cleanupFinalizedHeads(chain, numberToHex(finalizedHeight));
|
|
427
496
|
}
|
|
428
497
|
const newHeads = heads.filter((head) => {
|
|
429
498
|
return hexToNumber(head.number) > indexerHeight;
|
|
430
499
|
});
|
|
431
500
|
const [firstNewHead] = newHeads;
|
|
432
501
|
if (firstNewHead === void 0) {
|
|
433
|
-
|
|
502
|
+
throw new Error("Internal error. Expected at least one head greater than the indexer height.");
|
|
434
503
|
}
|
|
435
504
|
if (hexToNumber(firstNewHead.number) !== indexerHeight + 1) {
|
|
436
|
-
throw new Error(
|
|
437
|
-
`Client advanced too far (${hexToNumber(firstNewHead.number)}). Heads received are greater than the first new head ${indexerHeight + 1}`
|
|
438
|
-
);
|
|
505
|
+
throw new Error("Client advanced too far. Heads received are greater than the first new head");
|
|
439
506
|
}
|
|
440
507
|
for (const head of newHeads) {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
508
|
+
await Promise.all([
|
|
509
|
+
processEventsForCanonicalHead(head),
|
|
510
|
+
processActionsForCanonicalHead(head)
|
|
511
|
+
//
|
|
512
|
+
]);
|
|
513
|
+
await metadata.heights.upsert(head.chain, head.number);
|
|
514
|
+
}
|
|
515
|
+
await cleanupFinalizedHeads(chain, numberToHex(finalizedHeight));
|
|
516
|
+
};
|
|
517
|
+
async function processActionsForCanonicalHead(head) {
|
|
518
|
+
if (all_actions.every((action2) => typeof action2.handler.finalized === "undefined")) {
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
const block = await getBlock({ chain: head.chain, number: head.number, hash: head.hash });
|
|
522
|
+
const promises = all_actions.flatMap((action2) => {
|
|
523
|
+
if (typeof action2.handler.finalized === "undefined") {
|
|
524
|
+
return [];
|
|
445
525
|
}
|
|
446
|
-
|
|
447
|
-
|
|
526
|
+
return action2.event.handler(block).map(async (event2) => {
|
|
527
|
+
await retry(() => action2.handler.finalized(event2), 2).catch((error) => {
|
|
528
|
+
log.error(`Failed to execute 'finalized' action ${action2.id}`);
|
|
529
|
+
throw error;
|
|
530
|
+
});
|
|
448
531
|
});
|
|
449
|
-
|
|
450
|
-
|
|
532
|
+
});
|
|
533
|
+
await Promise.all(promises);
|
|
534
|
+
}
|
|
535
|
+
async function processEventsForCanonicalHead(head) {
|
|
536
|
+
const [processed, commits] = await Promise.all([
|
|
537
|
+
metadata.blocks.list(head.chain, head.number),
|
|
538
|
+
//
|
|
539
|
+
metadata.commits.list(head.chain, head.number)
|
|
540
|
+
]);
|
|
541
|
+
if (processed.length === 1) {
|
|
542
|
+
if (commits.some((commit) => isHexEqual(head.hash, commit.hash) && isHexEqual(head.parent_hash, commit.parent_hash))) {
|
|
543
|
+
return;
|
|
451
544
|
}
|
|
452
|
-
const block = processed.find((block2) => {
|
|
453
|
-
return isHexEqual(head.hash, block2.eth_getBlockByNumber.hash);
|
|
454
|
-
});
|
|
455
|
-
await writeFinalizedHead(head, block ?? null);
|
|
456
|
-
await metadata.blocks.delete(processed);
|
|
457
|
-
}
|
|
458
|
-
};
|
|
459
|
-
async function writeFinalizedHead(head, block_nullable) {
|
|
460
|
-
let block = block_nullable;
|
|
461
|
-
if (block === null) {
|
|
462
|
-
block = await getBlock({ chain: head.chain, number: head.number, hash: head.hash });
|
|
463
545
|
}
|
|
546
|
+
const reorganised = processed.filter((block) => {
|
|
547
|
+
return !isHexEqual(head.hash, block.hash);
|
|
548
|
+
});
|
|
549
|
+
await Promise.all(
|
|
550
|
+
reorganised.map(async (block) => {
|
|
551
|
+
await deleteReorganisedHead(block);
|
|
552
|
+
})
|
|
553
|
+
);
|
|
554
|
+
await writeFinalizedHead(head);
|
|
555
|
+
}
|
|
556
|
+
async function writeFinalizedHead(head) {
|
|
557
|
+
const block = await getBlock({ chain: head.chain, number: head.number, hash: head.hash });
|
|
464
558
|
if (block === null) {
|
|
465
559
|
throw new Error("Received unknown head that wasn't canonical");
|
|
466
560
|
}
|
|
@@ -496,18 +590,17 @@ function indexer(opts) {
|
|
|
496
590
|
});
|
|
497
591
|
await Promise.all(promises);
|
|
498
592
|
}
|
|
499
|
-
async function
|
|
500
|
-
|
|
501
|
-
|
|
593
|
+
async function deleteReorganisedHead(head) {
|
|
594
|
+
const block = await metadata.blocks.get(head);
|
|
595
|
+
if (block === null) {
|
|
596
|
+
throw new Error("Expected reorganised block to exist in metadata");
|
|
502
597
|
}
|
|
503
598
|
const promises = all_events.map(async (event2) => {
|
|
504
599
|
const batch = [];
|
|
505
600
|
try {
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
batch.push(event3);
|
|
510
|
-
}
|
|
601
|
+
const events = event2.handler(block);
|
|
602
|
+
for (const event3 of events) {
|
|
603
|
+
batch.push(event3);
|
|
511
604
|
}
|
|
512
605
|
} catch (error) {
|
|
513
606
|
log.error(`Failed to run your 'handler' for event ${event2.id}`);
|
|
@@ -523,6 +616,24 @@ function indexer(opts) {
|
|
|
523
616
|
});
|
|
524
617
|
await Promise.all(promises);
|
|
525
618
|
}
|
|
619
|
+
async function cleanupFinalizedHeads(chain, finalized) {
|
|
620
|
+
const [_blocks, _commits] = await Promise.all([
|
|
621
|
+
metadata.blocks.list(chain),
|
|
622
|
+
//
|
|
623
|
+
metadata.commits.list(chain)
|
|
624
|
+
]);
|
|
625
|
+
const blocks = _blocks.filter((block) => {
|
|
626
|
+
return hexToNumber(block.number) <= hexToNumber(finalized);
|
|
627
|
+
});
|
|
628
|
+
const commits = _commits.filter((commit) => {
|
|
629
|
+
return hexToNumber(commit.number) < hexToNumber(finalized);
|
|
630
|
+
});
|
|
631
|
+
await Promise.all([
|
|
632
|
+
metadata.blocks.delete(blocks),
|
|
633
|
+
//
|
|
634
|
+
metadata.commits.delete(commits)
|
|
635
|
+
]);
|
|
636
|
+
}
|
|
526
637
|
const private_getMetadata = async () => {
|
|
527
638
|
return {
|
|
528
639
|
version,
|
|
@@ -795,6 +906,10 @@ function indexer(opts) {
|
|
|
795
906
|
events_grouped_by_storage_map.set(event2.storage, group);
|
|
796
907
|
return event2;
|
|
797
908
|
};
|
|
909
|
+
const action = (action2) => {
|
|
910
|
+
all_actions.push(action2);
|
|
911
|
+
return action2;
|
|
912
|
+
};
|
|
798
913
|
const rpc = {
|
|
799
914
|
request: {
|
|
800
915
|
public_getFinalizedHeight,
|
|
@@ -816,6 +931,7 @@ function indexer(opts) {
|
|
|
816
931
|
const indexer2 = {
|
|
817
932
|
...rpc,
|
|
818
933
|
event,
|
|
934
|
+
action,
|
|
819
935
|
fetch: server.http
|
|
820
936
|
};
|
|
821
937
|
return indexer2;
|
package/dist/realtime.d.mts
CHANGED
package/dist/realtime.d.ts
CHANGED
package/dist/realtime.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as createLogger, i as iife, h as hexToNumber, n as numberToHex, m as mutex, r as retry, a as isHexEqual } from './shared/univo.
|
|
1
|
+
import { c as createLogger, i as iife, h as hexToNumber, n as numberToHex, m as mutex, r as retry, a as isHexEqual } from './shared/univo.bwpFiVa7.mjs';
|
|
2
2
|
|
|
3
3
|
function defineBlockchain(opts) {
|
|
4
4
|
const log = createLogger({ quiet: opts.quiet });
|
|
@@ -165,7 +165,7 @@ function realtime(opts) {
|
|
|
165
165
|
});
|
|
166
166
|
log.debug("Subscribed indexer chain to new heads");
|
|
167
167
|
let indexerHeight = initialIndexerHeight;
|
|
168
|
-
|
|
168
|
+
async function poll() {
|
|
169
169
|
try {
|
|
170
170
|
log.debug("Polling for finalized height...");
|
|
171
171
|
const finalizedBlock = await opts.node.request({
|
|
@@ -189,7 +189,11 @@ function realtime(opts) {
|
|
|
189
189
|
return log.debug("No new finalized heads to deliver, ignoring...");
|
|
190
190
|
}
|
|
191
191
|
log.debug(`Delivering ${newHeads.length} finalized head(s)...`);
|
|
192
|
-
await opts.indexer.request({
|
|
192
|
+
await opts.indexer.request({
|
|
193
|
+
params: [newHeads],
|
|
194
|
+
method: "public_writeFinalizedHeads",
|
|
195
|
+
signal: AbortSignal.timeout(Number.POSITIVE_INFINITY)
|
|
196
|
+
});
|
|
193
197
|
log.debug(`Delivered ${newHeads.length} finalized head(s)`);
|
|
194
198
|
indexerHeight = hexToNumber(finalizedBlock.number);
|
|
195
199
|
log.debug(`Updated indexer height to ${indexerHeight}`);
|
|
@@ -207,11 +211,9 @@ function realtime(opts) {
|
|
|
207
211
|
if (error instanceof Error) {
|
|
208
212
|
log.error(`Failed to reconcile finalized head: ${error.message}`);
|
|
209
213
|
}
|
|
210
|
-
} finally {
|
|
211
|
-
setTimeout(poll, POLLING_INTERVAL_MS);
|
|
212
214
|
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
+
}
|
|
216
|
+
setInterval(mutex(poll), POLLING_INTERVAL_MS);
|
|
215
217
|
log.debug("Started polling finalized height");
|
|
216
218
|
});
|
|
217
219
|
promise.catch((error) => {
|