univo 0.2.15 → 0.2.16
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 +31 -23
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +168 -98
- package/dist/realtime.d.mts +1 -1
- package/dist/realtime.d.ts +1 -1
- package/dist/realtime.mjs +32 -17
- package/dist/rpc.d.mts +5 -1
- package/dist/rpc.d.ts +5 -1
- package/dist/shared/{univo.BgwFxZpJ.mjs → univo.BVikUbk_.mjs} +2 -2
- package/dist/shared/{univo.O0SoVQWh.mjs → univo.CkhykU8v.mjs} +1 -1
- package/dist/shared/{univo.CjvLVVxo.d.mts → univo.DdHfpTWq.d.mts} +2 -2
- package/dist/shared/{univo.CjvLVVxo.d.ts → univo.DdHfpTWq.d.ts} +2 -2
- package/dist/transport.d.mts +1 -1
- package/dist/transport.d.ts +1 -1
- package/dist/transport.mjs +2 -2
- package/package.json +1 -1
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,
|
|
7
|
+
import { h as http } from './shared/univo.CkhykU8v.mjs';
|
|
8
|
+
import { c as createLogger, v as version, b as name } from './shared/univo.BVikUbk_.mjs';
|
|
9
9
|
|
|
10
10
|
const log = createLogger({ quiet: false });
|
|
11
11
|
const dev = defineCommand({
|
|
@@ -18,39 +18,47 @@ const dev = defineCommand({
|
|
|
18
18
|
},
|
|
19
19
|
async run(ctx) {
|
|
20
20
|
try {
|
|
21
|
-
if (!ctx.args.url.startsWith("http://"))
|
|
22
|
-
|
|
21
|
+
if (!ctx.args.url.startsWith("http://")) {
|
|
22
|
+
throw new Error("`url` must start with http://");
|
|
23
|
+
}
|
|
24
|
+
config({ quiet: true });
|
|
23
25
|
const localSigningKey = process.env.UNIVO_SIGNING_KEY;
|
|
24
|
-
if (localSigningKey === void 0)
|
|
26
|
+
if (localSigningKey === void 0) {
|
|
27
|
+
throw new Error("Please provide a UNIVO_SIGNING_KEY environment variable");
|
|
28
|
+
}
|
|
25
29
|
const client = http(ctx.args.url, { signingKey: localSigningKey });
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
]);
|
|
30
|
+
await client.request({ method: "private_getMetadata", params: [] }).catch(() => {
|
|
31
|
+
throw new Error(`Failed to connect to ${ctx.args.url}. Check that your server is running on the correct port.`);
|
|
32
|
+
});
|
|
30
33
|
const remoteSigningKey = crypto.randomUUID();
|
|
31
34
|
const url = `https://tunnel-univo.app/v1/${crypto.randomUUID()}`;
|
|
32
35
|
const endpoint = {
|
|
33
36
|
url,
|
|
34
|
-
events,
|
|
35
|
-
metadata,
|
|
36
37
|
signing_key: remoteSigningKey
|
|
37
38
|
};
|
|
38
|
-
const
|
|
39
|
-
|
|
39
|
+
const encoder = new TextEncoder();
|
|
40
|
+
const data = encoder.encode(JSON.stringify(endpoint));
|
|
41
|
+
const encoded = data.toBase64({ alphabet: "base64url", omitPadding: true });
|
|
42
|
+
await open(`https://univo.app/dev/${encoded}`);
|
|
40
43
|
const ws$1 = new WebSocket(url, [], { WebSocket: ws });
|
|
41
44
|
ws$1.binaryType = "arraybuffer";
|
|
42
|
-
ws$1.onerror = (event) =>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
ws$1.onerror = (event) => {
|
|
46
|
+
log.error(event.message);
|
|
47
|
+
};
|
|
48
|
+
ws$1.onopen = () => {
|
|
49
|
+
log.info(`Dev server started for ${ctx.args.url}`);
|
|
50
|
+
};
|
|
51
|
+
ws$1.onclose = () => {
|
|
52
|
+
log.info(`Dev server closed for ${ctx.args.url}`);
|
|
53
|
+
};
|
|
45
54
|
ws$1.onmessage = async (event) => {
|
|
46
55
|
try {
|
|
47
|
-
const
|
|
48
|
-
log.
|
|
49
|
-
const
|
|
50
|
-
ws$1.send(JSON.stringify(
|
|
51
|
-
} catch
|
|
52
|
-
|
|
53
|
-
ws$1.send(JSON.stringify(response));
|
|
56
|
+
const json = JSON.parse(event.data);
|
|
57
|
+
log.debug(`Received request (${json.id}) for method '${json.method}'`);
|
|
58
|
+
const result = await client.request({ method: json.method, params: json.params });
|
|
59
|
+
ws$1.send(JSON.stringify({ jsonrpc: "2.0", id: json.id, result }));
|
|
60
|
+
} catch {
|
|
61
|
+
ws$1.send(JSON.stringify({ jsonrpc: "2.0", id: null, error: { code: 0, message: "Invalid request" } }));
|
|
54
62
|
}
|
|
55
63
|
};
|
|
56
64
|
} catch (error) {
|
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
|
|
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.DdHfpTWq.mjs';
|
|
2
2
|
import '@storagesdk/core';
|
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
|
|
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.DdHfpTWq.js';
|
|
2
2
|
import '@storagesdk/core';
|
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,98 @@
|
|
|
1
1
|
import { StorageError } from '@storagesdk/core';
|
|
2
|
-
import { c as createException, a as catchException, l as local
|
|
3
|
-
import { c as createLogger,
|
|
2
|
+
import { g as getException, c as createException, a as catchException, l as local } from './shared/univo.CkhykU8v.mjs';
|
|
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.BVikUbk_.mjs';
|
|
4
4
|
import 'ws';
|
|
5
5
|
import 'partysocket';
|
|
6
6
|
|
|
7
|
+
function createServer(opts) {
|
|
8
|
+
const log = createLogger({ quiet: opts.quiet, prefix: "[server]" });
|
|
9
|
+
const http = async (req) => {
|
|
10
|
+
let body_buffer;
|
|
11
|
+
try {
|
|
12
|
+
body_buffer = await req.arrayBuffer();
|
|
13
|
+
} catch {
|
|
14
|
+
return Response.json(
|
|
15
|
+
{ jsonrpc: "2.0", id: null, error: { code: 0, message: "Invalid request body" } },
|
|
16
|
+
//
|
|
17
|
+
{ status: 400 }
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
const authorization = req.headers.get("Authorization");
|
|
21
|
+
const authenticated = authorization === `Bearer ${opts.signingKey}`;
|
|
22
|
+
let body_string;
|
|
23
|
+
if (req.headers.get("Content-Encoding") === "gzip") {
|
|
24
|
+
if (!authorization) {
|
|
25
|
+
return Response.json(
|
|
26
|
+
{ jsonrpc: "2.0", id: null, error: { code: 0, message: "No bearer token provided" } },
|
|
27
|
+
//
|
|
28
|
+
{ status: 400 }
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
if (!authenticated) {
|
|
32
|
+
return Response.json(
|
|
33
|
+
{ jsonrpc: "2.0", id: null, error: { code: 0, message: "Invalid bearer token" } },
|
|
34
|
+
//
|
|
35
|
+
{ status: 400 }
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
body_string = await decompress(body_buffer);
|
|
39
|
+
} else {
|
|
40
|
+
body_string = decoder.decode(body_buffer);
|
|
41
|
+
}
|
|
42
|
+
let json;
|
|
43
|
+
try {
|
|
44
|
+
json = JSON.parse(body_string);
|
|
45
|
+
} catch {
|
|
46
|
+
return Response.json(
|
|
47
|
+
{ jsonrpc: "2.0", id: null, error: { code: 0, message: "Malformed JSON request body" } },
|
|
48
|
+
//
|
|
49
|
+
{ status: 400 }
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
if (json.method.startsWith("private_")) {
|
|
53
|
+
if (!authorization) {
|
|
54
|
+
return Response.json(
|
|
55
|
+
{ jsonrpc: "2.0", id: json.id, error: { code: 0, message: "No bearer token provided" } },
|
|
56
|
+
//
|
|
57
|
+
{ status: 400 }
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
if (authorization !== `Bearer ${opts.signingKey}`) {
|
|
61
|
+
return Response.json(
|
|
62
|
+
{ jsonrpc: "2.0", id: json.id, error: { code: 0, message: "Invalid bearer token" } },
|
|
63
|
+
//
|
|
64
|
+
{ status: 400 }
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
const result = await opts.transport.request({ method: json.method, params: json.params });
|
|
70
|
+
return Response.json({ jsonrpc: "2.0", id: json.id, result });
|
|
71
|
+
} catch (error) {
|
|
72
|
+
const message = getException(error);
|
|
73
|
+
if (message) {
|
|
74
|
+
log.error(message);
|
|
75
|
+
return Response.json(
|
|
76
|
+
{ jsonrpc: "2.0", id: json.id, error: { code: 0, message } },
|
|
77
|
+
//
|
|
78
|
+
{ status: 400 }
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
if (error instanceof Error) {
|
|
82
|
+
log.error(error.message);
|
|
83
|
+
}
|
|
84
|
+
return Response.json(
|
|
85
|
+
{ jsonrpc: "2.0", id: json.id, error: { code: 0, message: "Internal server error" } },
|
|
86
|
+
//
|
|
87
|
+
{ status: 500 }
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
return {
|
|
92
|
+
http
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
7
96
|
function matchFilter(block, filter) {
|
|
8
97
|
if (!chainValid(block, filter)) return false;
|
|
9
98
|
if (!toBlockValid(block, filter)) return false;
|
|
@@ -164,6 +253,62 @@ function indexer(opts) {
|
|
|
164
253
|
const indexerHeight = hexToNumber(stored.number) - 1;
|
|
165
254
|
return Math.min(indexerHeight, finalizedHeight);
|
|
166
255
|
};
|
|
256
|
+
const public_writeUnfinalizedHead = async (head) => {
|
|
257
|
+
log.debug("Received unfinalized head...");
|
|
258
|
+
const blocks_start = Date.now();
|
|
259
|
+
const [finalizedBlock, block] = await Promise.all([
|
|
260
|
+
getBlock({ chain: head.chain, number: "finalized" }),
|
|
261
|
+
getBlock({ chain: head.chain, number: head.number, hash: head.hash })
|
|
262
|
+
]);
|
|
263
|
+
if (block === null) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
if (finalizedBlock === null) {
|
|
267
|
+
log.debug("Failed to determine finalized height when processing unfinalized heads");
|
|
268
|
+
throw new Error(GetBlockError);
|
|
269
|
+
}
|
|
270
|
+
const finalizedHeight = hexToNumber(finalizedBlock.eth_getBlockByNumber.number);
|
|
271
|
+
if (hexToNumber(head.number) <= finalizedHeight) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
log.debug(`Loaded block in ${Date.now() - blocks_start}ms`);
|
|
275
|
+
const metadata_start = Date.now();
|
|
276
|
+
await metadata.blocks.upsert([block]);
|
|
277
|
+
log.debug(`Persisted block to metadata in ${Date.now() - metadata_start}ms`);
|
|
278
|
+
const events_start = Date.now();
|
|
279
|
+
const promises = events_grouped_by_storage_map.entries().map(async ([storage, grouped_events]) => {
|
|
280
|
+
const batch = [];
|
|
281
|
+
for (const event2 of grouped_events) {
|
|
282
|
+
try {
|
|
283
|
+
if (!event2.filters.some((filter) => matchFilter(block, filter))) {
|
|
284
|
+
log.debug(`Block matches no filters for event ${event2.id}`);
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
const events = event2.handler(block);
|
|
288
|
+
for (const event3 of events) {
|
|
289
|
+
batch.push(event3);
|
|
290
|
+
}
|
|
291
|
+
} catch (error) {
|
|
292
|
+
log.error(`Failed to run your 'handler' for event ${event2.id}`);
|
|
293
|
+
throw error;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (batch.length > 0) {
|
|
297
|
+
const start = Date.now();
|
|
298
|
+
await retry(() => storage.upsert(batch), 2).catch((error) => {
|
|
299
|
+
for (const event2 of grouped_events) {
|
|
300
|
+
log.error(`Failed to run your 'upsert' handler for event ${event2.id}`);
|
|
301
|
+
}
|
|
302
|
+
throw error;
|
|
303
|
+
});
|
|
304
|
+
for (const event2 of grouped_events) {
|
|
305
|
+
log.debug(`Recorded ${batch.length} ${event2.id} in ${Date.now() - start}ms`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
await Promise.all(promises);
|
|
310
|
+
log.debug(`Wrote head in ${Date.now() - events_start}ms`);
|
|
311
|
+
};
|
|
167
312
|
const public_writeUnfinalizedHeads = async (heads) => {
|
|
168
313
|
if (heads.length === 0) {
|
|
169
314
|
return log.debug("No heads received, returning...");
|
|
@@ -345,7 +490,9 @@ function indexer(opts) {
|
|
|
345
490
|
return log.debug("No new heads received, returning...");
|
|
346
491
|
}
|
|
347
492
|
if (hexToNumber(firstNewHead.number) !== indexerHeight + 1) {
|
|
348
|
-
throw new Error(
|
|
493
|
+
throw new Error(
|
|
494
|
+
`Client advanced too far (${hexToNumber(firstNewHead.number)}). Heads received are greater than the first new head ${indexerHeight + 1}`
|
|
495
|
+
);
|
|
349
496
|
}
|
|
350
497
|
for (const head of newHeads) {
|
|
351
498
|
const processed = await metadata.blocks.getBlocksByNumber(head.chain, head.number);
|
|
@@ -698,10 +845,21 @@ function indexer(opts) {
|
|
|
698
845
|
});
|
|
699
846
|
return { results: results_array, keys: filtered_keys };
|
|
700
847
|
};
|
|
848
|
+
const event = (event2) => {
|
|
849
|
+
if (!/^[A-Za-z0-9_-]+$/.test(event2.id)) {
|
|
850
|
+
throw new Error(`Invalid event id \`${event2.id}\`. Only characters A-Z, a-z, 0-9, underscores, and hyphens are permitted.`);
|
|
851
|
+
}
|
|
852
|
+
all_events.push(event2);
|
|
853
|
+
const group = events_grouped_by_storage_map.get(event2.storage) ?? [];
|
|
854
|
+
group.push(event2);
|
|
855
|
+
events_grouped_by_storage_map.set(event2.storage, group);
|
|
856
|
+
return event2;
|
|
857
|
+
};
|
|
701
858
|
const rpc = {
|
|
702
859
|
request: {
|
|
703
860
|
public_getFinalizedHeight,
|
|
704
861
|
public_writeFinalizedHeads,
|
|
862
|
+
public_writeUnfinalizedHead,
|
|
705
863
|
public_writeUnfinalizedHeads,
|
|
706
864
|
public_deleteReorganisedHead,
|
|
707
865
|
private_getEvents,
|
|
@@ -711,105 +869,17 @@ function indexer(opts) {
|
|
|
711
869
|
},
|
|
712
870
|
subscribe: {}
|
|
713
871
|
};
|
|
714
|
-
const
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
} catch {
|
|
720
|
-
return Response.json(
|
|
721
|
-
{ jsonrpc: "2.0", id: null, error: { code: 0, message: "Invalid request body" } },
|
|
722
|
-
//
|
|
723
|
-
{ status: 400 }
|
|
724
|
-
);
|
|
725
|
-
}
|
|
726
|
-
const authorization = req.headers.get("Authorization");
|
|
727
|
-
const authenticated = authorization === `Bearer ${opts.signingKey}`;
|
|
728
|
-
let body_string;
|
|
729
|
-
if (req.headers.get("Content-Encoding") === "gzip") {
|
|
730
|
-
if (!authorization) {
|
|
731
|
-
return Response.json(
|
|
732
|
-
{ jsonrpc: "2.0", id: null, error: { code: 0, message: "No bearer token provided" } },
|
|
733
|
-
//
|
|
734
|
-
{ status: 400 }
|
|
735
|
-
);
|
|
736
|
-
}
|
|
737
|
-
if (!authenticated) {
|
|
738
|
-
return Response.json(
|
|
739
|
-
{ jsonrpc: "2.0", id: null, error: { code: 0, message: "Invalid bearer token" } },
|
|
740
|
-
//
|
|
741
|
-
{ status: 400 }
|
|
742
|
-
);
|
|
743
|
-
}
|
|
744
|
-
body_string = await decompress(body_buffer);
|
|
745
|
-
} else {
|
|
746
|
-
body_string = decoder.decode(body_buffer);
|
|
747
|
-
}
|
|
748
|
-
let json;
|
|
749
|
-
try {
|
|
750
|
-
json = JSON.parse(body_string);
|
|
751
|
-
} catch {
|
|
752
|
-
return Response.json(
|
|
753
|
-
{ jsonrpc: "2.0", id: null, error: { code: 0, message: "Malformed JSON request body" } },
|
|
754
|
-
//
|
|
755
|
-
{ status: 400 }
|
|
756
|
-
);
|
|
757
|
-
}
|
|
758
|
-
if (json.method.startsWith("private_")) {
|
|
759
|
-
if (!authorization) {
|
|
760
|
-
return Response.json(
|
|
761
|
-
{ jsonrpc: "2.0", id: json.id, error: { code: 0, message: "No bearer token provided" } },
|
|
762
|
-
//
|
|
763
|
-
{ status: 400 }
|
|
764
|
-
);
|
|
765
|
-
}
|
|
766
|
-
if (authorization !== `Bearer ${opts.signingKey}`) {
|
|
767
|
-
return Response.json(
|
|
768
|
-
{ jsonrpc: "2.0", id: json.id, error: { code: 0, message: "Invalid bearer token" } },
|
|
769
|
-
//
|
|
770
|
-
{ status: 400 }
|
|
771
|
-
);
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
try {
|
|
775
|
-
const result = await transport.request({ method: json.method, params: json.params });
|
|
776
|
-
return Response.json({ jsonrpc: "2.0", id: json.id, result });
|
|
777
|
-
} catch (error) {
|
|
778
|
-
const message = getException(error);
|
|
779
|
-
if (message) {
|
|
780
|
-
log.error(message);
|
|
781
|
-
return Response.json(
|
|
782
|
-
{ jsonrpc: "2.0", id: json.id, error: { code: 0, message } },
|
|
783
|
-
//
|
|
784
|
-
{ status: 400 }
|
|
785
|
-
);
|
|
786
|
-
}
|
|
787
|
-
if (error instanceof Error) {
|
|
788
|
-
log.error(error.message);
|
|
789
|
-
}
|
|
790
|
-
return Response.json(
|
|
791
|
-
{ jsonrpc: "2.0", id: json.id, error: { code: 0, message: "Internal server error" } },
|
|
792
|
-
//
|
|
793
|
-
{ status: 500 }
|
|
794
|
-
);
|
|
795
|
-
}
|
|
796
|
-
};
|
|
797
|
-
const event = (event2) => {
|
|
798
|
-
if (!/^[A-Za-z0-9_-]+$/.test(event2.id)) {
|
|
799
|
-
throw new Error(`Invalid event id \`${event2.id}\`. Only characters A-Z, a-z, 0-9, underscores, and hyphens are permitted.`);
|
|
800
|
-
}
|
|
801
|
-
all_events.push(event2);
|
|
802
|
-
const group = events_grouped_by_storage_map.get(event2.storage) ?? [];
|
|
803
|
-
group.push(event2);
|
|
804
|
-
events_grouped_by_storage_map.set(event2.storage, group);
|
|
805
|
-
return event2;
|
|
806
|
-
};
|
|
872
|
+
const server = createServer({
|
|
873
|
+
transport: local(rpc),
|
|
874
|
+
quiet: opts.quiet ?? false,
|
|
875
|
+
signingKey: opts.signingKey
|
|
876
|
+
});
|
|
807
877
|
const indexer2 = {
|
|
808
878
|
...rpc,
|
|
809
879
|
event,
|
|
810
|
-
fetch:
|
|
880
|
+
fetch: server.http
|
|
811
881
|
};
|
|
812
882
|
return indexer2;
|
|
813
883
|
}
|
|
814
884
|
|
|
815
|
-
export { indexer
|
|
885
|
+
export { indexer };
|
package/dist/realtime.d.mts
CHANGED
package/dist/realtime.d.ts
CHANGED
package/dist/realtime.mjs
CHANGED
|
@@ -1,17 +1,27 @@
|
|
|
1
|
-
import { c as createLogger,
|
|
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.BVikUbk_.mjs';
|
|
2
2
|
|
|
3
|
-
const MAX_LENGTH = 1e4;
|
|
4
3
|
function defineBlockchain(opts) {
|
|
5
4
|
const log = createLogger({ quiet: opts.quiet });
|
|
6
5
|
const chain = [];
|
|
6
|
+
function getHeadBlock() {
|
|
7
|
+
const block = chain[chain.length - 1];
|
|
8
|
+
if (block === void 0) {
|
|
9
|
+
throw new Error("Expected non-empty chain when retrieving latest block");
|
|
10
|
+
}
|
|
11
|
+
return block;
|
|
12
|
+
}
|
|
13
|
+
function getTailBlock() {
|
|
14
|
+
const block = chain[0];
|
|
15
|
+
if (block === void 0) {
|
|
16
|
+
throw new Error("Expected non-empty chain when retrieving oldest block");
|
|
17
|
+
}
|
|
18
|
+
return block;
|
|
19
|
+
}
|
|
7
20
|
function setHeadBlock(newBlock) {
|
|
8
21
|
chain.push(newBlock);
|
|
9
22
|
if (opts.onBlockAdded) {
|
|
10
23
|
opts.onBlockAdded(newBlock);
|
|
11
24
|
}
|
|
12
|
-
if (chain.length > MAX_LENGTH) {
|
|
13
|
-
chain.shift();
|
|
14
|
-
}
|
|
15
25
|
}
|
|
16
26
|
function removeHeadBlock() {
|
|
17
27
|
const head = chain.pop();
|
|
@@ -22,25 +32,19 @@ function defineBlockchain(opts) {
|
|
|
22
32
|
opts.onBlockReorganised(head);
|
|
23
33
|
}
|
|
24
34
|
}
|
|
25
|
-
function
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
throw new Error("Expected non-empty chain when retrieving latest block");
|
|
35
|
+
async function prune(tail) {
|
|
36
|
+
if (hexToNumber(tail.number) >= hexToNumber(getHeadBlock().number)) {
|
|
37
|
+
throw new Error("Cannot remove head block");
|
|
29
38
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
function getOldestBlock() {
|
|
33
|
-
const block = chain[0];
|
|
34
|
-
if (block === void 0) {
|
|
35
|
-
throw new Error("Expected non-empty chain when retrieving oldest block");
|
|
39
|
+
while (hexToNumber(tail.number) > hexToNumber(getTailBlock().number)) {
|
|
40
|
+
chain.shift();
|
|
36
41
|
}
|
|
37
|
-
return block;
|
|
38
42
|
}
|
|
39
43
|
async function reconcile(newBlock) {
|
|
40
44
|
if (chain.length === 0) {
|
|
41
45
|
return setHeadBlock(newBlock);
|
|
42
46
|
}
|
|
43
|
-
if (hexToNumber(newBlock.number) < hexToNumber(
|
|
47
|
+
if (hexToNumber(newBlock.number) < hexToNumber(getTailBlock().number)) {
|
|
44
48
|
log.info(`Received block ${hexToNumber(newBlock.number)} older than our local chain`);
|
|
45
49
|
throw new Error("Not implemented");
|
|
46
50
|
}
|
|
@@ -74,6 +78,7 @@ function defineBlockchain(opts) {
|
|
|
74
78
|
}
|
|
75
79
|
return {
|
|
76
80
|
chain,
|
|
81
|
+
prune: mutex(prune),
|
|
77
82
|
reconcile: mutex(reconcile)
|
|
78
83
|
};
|
|
79
84
|
}
|
|
@@ -194,6 +199,16 @@ function realtime(opts) {
|
|
|
194
199
|
log.debug(`Delivered ${newHeads.length} finalized head(s)`);
|
|
195
200
|
indexerHeight = hexToNumber(finalizedBlock.number);
|
|
196
201
|
log.debug(`Updated indexer height to ${indexerHeight}`);
|
|
202
|
+
await indexer.prune({
|
|
203
|
+
number: finalizedBlock.number,
|
|
204
|
+
hash: finalizedBlock.hash,
|
|
205
|
+
parent_hash: finalizedBlock.parentHash
|
|
206
|
+
});
|
|
207
|
+
await latest.prune({
|
|
208
|
+
number: finalizedBlock.number,
|
|
209
|
+
hash: finalizedBlock.hash,
|
|
210
|
+
parent_hash: finalizedBlock.parentHash
|
|
211
|
+
});
|
|
197
212
|
} catch (error) {
|
|
198
213
|
if (error instanceof Error) {
|
|
199
214
|
log.error(`Failed to reconcile finalized head: ${error.message}`);
|
package/dist/rpc.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { H as Head, M as Metadata, F as Flatten, a as Filter, B as Block, R as Result } from './shared/univo.
|
|
1
|
+
import { H as Head, M as Metadata, F as Flatten, a as Filter, B as Block, R as Result } from './shared/univo.DdHfpTWq.mjs';
|
|
2
2
|
import '@storagesdk/core';
|
|
3
3
|
|
|
4
4
|
type Rpc = {
|
|
@@ -54,6 +54,10 @@ type IndexerRpc = {
|
|
|
54
54
|
* Accepts a reorganised block and deletes all related events from storage
|
|
55
55
|
*/
|
|
56
56
|
public_deleteReorganisedHead: (head: Head) => Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Accepts an unfinalized head and writes all events to storage
|
|
59
|
+
*/
|
|
60
|
+
public_writeUnfinalizedHead: (head: Head) => Promise<void>;
|
|
57
61
|
/**
|
|
58
62
|
* Accepts a list of the latest block heads and writes all events to storage
|
|
59
63
|
*/
|
package/dist/rpc.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { H as Head, M as Metadata, F as Flatten, a as Filter, B as Block, R as Result } from './shared/univo.
|
|
1
|
+
import { H as Head, M as Metadata, F as Flatten, a as Filter, B as Block, R as Result } from './shared/univo.DdHfpTWq.js';
|
|
2
2
|
import '@storagesdk/core';
|
|
3
3
|
|
|
4
4
|
type Rpc = {
|
|
@@ -54,6 +54,10 @@ type IndexerRpc = {
|
|
|
54
54
|
* Accepts a reorganised block and deletes all related events from storage
|
|
55
55
|
*/
|
|
56
56
|
public_deleteReorganisedHead: (head: Head) => Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Accepts an unfinalized head and writes all events to storage
|
|
59
|
+
*/
|
|
60
|
+
public_writeUnfinalizedHead: (head: Head) => Promise<void>;
|
|
57
61
|
/**
|
|
58
62
|
* Accepts a list of the latest block heads and writes all events to storage
|
|
59
63
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const name = "univo";
|
|
2
|
-
const version = "0.2.
|
|
2
|
+
const version = "0.2.16";
|
|
3
3
|
|
|
4
4
|
function createLogger(opts) {
|
|
5
5
|
let prefix = `[${name}@${version}]`;
|
|
@@ -93,4 +93,4 @@ const normalizeHex = (hex, length) => {
|
|
|
93
93
|
return `0x${hex.slice(2).toLowerCase().padStart(length, "0")}`;
|
|
94
94
|
};
|
|
95
95
|
|
|
96
|
-
export {
|
|
96
|
+
export { isHexEqual as a, name as b, createLogger as c, decompress as d, decoder as e, normalizeHex as f, compress as g, hexToNumber as h, iife as i, raise as j, mutex as m, numberToHex as n, retry as r, version as v };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import ws from 'ws';
|
|
2
2
|
import { WebSocket } from 'partysocket';
|
|
3
|
-
import {
|
|
3
|
+
import { g as compress, j as raise, c as createLogger } from './univo.BVikUbk_.mjs';
|
|
4
4
|
|
|
5
5
|
function createException(message) {
|
|
6
6
|
return `univo:${message}`;
|
|
@@ -38,7 +38,6 @@ type Filter = {
|
|
|
38
38
|
/** Index blocks that involve this address */
|
|
39
39
|
address?: `0x${string}`;
|
|
40
40
|
};
|
|
41
|
-
declare function matchFilter(block: Block, filter: Filter): boolean;
|
|
42
41
|
/**
|
|
43
42
|
* Events -----------------------------------------------------------------------------------------------------------------------------------
|
|
44
43
|
*/
|
|
@@ -192,6 +191,7 @@ declare function indexer<TBlock extends Block>(opts: IndexerOptions<TBlock>): {
|
|
|
192
191
|
request: {
|
|
193
192
|
public_getFinalizedHeight: (chain: `0x${string}`) => Promise<number>;
|
|
194
193
|
public_deleteReorganisedHead: (head: Head) => Promise<void>;
|
|
194
|
+
public_writeUnfinalizedHead: (head: Head) => Promise<void>;
|
|
195
195
|
public_writeUnfinalizedHeads: (heads: Head[]) => Promise<void>;
|
|
196
196
|
public_writeFinalizedHeads: (heads: Head[]) => Promise<void>;
|
|
197
197
|
private_getMetadata: () => Promise<Metadata>;
|
|
@@ -216,5 +216,5 @@ declare function indexer<TBlock extends Block>(opts: IndexerOptions<TBlock>): {
|
|
|
216
216
|
subscribe: Record<string, never>;
|
|
217
217
|
};
|
|
218
218
|
|
|
219
|
-
export { indexer as i
|
|
219
|
+
export { indexer as i };
|
|
220
220
|
export type { Block as B, Event as E, Flatten as F, Head as H, Indexer as I, Metadata as M, Result as R, Filter as a };
|
|
@@ -38,7 +38,6 @@ type Filter = {
|
|
|
38
38
|
/** Index blocks that involve this address */
|
|
39
39
|
address?: `0x${string}`;
|
|
40
40
|
};
|
|
41
|
-
declare function matchFilter(block: Block, filter: Filter): boolean;
|
|
42
41
|
/**
|
|
43
42
|
* Events -----------------------------------------------------------------------------------------------------------------------------------
|
|
44
43
|
*/
|
|
@@ -192,6 +191,7 @@ declare function indexer<TBlock extends Block>(opts: IndexerOptions<TBlock>): {
|
|
|
192
191
|
request: {
|
|
193
192
|
public_getFinalizedHeight: (chain: `0x${string}`) => Promise<number>;
|
|
194
193
|
public_deleteReorganisedHead: (head: Head) => Promise<void>;
|
|
194
|
+
public_writeUnfinalizedHead: (head: Head) => Promise<void>;
|
|
195
195
|
public_writeUnfinalizedHeads: (heads: Head[]) => Promise<void>;
|
|
196
196
|
public_writeFinalizedHeads: (heads: Head[]) => Promise<void>;
|
|
197
197
|
private_getMetadata: () => Promise<Metadata>;
|
|
@@ -216,5 +216,5 @@ declare function indexer<TBlock extends Block>(opts: IndexerOptions<TBlock>): {
|
|
|
216
216
|
subscribe: Record<string, never>;
|
|
217
217
|
};
|
|
218
218
|
|
|
219
|
-
export { indexer as i
|
|
219
|
+
export { indexer as i };
|
|
220
220
|
export type { Block as B, Event as E, Flatten as F, Head as H, Indexer as I, Metadata as M, Result as R, Filter as a };
|
package/dist/transport.d.mts
CHANGED
package/dist/transport.d.ts
CHANGED
package/dist/transport.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import 'ws';
|
|
2
2
|
import 'partysocket';
|
|
3
|
-
export { h as http, l as local, w as wss } from './shared/univo.
|
|
4
|
-
import './shared/univo.
|
|
3
|
+
export { h as http, l as local, w as wss } from './shared/univo.CkhykU8v.mjs';
|
|
4
|
+
import './shared/univo.BVikUbk_.mjs';
|