@typeberry/lib 0.5.6 → 0.5.7-d496f70
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/bin/lib/scripts/build-lib.js +1 -1
- package/package.json +7 -3
- package/packages/core/networking/metrics.js +2 -2
- package/packages/core/telemetry/index.d.ts.map +1 -1
- package/packages/core/telemetry/index.js +2 -3
- package/packages/core/utils/index.d.ts +1 -0
- package/packages/core/utils/index.d.ts.map +1 -1
- package/packages/core/utils/index.js +1 -0
- package/packages/core/utils/package.d.ts +3 -0
- package/packages/core/utils/package.d.ts.map +1 -0
- package/packages/core/utils/package.js +4 -0
- package/packages/jam/jamnp-s/network.d.ts +2 -0
- package/packages/jam/jamnp-s/network.d.ts.map +1 -1
- package/packages/jam/jamnp-s/network.js +4 -0
- package/packages/jam/jamnp-s/protocol/ce-131-ce-132-safrole-ticket-distribution.d.ts +4 -0
- package/packages/jam/jamnp-s/protocol/ce-131-ce-132-safrole-ticket-distribution.d.ts.map +1 -1
- package/packages/jam/jamnp-s/protocol/ce-131-ce-132-safrole-ticket-distribution.js +4 -0
- package/packages/jam/jamnp-s/tasks/ticket-distribution.d.ts +33 -0
- package/packages/jam/jamnp-s/tasks/ticket-distribution.d.ts.map +1 -0
- package/packages/jam/jamnp-s/tasks/ticket-distribution.js +115 -0
- package/packages/jam/jamnp-s/tasks/ticket-distribution.test.d.ts +2 -0
- package/packages/jam/jamnp-s/tasks/ticket-distribution.test.d.ts.map +1 -0
- package/packages/jam/jamnp-s/tasks/ticket-distribution.test.js +219 -0
- package/packages/jam/node/main-fuzz.d.ts.map +1 -1
- package/packages/jam/node/main-fuzz.js +2 -3
- package/packages/jam/node/main-fuzz.test.js +2 -4
- package/packages/jam/node/main-importer.d.ts.map +1 -1
- package/packages/jam/node/main-importer.js +2 -3
- package/packages/jam/node/main.d.ts.map +1 -1
- package/packages/jam/node/main.js +58 -29
- package/packages/jam/node/metrics.js +2 -2
- package/packages/jam/node/workers.d.ts +10 -3
- package/packages/jam/node/workers.d.ts.map +1 -1
- package/packages/jam/node/workers.js +16 -7
- package/packages/workers/api-node/config.d.ts +5 -1
- package/packages/workers/api-node/config.d.ts.map +1 -1
- package/packages/workers/api-node/config.js +9 -3
- package/packages/workers/api-node/port.d.ts +8 -0
- package/packages/workers/api-node/port.d.ts.map +1 -1
- package/packages/workers/api-node/port.js +10 -0
- package/packages/workers/block-authorship/bootstrap-main.js +10 -3
- package/packages/workers/block-authorship/main.d.ts +2 -1
- package/packages/workers/block-authorship/main.d.ts.map +1 -1
- package/packages/workers/block-authorship/main.js +4 -3
- package/packages/workers/block-authorship/metrics.js +2 -2
- package/packages/workers/comms-authorship-network/index.d.ts +3 -0
- package/packages/workers/comms-authorship-network/index.d.ts.map +1 -0
- package/packages/workers/comms-authorship-network/index.js +2 -0
- package/packages/workers/comms-authorship-network/protocol.d.ts +27 -0
- package/packages/workers/comms-authorship-network/protocol.d.ts.map +1 -0
- package/packages/workers/comms-authorship-network/protocol.js +24 -0
- package/packages/workers/comms-authorship-network/tickets-message.d.ts +18 -0
- package/packages/workers/comms-authorship-network/tickets-message.d.ts.map +1 -0
- package/packages/workers/comms-authorship-network/tickets-message.js +19 -0
- package/packages/workers/importer/metrics.js +2 -2
- package/packages/workers/jam-network/bootstrap-main.js +10 -3
- package/packages/workers/jam-network/main.d.ts +2 -1
- package/packages/workers/jam-network/main.d.ts.map +1 -1
- package/packages/workers/jam-network/main.js +8 -1
- package/packages/core/networking/package.json +0 -26
- package/packages/core/telemetry/package.json +0 -19
- package/packages/jam/node/package.json +0 -39
- package/packages/workers/block-authorship/package.json +0 -33
- package/packages/workers/importer/package.json +0 -33
|
@@ -85,7 +85,7 @@ const DIST_DIR = path.resolve(ROOT_DIR, "dist/lib");
|
|
|
85
85
|
* getVersion("0.5.1") // Returns "0.5.1"
|
|
86
86
|
*/
|
|
87
87
|
function getVersion(baseVersion) {
|
|
88
|
-
const isRelease = process.env.IS_RELEASE
|
|
88
|
+
const isRelease = Boolean(process.env.IS_RELEASE);
|
|
89
89
|
if (isRelease) {
|
|
90
90
|
return baseVersion;
|
|
91
91
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typeberry/lib",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.7-d496f70",
|
|
4
4
|
"description": "Typeberry Library",
|
|
5
5
|
"main": "./bin/lib/index.js",
|
|
6
6
|
"types": "./bin/lib/index.d.ts",
|
|
@@ -256,6 +256,8 @@
|
|
|
256
256
|
"#@typeberry/workers-api-node/*": "./packages/workers/api-node/*",
|
|
257
257
|
"#@typeberry/block-authorship": "./packages/workers/block-authorship/index.js",
|
|
258
258
|
"#@typeberry/block-authorship/*": "./packages/workers/block-authorship/*",
|
|
259
|
+
"#@typeberry/comms-authorship-network": "./packages/workers/comms-authorship-network/index.js",
|
|
260
|
+
"#@typeberry/comms-authorship-network/*": "./packages/workers/comms-authorship-network/*",
|
|
259
261
|
"#@typeberry/importer": "./packages/workers/importer/index.js",
|
|
260
262
|
"#@typeberry/importer/*": "./packages/workers/importer/*",
|
|
261
263
|
"#@typeberry/jam-network": "./packages/workers/jam-network/index.js",
|
|
@@ -264,8 +266,10 @@
|
|
|
264
266
|
"dependencies": {
|
|
265
267
|
"@fluffylabs/anan-as": "^1.1.5",
|
|
266
268
|
"@noble/ed25519": "2.2.3",
|
|
267
|
-
"
|
|
268
|
-
"
|
|
269
|
+
"hash-wasm": "4.12.0",
|
|
270
|
+
"@typeberry/native": "0.2.0-74dd7d7",
|
|
271
|
+
"eventemitter3": "^5.0.1",
|
|
272
|
+
"@opentelemetry/api": "1.9.0"
|
|
269
273
|
},
|
|
270
274
|
"repository": {
|
|
271
275
|
"type": "git",
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { metrics } from "@opentelemetry/api";
|
|
2
|
-
import
|
|
2
|
+
import { version } from "#@typeberry/utils";
|
|
3
3
|
/**
|
|
4
4
|
* Network metrics for JAM implementation.
|
|
5
5
|
*
|
|
6
6
|
* https://github.com/polkadot-fellows/JIPs/blob/main/JIP-3.md#networking-events
|
|
7
7
|
*/
|
|
8
8
|
export function createMetrics() {
|
|
9
|
-
const meter = metrics.getMeter(
|
|
9
|
+
const meter = metrics.getMeter("@typeberry/networking", version);
|
|
10
10
|
const connectionDuration = meter.createHistogram("jam.connectionTime", {
|
|
11
11
|
description: "Duration of connection to another peer",
|
|
12
12
|
unit: "ms",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../packages/core/telemetry/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../packages/core/telemetry/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAOlD,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,SAAS;aAoBgB,GAAG,EAAE,OAAO;IAnBhD,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,eAAe;IAmBzC,OAAO;IAED,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ7B"}
|
|
@@ -5,8 +5,7 @@ import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
|
|
|
5
5
|
import { NodeSDK } from "@opentelemetry/sdk-node";
|
|
6
6
|
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions";
|
|
7
7
|
import { Logger } from "#@typeberry/logger";
|
|
8
|
-
import { env } from "#@typeberry/utils";
|
|
9
|
-
import packageJson from "./package.json" with { type: "json" };
|
|
8
|
+
import { env, version } from "#@typeberry/utils";
|
|
10
9
|
const logger = Logger.new(import.meta.filename, "tele");
|
|
11
10
|
export class Telemetry {
|
|
12
11
|
sdk;
|
|
@@ -14,7 +13,7 @@ export class Telemetry {
|
|
|
14
13
|
const sdk = initializeTelemetry({
|
|
15
14
|
isMain: config.isMain ?? false,
|
|
16
15
|
serviceName: `typeberry-${config.nodeName}`,
|
|
17
|
-
serviceVersion:
|
|
16
|
+
serviceVersion: version,
|
|
18
17
|
enabled: env.OTEL_ENABLED !== "false",
|
|
19
18
|
otlpEndpoint: env.OTEL_EXPORTER_OTLP_ENDPOINT ?? "http://localhost:9090/api/v1/otlp",
|
|
20
19
|
resourceAttributes: {
|
|
@@ -15,6 +15,7 @@ export * from "./debug.js";
|
|
|
15
15
|
export * from "./dev.js";
|
|
16
16
|
export * from "./env.js";
|
|
17
17
|
export * from "./opaque.js";
|
|
18
|
+
export { name, version } from "./package.js";
|
|
18
19
|
export * from "./result.js";
|
|
19
20
|
export * from "./safe-alloc-uint8array.js";
|
|
20
21
|
export * from "./test.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../packages/core/utils/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../packages/core/utils/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC7C,cAAc,aAAa,CAAC;AAC5B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,WAAW,CAAC"}
|
|
@@ -15,6 +15,7 @@ export * from "./debug.js";
|
|
|
15
15
|
export * from "./dev.js";
|
|
16
16
|
export * from "./env.js";
|
|
17
17
|
export * from "./opaque.js";
|
|
18
|
+
export { name, version } from "./package.js";
|
|
18
19
|
export * from "./result.js";
|
|
19
20
|
export * from "./safe-alloc-uint8array.js";
|
|
20
21
|
export * from "./test.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package.d.ts","sourceRoot":"","sources":["../../../../../packages/core/utils/package.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,IAAI,QAAW,CAAC;AAC7B,eAAO,MAAM,OAAO,QAAc,CAAC"}
|
|
@@ -6,12 +6,14 @@ import { type Network, type Peer } from "#@typeberry/networking";
|
|
|
6
6
|
import { type Bootnode } from "./peers.js";
|
|
7
7
|
import { StreamManager } from "./stream-manager.js";
|
|
8
8
|
import { SyncTask } from "./tasks/sync.js";
|
|
9
|
+
import { TicketDistributionTask } from "./tasks/ticket-distribution.js";
|
|
9
10
|
export declare function setup(bind: {
|
|
10
11
|
host: string;
|
|
11
12
|
port: number;
|
|
12
13
|
}, genesisHash: HeaderHash, key: ed25519.Ed25519Pair, bootnodes: Bootnode[], spec: ChainSpec, blocks: BlocksDb, onNewBlocks: (blocks: BlockView[]) => Promise<void>): Promise<{
|
|
13
14
|
network: import("@typeberry/networking/quic-network.js").QuicNetwork;
|
|
14
15
|
syncTask: SyncTask;
|
|
16
|
+
ticketTask: TicketDistributionTask;
|
|
15
17
|
streamManager: StreamManager;
|
|
16
18
|
}>;
|
|
17
19
|
export declare function setupPeerListeners(syncTask: SyncTask, network: Network<Peer>, streamManager: StreamManager): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/jamnp-s/network.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAGpD,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,IAAI,EAAQ,MAAM,uBAAuB,CAAC;AAEtE,OAAO,EAAE,KAAK,QAAQ,EAAe,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/jamnp-s/network.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAGpD,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,IAAI,EAAQ,MAAM,uBAAuB,CAAC;AAEtE,OAAO,EAAE,KAAK,QAAQ,EAAe,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAKxE,wBAAsB,KAAK,CACzB,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EACpC,WAAW,EAAE,UAAU,EACvB,GAAG,EAAE,OAAO,CAAC,WAAW,EACxB,SAAS,EAAE,QAAQ,EAAE,EACrB,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,QAAQ,EAEhB,WAAW,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC;;;;;GA0CpD;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,aAAa,QAmB1G"}
|
|
@@ -6,6 +6,7 @@ import { OK } from "#@typeberry/utils";
|
|
|
6
6
|
import { Connections } from "./peers.js";
|
|
7
7
|
import { StreamManager } from "./stream-manager.js";
|
|
8
8
|
import { SyncTask } from "./tasks/sync.js";
|
|
9
|
+
import { TicketDistributionTask } from "./tasks/ticket-distribution.js";
|
|
9
10
|
import { handleAsyncErrors } from "./utils.js";
|
|
10
11
|
const logger = Logger.new(import.meta.filename, "jamnps");
|
|
11
12
|
export async function setup(bind, genesisHash, key, bootnodes, spec, blocks,
|
|
@@ -24,10 +25,12 @@ onNewBlocks) {
|
|
|
24
25
|
const streamManager = new StreamManager();
|
|
25
26
|
// start the networking tasks
|
|
26
27
|
const syncTask = SyncTask.start(spec, blake2b, streamManager, connections, blocks, onNewBlocks);
|
|
28
|
+
const ticketTask = TicketDistributionTask.start(streamManager, connections);
|
|
27
29
|
setImmediate(async () => {
|
|
28
30
|
while (network.isRunning) {
|
|
29
31
|
await setTimeout(3000);
|
|
30
32
|
syncTask.maintainSync();
|
|
33
|
+
ticketTask.maintainDistribution();
|
|
31
34
|
}
|
|
32
35
|
});
|
|
33
36
|
// TODO [ToDr] This design is a bit weird,
|
|
@@ -40,6 +43,7 @@ onNewBlocks) {
|
|
|
40
43
|
return {
|
|
41
44
|
network,
|
|
42
45
|
syncTask,
|
|
46
|
+
ticketTask,
|
|
43
47
|
streamManager,
|
|
44
48
|
};
|
|
45
49
|
}
|
|
@@ -14,6 +14,10 @@ import { type StreamHandler, type StreamId, type StreamMessageSender } from "./s
|
|
|
14
14
|
export declare const STREAM_KIND_GENERATOR_TO_PROXY: 131 & import("@typeberry/numbers").WithBytesRepresentation<1>;
|
|
15
15
|
export declare const STREAM_KIND_PROXY_TO_ALL: 132 & import("@typeberry/numbers").WithBytesRepresentation<1>;
|
|
16
16
|
type STREAM_KIND = typeof STREAM_KIND_GENERATOR_TO_PROXY | typeof STREAM_KIND_PROXY_TO_ALL;
|
|
17
|
+
/**
|
|
18
|
+
* Network protocol message for distributing a single ticket.
|
|
19
|
+
* Used in CE-131/CE-132 streams.
|
|
20
|
+
*/
|
|
17
21
|
export declare class TicketDistributionRequest extends WithDebug {
|
|
18
22
|
readonly epochIndex: Epoch;
|
|
19
23
|
readonly ticket: SignedTicket;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ce-131-ce-132-safrole-ticket-distribution.d.ts","sourceRoot":"","sources":["../../../../../../packages/jam/jamnp-s/protocol/ce-131-ce-132-safrole-ticket-distribution.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,KAAK,WAAW,EAA2B,MAAM,kBAAkB,CAAC;AAE7E,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,QAAQ,EAAE,KAAK,mBAAmB,EAAmB,MAAM,aAAa,CAAC;AAE3G;;;;;;GAMG;AAEH,eAAO,MAAM,8BAA8B,+DAAuB,CAAC;AACnE,eAAO,MAAM,wBAAwB,+DAAuB,CAAC;AAE7D,KAAK,WAAW,GAAG,OAAO,8BAA8B,GAAG,OAAO,wBAAwB,CAAC;AAE3F,qBAAa,yBAA0B,SAAQ,SAAS;aAWpC,UAAU,EAAE,KAAK;aACjB,MAAM,EAAE,YAAY;IAXtC,MAAM,CAAC,KAAK;;;;;;QAGT;IAEH,MAAM,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,WAAW,CAAC,yBAAyB,CAAC;IAI5E,OAAO;CAMR;AAID,qBAAa,aAAa,CAAC,CAAC,SAAS,WAAW,CAAE,YAAW,aAAa,CAAC,CAAC,CAAC;aAEzD,IAAI,EAAE,CAAC;IACvB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;gBADjB,IAAI,EAAE,CAAC,EACN,gBAAgB,EAAE,CAAC,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,KAAK,IAAI;IAGtF,eAAe,CAAC,MAAM,EAAE,mBAAmB,EAAE,OAAO,EAAE,SAAS,GAAG,IAAI;IAOtE,OAAO,CAAC,SAAS,EAAE,QAAQ;CAC5B;AAED,qBAAa,aAAa,CAAC,CAAC,SAAS,WAAW,CAAE,YAAW,aAAa,CAAC,CAAC,CAAC;aAC/C,IAAI,EAAE,CAAC;gBAAP,IAAI,EAAE,CAAC;IAEnC,eAAe,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI;IAKlD,OAAO,CAAC,SAAS,EAAE,QAAQ;IAE3B,UAAU,CAAC,MAAM,EAAE,mBAAmB,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY;CAKhF"}
|
|
1
|
+
{"version":3,"file":"ce-131-ce-132-safrole-ticket-distribution.d.ts","sourceRoot":"","sources":["../../../../../../packages/jam/jamnp-s/protocol/ce-131-ce-132-safrole-ticket-distribution.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,KAAK,WAAW,EAA2B,MAAM,kBAAkB,CAAC;AAE7E,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,QAAQ,EAAE,KAAK,mBAAmB,EAAmB,MAAM,aAAa,CAAC;AAE3G;;;;;;GAMG;AAEH,eAAO,MAAM,8BAA8B,+DAAuB,CAAC;AACnE,eAAO,MAAM,wBAAwB,+DAAuB,CAAC;AAE7D,KAAK,WAAW,GAAG,OAAO,8BAA8B,GAAG,OAAO,wBAAwB,CAAC;AAE3F;;;GAGG;AACH,qBAAa,yBAA0B,SAAQ,SAAS;aAWpC,UAAU,EAAE,KAAK;aACjB,MAAM,EAAE,YAAY;IAXtC,MAAM,CAAC,KAAK;;;;;;QAGT;IAEH,MAAM,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,WAAW,CAAC,yBAAyB,CAAC;IAI5E,OAAO;CAMR;AAID,qBAAa,aAAa,CAAC,CAAC,SAAS,WAAW,CAAE,YAAW,aAAa,CAAC,CAAC,CAAC;aAEzD,IAAI,EAAE,CAAC;IACvB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;gBADjB,IAAI,EAAE,CAAC,EACN,gBAAgB,EAAE,CAAC,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,KAAK,IAAI;IAGtF,eAAe,CAAC,MAAM,EAAE,mBAAmB,EAAE,OAAO,EAAE,SAAS,GAAG,IAAI;IAOtE,OAAO,CAAC,SAAS,EAAE,QAAQ;CAC5B;AAED,qBAAa,aAAa,CAAC,CAAC,SAAS,WAAW,CAAE,YAAW,aAAa,CAAC,CAAC,CAAC;aAC/C,IAAI,EAAE,CAAC;gBAAP,IAAI,EAAE,CAAC;IAEnC,eAAe,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI;IAKlD,OAAO,CAAC,SAAS,EAAE,QAAQ;IAE3B,UAAU,CAAC,MAAM,EAAE,mBAAmB,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY;CAKhF"}
|
|
@@ -12,6 +12,10 @@ import { tryAsStreamKind } from "./stream.js";
|
|
|
12
12
|
*/
|
|
13
13
|
export const STREAM_KIND_GENERATOR_TO_PROXY = tryAsStreamKind(131);
|
|
14
14
|
export const STREAM_KIND_PROXY_TO_ALL = tryAsStreamKind(132);
|
|
15
|
+
/**
|
|
16
|
+
* Network protocol message for distributing a single ticket.
|
|
17
|
+
* Used in CE-131/CE-132 streams.
|
|
18
|
+
*/
|
|
15
19
|
export class TicketDistributionRequest extends WithDebug {
|
|
16
20
|
epochIndex;
|
|
17
21
|
ticket;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Epoch } from "#@typeberry/block";
|
|
2
|
+
import type { SignedTicket } from "#@typeberry/block/tickets.js";
|
|
3
|
+
import type { Connections } from "../peers.js";
|
|
4
|
+
import type { StreamManager } from "../stream-manager.js";
|
|
5
|
+
/**
|
|
6
|
+
* Manages distribution of Safrole tickets to connected peers.
|
|
7
|
+
*
|
|
8
|
+
* Uses CE-132 (proxy-to-all) for direct broadcast to all peers.
|
|
9
|
+
* Implements a maintain pattern similar to SyncTask: tickets are collected
|
|
10
|
+
* and periodically distributed to peers that haven't received them yet.
|
|
11
|
+
*/
|
|
12
|
+
export declare class TicketDistributionTask {
|
|
13
|
+
private readonly streamManager;
|
|
14
|
+
private readonly connections;
|
|
15
|
+
static start(streamManager: StreamManager, connections: Connections): TicketDistributionTask;
|
|
16
|
+
/** Pending tickets waiting to be distributed to peers */
|
|
17
|
+
private pendingTickets;
|
|
18
|
+
/** Current epoch being tracked (cleared when epoch changes) */
|
|
19
|
+
private currentEpoch;
|
|
20
|
+
private constructor();
|
|
21
|
+
/**
|
|
22
|
+
* Should be called periodically to distribute pending tickets to connected peers.
|
|
23
|
+
*/
|
|
24
|
+
maintainDistribution(): void;
|
|
25
|
+
/**
|
|
26
|
+
* Add a ticket to the pending queue for distribution.
|
|
27
|
+
* Clears pending tickets when epoch changes.
|
|
28
|
+
* Deduplicates tickets based on signature.
|
|
29
|
+
*/
|
|
30
|
+
addTicket(epochIndex: Epoch, ticket: SignedTicket): void;
|
|
31
|
+
private onTicketReceived;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=ticket-distribution.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ticket-distribution.d.ts","sourceRoot":"","sources":["../../../../../../packages/jam/jamnp-s/tasks/ticket-distribution.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAGhE,OAAO,KAAK,EAAW,WAAW,EAAE,MAAM,aAAa,CAAC;AAExD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAe1D;;;;;;GAMG;AACH,qBAAa,sBAAsB;IAuB/B,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAvB9B,MAAM,CAAC,KAAK,CAAC,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW;IAgBnE,yDAAyD;IACzD,OAAO,CAAC,cAAc,CAA0D;IAChF,+DAA+D;IAC/D,OAAO,CAAC,YAAY,CAAsB;IAE1C,OAAO;IAKP;;OAEG;IACH,oBAAoB;IAkDpB;;;;OAIG;IACH,SAAS,CAAC,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY;IAgCjD,OAAO,CAAC,gBAAgB;CAKzB"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Logger } from "#@typeberry/logger";
|
|
2
|
+
import { OK } from "#@typeberry/utils";
|
|
3
|
+
import { ce131 } from "../protocol/index.js";
|
|
4
|
+
const logger = Logger.new(import.meta.filename, "net:tickets");
|
|
5
|
+
/** Aux data to track which tickets have been sent to each peer (using indices) */
|
|
6
|
+
const TICKET_AUX = {
|
|
7
|
+
id: Symbol("tickets"),
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Manages distribution of Safrole tickets to connected peers.
|
|
11
|
+
*
|
|
12
|
+
* Uses CE-132 (proxy-to-all) for direct broadcast to all peers.
|
|
13
|
+
* Implements a maintain pattern similar to SyncTask: tickets are collected
|
|
14
|
+
* and periodically distributed to peers that haven't received them yet.
|
|
15
|
+
*/
|
|
16
|
+
export class TicketDistributionTask {
|
|
17
|
+
streamManager;
|
|
18
|
+
connections;
|
|
19
|
+
static start(streamManager, connections) {
|
|
20
|
+
const task = new TicketDistributionTask(streamManager, connections);
|
|
21
|
+
// server mode: receive tickets from peers
|
|
22
|
+
streamManager.registerIncomingHandlers(new ce131.ServerHandler(ce131.STREAM_KIND_PROXY_TO_ALL, (epochIndex, ticket) => {
|
|
23
|
+
task.onTicketReceived(epochIndex, ticket);
|
|
24
|
+
}));
|
|
25
|
+
// client mode: send tickets to peers
|
|
26
|
+
streamManager.registerOutgoingHandlers(new ce131.ClientHandler(ce131.STREAM_KIND_PROXY_TO_ALL));
|
|
27
|
+
return task;
|
|
28
|
+
}
|
|
29
|
+
/** Pending tickets waiting to be distributed to peers */
|
|
30
|
+
pendingTickets = [];
|
|
31
|
+
/** Current epoch being tracked (cleared when epoch changes) */
|
|
32
|
+
currentEpoch = null;
|
|
33
|
+
constructor(streamManager, connections) {
|
|
34
|
+
this.streamManager = streamManager;
|
|
35
|
+
this.connections = connections;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Should be called periodically to distribute pending tickets to connected peers.
|
|
39
|
+
*/
|
|
40
|
+
maintainDistribution() {
|
|
41
|
+
if (this.currentEpoch === null) {
|
|
42
|
+
return; // No tickets to distribute yet
|
|
43
|
+
}
|
|
44
|
+
/** `this` is mutable and TS can't narrow this.currentEpoch inside the callback closure */
|
|
45
|
+
const currentEpoch = this.currentEpoch;
|
|
46
|
+
// Iterate through all pending tickets
|
|
47
|
+
for (let ticketIdx = 0; ticketIdx < this.pendingTickets.length; ticketIdx++) {
|
|
48
|
+
const { epochIndex, ticket } = this.pendingTickets[ticketIdx];
|
|
49
|
+
// Try to send to each connected peer
|
|
50
|
+
for (const peerInfo of this.connections.getConnectedPeers()) {
|
|
51
|
+
this.connections.withAuxData(peerInfo.peerId, TICKET_AUX, (maybeAux) => {
|
|
52
|
+
const shouldReset = maybeAux === undefined || maybeAux.epoch !== currentEpoch;
|
|
53
|
+
const aux = shouldReset ? { epoch: currentEpoch, seen: new Set() } : maybeAux;
|
|
54
|
+
if (peerInfo.peerRef === null) {
|
|
55
|
+
return aux;
|
|
56
|
+
}
|
|
57
|
+
// Check if we already sent this ticket to this peer
|
|
58
|
+
if (aux.seen.has(ticketIdx)) {
|
|
59
|
+
return aux; // Already sent
|
|
60
|
+
}
|
|
61
|
+
// Send the ticket - only mark as sent after successful send
|
|
62
|
+
try {
|
|
63
|
+
this.streamManager.withNewStream(peerInfo.peerRef, ce131.STREAM_KIND_PROXY_TO_ALL, (handler, sender) => {
|
|
64
|
+
logger.trace `[${peerInfo.peerId}] <-- Sending ticket for epoch ${epochIndex}`;
|
|
65
|
+
handler.sendTicket(sender, epochIndex, ticket);
|
|
66
|
+
return OK;
|
|
67
|
+
});
|
|
68
|
+
// Mark as sent only after successful send, so failed sends will be retried
|
|
69
|
+
aux.seen.add(ticketIdx);
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
logger.warn `[${peerInfo.peerId}] Failed to send ticket for epoch ${epochIndex}: ${e}`;
|
|
73
|
+
}
|
|
74
|
+
return aux;
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Add a ticket to the pending queue for distribution.
|
|
81
|
+
* Clears pending tickets when epoch changes.
|
|
82
|
+
* Deduplicates tickets based on signature.
|
|
83
|
+
*/
|
|
84
|
+
addTicket(epochIndex, ticket) {
|
|
85
|
+
// Check if epoch changed - if so, clear old tickets
|
|
86
|
+
if (this.currentEpoch !== null && this.currentEpoch !== epochIndex) {
|
|
87
|
+
logger.log `[addTicket] Epoch changed from ${this.currentEpoch} to ${epochIndex}, clearing ${this.pendingTickets.length} old tickets`;
|
|
88
|
+
this.pendingTickets = [];
|
|
89
|
+
// Note: We don't need to clear aux data for all peers here.
|
|
90
|
+
// The aux data contains the epoch, so maintainDistribution will lazily
|
|
91
|
+
// reset it when it detects an epoch mismatch. This handles both connected
|
|
92
|
+
// and disconnected peers correctly.
|
|
93
|
+
}
|
|
94
|
+
this.currentEpoch = epochIndex;
|
|
95
|
+
/**
|
|
96
|
+
* Deduplicate: check if a ticket with the same signature already exists
|
|
97
|
+
*
|
|
98
|
+
* Here we are risking "poisoning" the local pendingTickets - i.e:
|
|
99
|
+
* 1. The adversary sees a signature and swaps the ticket attempt to something different.
|
|
100
|
+
* 2. This creates an invalid ticket, but prevents a valid ticket with the same signature from being included and distributed.
|
|
101
|
+
*
|
|
102
|
+
* TODO [MaSi]: The poisoning risk should be fixed during implementation of ticket validation.
|
|
103
|
+
*/
|
|
104
|
+
const isDuplicate = this.pendingTickets.some((pending) => pending.epochIndex === epochIndex && pending.ticket.signature.isEqualTo(ticket.signature));
|
|
105
|
+
if (!isDuplicate) {
|
|
106
|
+
this.pendingTickets.push({ epochIndex, ticket });
|
|
107
|
+
logger.info `[addTicket] Added ticket for epoch ${epochIndex}, total: ${this.pendingTickets.length}`;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
onTicketReceived(epochIndex, ticket) {
|
|
111
|
+
logger.trace `Received ticket for epoch ${epochIndex}, attempt ${ticket.attempt}`;
|
|
112
|
+
// Add to pending queue for potential re-distribution
|
|
113
|
+
this.addTicket(epochIndex, ticket);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ticket-distribution.test.d.ts","sourceRoot":"","sources":["../../../../../../packages/jam/jamnp-s/tasks/ticket-distribution.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, it } from "node:test";
|
|
3
|
+
import { setTimeout } from "node:timers/promises";
|
|
4
|
+
import { tryAsEpoch } from "#@typeberry/block";
|
|
5
|
+
import { SignedTicket, tryAsTicketAttempt } from "#@typeberry/block/tickets.js";
|
|
6
|
+
import { Bytes } from "#@typeberry/bytes";
|
|
7
|
+
import { BANDERSNATCH_PROOF_BYTES } from "#@typeberry/crypto";
|
|
8
|
+
import { Logger } from "#@typeberry/logger";
|
|
9
|
+
import { createTestPeerPair, MockNetwork } from "#@typeberry/networking/testing.js";
|
|
10
|
+
import { OK } from "#@typeberry/utils";
|
|
11
|
+
import { Connections } from "../peers.js";
|
|
12
|
+
import { StreamManager } from "../stream-manager.js";
|
|
13
|
+
import { TicketDistributionTask } from "./ticket-distribution.js";
|
|
14
|
+
const logger = Logger.new(import.meta.filename, "test:tickets");
|
|
15
|
+
const TEST_EPOCH = tryAsEpoch(42);
|
|
16
|
+
const OTHER_EPOCH = tryAsEpoch(43);
|
|
17
|
+
function createTestTicket(attempt, signatureByte = 0) {
|
|
18
|
+
const signatureBytes = Bytes.zero(BANDERSNATCH_PROOF_BYTES);
|
|
19
|
+
// Make signature unique based on attempt and signatureByte
|
|
20
|
+
signatureBytes.raw[0] = attempt;
|
|
21
|
+
signatureBytes.raw[1] = signatureByte;
|
|
22
|
+
return SignedTicket.create({
|
|
23
|
+
attempt: tryAsTicketAttempt(attempt),
|
|
24
|
+
signature: signatureBytes.asOpaque(),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
describe("TicketDistributionTask", () => {
|
|
28
|
+
async function init(name) {
|
|
29
|
+
const network = new MockNetwork(name);
|
|
30
|
+
const streamManager = new StreamManager();
|
|
31
|
+
const connections = new Connections(network);
|
|
32
|
+
// Track received tickets for verification
|
|
33
|
+
const receivedTickets = [];
|
|
34
|
+
// Use real TicketDistributionTask
|
|
35
|
+
const ticketTask = TicketDistributionTask.start(streamManager, connections);
|
|
36
|
+
// Intercept received tickets by wrapping onTicketReceived behavior
|
|
37
|
+
// The task already adds received tickets to pending queue via addTicket,
|
|
38
|
+
// so we can track them by checking the pending queue growth or by
|
|
39
|
+
// hooking into the CE-131 server handler directly
|
|
40
|
+
const originalAddTicket = ticketTask.addTicket.bind(ticketTask);
|
|
41
|
+
ticketTask.addTicket = (epochIndex, ticket) => {
|
|
42
|
+
receivedTickets.push({ epochIndex, ticket });
|
|
43
|
+
originalAddTicket(epochIndex, ticket);
|
|
44
|
+
};
|
|
45
|
+
// Setup peer listeners for incoming streams
|
|
46
|
+
network.peers.onPeerConnected((peer) => {
|
|
47
|
+
peer.addOnIncomingStream((stream) => {
|
|
48
|
+
streamManager.onIncomingStream(peer, stream);
|
|
49
|
+
return OK;
|
|
50
|
+
});
|
|
51
|
+
return OK;
|
|
52
|
+
});
|
|
53
|
+
let connectionIdx = 0;
|
|
54
|
+
const openConnection = (other) => {
|
|
55
|
+
const [self, peer] = createTestPeerPair(connectionIdx++, name, other.name);
|
|
56
|
+
network._peers.peerConnected(peer);
|
|
57
|
+
other.network._peers.peerConnected(self);
|
|
58
|
+
return [self, peer];
|
|
59
|
+
};
|
|
60
|
+
return {
|
|
61
|
+
name,
|
|
62
|
+
ticketTask,
|
|
63
|
+
network,
|
|
64
|
+
connections,
|
|
65
|
+
streamManager,
|
|
66
|
+
openConnection,
|
|
67
|
+
receivedTickets,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
async function tick() {
|
|
71
|
+
logger.log `tick`;
|
|
72
|
+
await setTimeout(10);
|
|
73
|
+
}
|
|
74
|
+
it("should distribute ticket to all connected peers via maintainDistribution", async () => {
|
|
75
|
+
const self = await init("self");
|
|
76
|
+
const peer1 = await init("peer1");
|
|
77
|
+
const peer2 = await init("peer2");
|
|
78
|
+
self.openConnection(peer1);
|
|
79
|
+
self.openConnection(peer2);
|
|
80
|
+
await tick();
|
|
81
|
+
const ticket = createTestTicket(0);
|
|
82
|
+
self.ticketTask.addTicket(TEST_EPOCH, ticket);
|
|
83
|
+
self.ticketTask.maintainDistribution();
|
|
84
|
+
await tick();
|
|
85
|
+
// Both peers should have received the ticket
|
|
86
|
+
assert.strictEqual(peer1.receivedTickets.length, 1);
|
|
87
|
+
assert.strictEqual(peer1.receivedTickets[0].epochIndex, TEST_EPOCH);
|
|
88
|
+
assert.deepStrictEqual(peer1.receivedTickets[0].ticket, ticket);
|
|
89
|
+
assert.strictEqual(peer2.receivedTickets.length, 1);
|
|
90
|
+
assert.strictEqual(peer2.receivedTickets[0].epochIndex, TEST_EPOCH);
|
|
91
|
+
assert.deepStrictEqual(peer2.receivedTickets[0].ticket, ticket);
|
|
92
|
+
});
|
|
93
|
+
it("should receive tickets from peers", async () => {
|
|
94
|
+
const self = await init("self");
|
|
95
|
+
const peer1 = await init("peer1");
|
|
96
|
+
self.openConnection(peer1);
|
|
97
|
+
await tick();
|
|
98
|
+
const ticket = createTestTicket(1);
|
|
99
|
+
peer1.ticketTask.addTicket(TEST_EPOCH, ticket);
|
|
100
|
+
peer1.ticketTask.maintainDistribution();
|
|
101
|
+
await tick();
|
|
102
|
+
assert.strictEqual(self.receivedTickets.length, 1);
|
|
103
|
+
assert.strictEqual(self.receivedTickets[0].epochIndex, TEST_EPOCH);
|
|
104
|
+
assert.deepStrictEqual(self.receivedTickets[0].ticket, ticket);
|
|
105
|
+
});
|
|
106
|
+
it("should handle multiple tickets", async () => {
|
|
107
|
+
const self = await init("self");
|
|
108
|
+
const peer1 = await init("peer1");
|
|
109
|
+
self.openConnection(peer1);
|
|
110
|
+
await tick();
|
|
111
|
+
const ticket0 = createTestTicket(0);
|
|
112
|
+
const ticket1 = createTestTicket(1);
|
|
113
|
+
self.ticketTask.addTicket(TEST_EPOCH, ticket0);
|
|
114
|
+
self.ticketTask.addTicket(TEST_EPOCH, ticket1);
|
|
115
|
+
self.ticketTask.maintainDistribution();
|
|
116
|
+
await tick();
|
|
117
|
+
assert.strictEqual(peer1.receivedTickets.length, 2);
|
|
118
|
+
assert.deepStrictEqual(peer1.receivedTickets[0].ticket, ticket0);
|
|
119
|
+
assert.deepStrictEqual(peer1.receivedTickets[1].ticket, ticket1);
|
|
120
|
+
});
|
|
121
|
+
it("should handle no connected peers gracefully", async () => {
|
|
122
|
+
const self = await init("self");
|
|
123
|
+
const ticket = createTestTicket(0);
|
|
124
|
+
// Should not throw
|
|
125
|
+
self.ticketTask.addTicket(TEST_EPOCH, ticket);
|
|
126
|
+
self.ticketTask.maintainDistribution();
|
|
127
|
+
await tick();
|
|
128
|
+
});
|
|
129
|
+
it("should deduplicate tickets with same signature", async () => {
|
|
130
|
+
const self = await init("self");
|
|
131
|
+
const peer1 = await init("peer1");
|
|
132
|
+
self.openConnection(peer1);
|
|
133
|
+
await tick();
|
|
134
|
+
const ticket = createTestTicket(0);
|
|
135
|
+
// Add same ticket twice
|
|
136
|
+
self.ticketTask.addTicket(TEST_EPOCH, ticket);
|
|
137
|
+
self.ticketTask.addTicket(TEST_EPOCH, ticket);
|
|
138
|
+
self.ticketTask.maintainDistribution();
|
|
139
|
+
await tick();
|
|
140
|
+
// Peer should only receive it once
|
|
141
|
+
assert.strictEqual(peer1.receivedTickets.length, 1);
|
|
142
|
+
});
|
|
143
|
+
it("should not send same ticket to same peer twice", async () => {
|
|
144
|
+
const self = await init("self");
|
|
145
|
+
const peer1 = await init("peer1");
|
|
146
|
+
self.openConnection(peer1);
|
|
147
|
+
await tick();
|
|
148
|
+
const ticket = createTestTicket(0);
|
|
149
|
+
self.ticketTask.addTicket(TEST_EPOCH, ticket);
|
|
150
|
+
// Call maintainDistribution twice
|
|
151
|
+
self.ticketTask.maintainDistribution();
|
|
152
|
+
await tick();
|
|
153
|
+
self.ticketTask.maintainDistribution();
|
|
154
|
+
await tick();
|
|
155
|
+
// Peer should only receive the ticket once (aux data tracks sent indices)
|
|
156
|
+
assert.strictEqual(peer1.receivedTickets.length, 1);
|
|
157
|
+
});
|
|
158
|
+
it("should clear tickets when epoch changes", async () => {
|
|
159
|
+
const self = await init("self");
|
|
160
|
+
const peer1 = await init("peer1");
|
|
161
|
+
self.openConnection(peer1);
|
|
162
|
+
await tick();
|
|
163
|
+
const ticket1 = createTestTicket(0);
|
|
164
|
+
const ticket2 = createTestTicket(1);
|
|
165
|
+
// Add ticket for first epoch
|
|
166
|
+
self.ticketTask.addTicket(TEST_EPOCH, ticket1);
|
|
167
|
+
// Change epoch - this should clear old tickets
|
|
168
|
+
self.ticketTask.addTicket(OTHER_EPOCH, ticket2);
|
|
169
|
+
self.ticketTask.maintainDistribution();
|
|
170
|
+
await tick();
|
|
171
|
+
// Peer should only receive the second ticket (first was cleared on epoch change)
|
|
172
|
+
assert.strictEqual(peer1.receivedTickets.length, 1);
|
|
173
|
+
assert.strictEqual(peer1.receivedTickets[0].epochIndex, OTHER_EPOCH);
|
|
174
|
+
assert.deepStrictEqual(peer1.receivedTickets[0].ticket, ticket2);
|
|
175
|
+
});
|
|
176
|
+
it("should send new tickets to newly connected peers", async () => {
|
|
177
|
+
const self = await init("self");
|
|
178
|
+
const peer1 = await init("peer1");
|
|
179
|
+
const peer2 = await init("peer2");
|
|
180
|
+
// Connect peer1 first
|
|
181
|
+
self.openConnection(peer1);
|
|
182
|
+
await tick();
|
|
183
|
+
const ticket = createTestTicket(0);
|
|
184
|
+
self.ticketTask.addTicket(TEST_EPOCH, ticket);
|
|
185
|
+
self.ticketTask.maintainDistribution();
|
|
186
|
+
await tick();
|
|
187
|
+
// Now connect peer2 after ticket was already distributed to peer1
|
|
188
|
+
self.openConnection(peer2);
|
|
189
|
+
await tick();
|
|
190
|
+
// Run maintainDistribution again - peer2 should get the ticket
|
|
191
|
+
self.ticketTask.maintainDistribution();
|
|
192
|
+
await tick();
|
|
193
|
+
assert.strictEqual(peer1.receivedTickets.length, 1);
|
|
194
|
+
assert.strictEqual(peer2.receivedTickets.length, 1);
|
|
195
|
+
assert.deepStrictEqual(peer2.receivedTickets[0].ticket, ticket);
|
|
196
|
+
});
|
|
197
|
+
it("should re-distribute received tickets to other peers", async () => {
|
|
198
|
+
const self = await init("self");
|
|
199
|
+
const peer1 = await init("peer1");
|
|
200
|
+
const peer2 = await init("peer2");
|
|
201
|
+
// Self connects to both peers
|
|
202
|
+
self.openConnection(peer1);
|
|
203
|
+
self.openConnection(peer2);
|
|
204
|
+
await tick();
|
|
205
|
+
// peer1 sends a ticket to self
|
|
206
|
+
const ticket = createTestTicket(0);
|
|
207
|
+
peer1.ticketTask.addTicket(TEST_EPOCH, ticket);
|
|
208
|
+
peer1.ticketTask.maintainDistribution();
|
|
209
|
+
await tick();
|
|
210
|
+
// Self receives the ticket (via onTicketReceived -> addTicket)
|
|
211
|
+
assert.strictEqual(self.receivedTickets.length, 1);
|
|
212
|
+
// Self should re-distribute to peer2 (and peer1, but peer1 already has it)
|
|
213
|
+
self.ticketTask.maintainDistribution();
|
|
214
|
+
await tick();
|
|
215
|
+
// peer2 should now have received the ticket from self
|
|
216
|
+
assert.strictEqual(peer2.receivedTickets.length, 1);
|
|
217
|
+
assert.deepStrictEqual(peer2.receivedTickets[0].ticket, ticket);
|
|
218
|
+
});
|
|
219
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main-fuzz.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main-fuzz.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,WAAW,EAAmB,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAMrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"main-fuzz.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main-fuzz.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,WAAW,EAAmB,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAMrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,EAAE,WAAW,CAAC;IACrB,aAAa,EAAE,SAAS,CAAC;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,uBAAuB,EAAE,OAAO,CAAC;CAClC,CAAC;AAIF,wBAAgB,cAAc;;;;EAM7B;AAED,wBAAsB,QAAQ,CAAC,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,uBAoExF"}
|
|
@@ -6,15 +6,14 @@ import { startFuzzTarget } from "#@typeberry/ext-ipc";
|
|
|
6
6
|
import { v1 as fuzzV1 } from "#@typeberry/fuzz-proto";
|
|
7
7
|
import { HASH_SIZE } from "#@typeberry/hash";
|
|
8
8
|
import { Logger } from "#@typeberry/logger";
|
|
9
|
-
import { CURRENT_VERSION, Result } from "#@typeberry/utils";
|
|
9
|
+
import { CURRENT_VERSION, Result, version } from "#@typeberry/utils";
|
|
10
10
|
import { getChainSpec } from "./common.js";
|
|
11
11
|
import { mainImporter } from "./main-importer.js";
|
|
12
|
-
import packageJson from "./package.json" with { type: "json" };
|
|
13
12
|
const logger = Logger.new(import.meta.filename, "fuzztarget");
|
|
14
13
|
export function getFuzzDetails() {
|
|
15
14
|
return {
|
|
16
15
|
nodeName: "@typeberry/jam",
|
|
17
|
-
nodeVersion: fuzzV1.Version.tryFromString(
|
|
16
|
+
nodeVersion: fuzzV1.Version.tryFromString(version),
|
|
18
17
|
gpVersion: fuzzV1.Version.tryFromString(CURRENT_VERSION.split("-")[0]),
|
|
19
18
|
};
|
|
20
19
|
}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { describe, it } from "node:test";
|
|
2
2
|
import { tryAsU8 } from "#@typeberry/numbers";
|
|
3
|
-
import { CURRENT_VERSION, deepEqual } from "#@typeberry/utils";
|
|
4
|
-
//eslint-disable-next-line import/no-relative-packages
|
|
5
|
-
import pkg from "../../../package.json" with { type: "json" };
|
|
3
|
+
import { CURRENT_VERSION, deepEqual, version } from "#@typeberry/utils";
|
|
6
4
|
import { getFuzzDetails } from "./main-fuzz.js";
|
|
7
5
|
describe("fuzzing config", () => {
|
|
8
6
|
it("should create config from current version", () => {
|
|
9
|
-
const [m, i, p] =
|
|
7
|
+
const [m, i, p] = version.split(".").map((x) => Number.parseInt(x, 10));
|
|
10
8
|
const [gpM, gpI, gpP] = CURRENT_VERSION.split(".").map((x) => Number.parseInt(x, 10));
|
|
11
9
|
const fuzzDetails = getFuzzDetails();
|
|
12
10
|
deepEqual(fuzzDetails, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main-importer.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main-importer.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"main-importer.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main-importer.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIzC,MAAM,MAAM,eAAe,GAAG;IAC5B,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC,CAAC;AAEF,wBAAsB,YAAY,CAChC,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,EAClC,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,OAAO,CAAC,CA+ElB"}
|