@typeberry/lib 0.8.4 → 0.9.0-3f2c45b
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/package.json +6 -4
- package/packages/configs/index.d.ts +30 -1
- package/packages/configs/index.d.ts.map +1 -1
- package/packages/configs/index.js +4 -2
- package/packages/configs/typeberry-dev-full.json +29 -0
- package/packages/core/bytes/bytes.d.ts +1 -0
- package/packages/core/bytes/bytes.d.ts.map +1 -1
- package/packages/core/bytes/bytes.js +8 -0
- package/packages/core/utils/debug.d.ts +4 -2
- package/packages/core/utils/debug.d.ts.map +1 -1
- package/packages/core/utils/debug.js +18 -13
- package/packages/core/utils/debug.test.js +12 -6
- package/packages/jam/config-node/node-config.d.ts +2 -1
- package/packages/jam/config-node/node-config.d.ts.map +1 -1
- package/packages/jam/config-node/node-config.js +8 -3
- package/packages/jam/config-node/node-config.test.js +3 -3
- package/packages/jam/database-fjall/hybrid-states.d.ts +82 -0
- package/packages/jam/database-fjall/hybrid-states.d.ts.map +1 -0
- package/packages/jam/database-fjall/hybrid-states.js +169 -0
- package/packages/jam/database-fjall/hybrid-states.test.d.ts +2 -0
- package/packages/jam/database-fjall/hybrid-states.test.d.ts.map +1 -0
- package/packages/jam/database-fjall/hybrid-states.test.js +113 -0
- package/packages/jam/database-fjall/index.d.ts +3 -0
- package/packages/jam/database-fjall/index.d.ts.map +1 -0
- package/packages/jam/database-fjall/index.js +2 -0
- package/packages/jam/database-fjall/root.d.ts +58 -0
- package/packages/jam/database-fjall/root.d.ts.map +1 -0
- package/packages/jam/database-fjall/root.js +89 -0
- package/packages/jam/jamnp-s/tasks/ticket-distribution.d.ts +18 -10
- package/packages/jam/jamnp-s/tasks/ticket-distribution.d.ts.map +1 -1
- package/packages/jam/jamnp-s/tasks/ticket-distribution.js +44 -68
- package/packages/jam/jamnp-s/tasks/ticket-distribution.test.js +30 -8
- package/packages/jam/node/main-fuzz.d.ts.map +1 -1
- package/packages/jam/node/main-fuzz.js +69 -11
- package/packages/jam/node/main-importer.d.ts +13 -3
- package/packages/jam/node/main-importer.d.ts.map +1 -1
- package/packages/jam/node/main-importer.js +5 -3
- package/packages/jam/safrole/bandersnatch-vrf.d.ts +24 -4
- package/packages/jam/safrole/bandersnatch-vrf.d.ts.map +1 -1
- package/packages/jam/safrole/bandersnatch-vrf.js +63 -26
- package/packages/jam/safrole/bandersnatch-vrf.test.js +12 -9
- package/packages/jam/safrole/bandersnatch-wasm.d.ts +10 -0
- package/packages/jam/safrole/bandersnatch-wasm.d.ts.map +1 -1
- package/packages/jam/safrole/bandersnatch-wasm.js +12 -0
- package/packages/jam/safrole/safrole.js +5 -5
- package/packages/jam/safrole/safrole.test.js +13 -13
- package/packages/jam/ticket-pool/index.d.ts +4 -0
- package/packages/jam/ticket-pool/index.d.ts.map +1 -0
- package/packages/jam/ticket-pool/index.js +3 -0
- package/packages/jam/ticket-pool/pending-ticket-pool.d.ts +30 -0
- package/packages/jam/ticket-pool/pending-ticket-pool.d.ts.map +1 -0
- package/packages/jam/ticket-pool/pending-ticket-pool.js +56 -0
- package/packages/jam/ticket-pool/pending-ticket-pool.test.d.ts +2 -0
- package/packages/jam/ticket-pool/pending-ticket-pool.test.d.ts.map +1 -0
- package/packages/jam/ticket-pool/pending-ticket-pool.test.js +67 -0
- package/packages/jam/ticket-pool/ticket-validator.d.ts +47 -0
- package/packages/jam/ticket-pool/ticket-validator.d.ts.map +1 -0
- package/packages/jam/ticket-pool/ticket-validator.js +34 -0
- package/packages/jam/ticket-pool/ticket-validator.test.d.ts +2 -0
- package/packages/jam/ticket-pool/ticket-validator.test.d.ts.map +1 -0
- package/packages/jam/ticket-pool/ticket-validator.test.js +35 -0
- package/packages/jam/ticket-pool/verified-ticket-pool.d.ts +26 -0
- package/packages/jam/ticket-pool/verified-ticket-pool.d.ts.map +1 -0
- package/packages/jam/ticket-pool/verified-ticket-pool.js +41 -0
- package/packages/jam/ticket-pool/verified-ticket-pool.test.d.ts +2 -0
- package/packages/jam/ticket-pool/verified-ticket-pool.test.d.ts.map +1 -0
- package/packages/jam/ticket-pool/verified-ticket-pool.test.js +54 -0
- package/packages/workers/api-node/config.d.ts +21 -6
- package/packages/workers/api-node/config.d.ts.map +1 -1
- package/packages/workers/api-node/config.js +26 -19
- package/packages/workers/api-node/config.test.js +38 -1
- package/packages/workers/block-authorship/{generator.d.ts → block-generator.d.ts} +5 -5
- package/packages/workers/block-authorship/block-generator.d.ts.map +1 -0
- package/packages/workers/block-authorship/{generator.js → block-generator.js} +3 -3
- package/packages/workers/block-authorship/block-generator.test.d.ts +2 -0
- package/packages/workers/block-authorship/block-generator.test.d.ts.map +1 -0
- package/packages/workers/block-authorship/{generator.test.js → block-generator.test.js} +8 -8
- package/packages/workers/block-authorship/epoch-authoring-slots.d.ts +35 -0
- package/packages/workers/block-authorship/epoch-authoring-slots.d.ts.map +1 -0
- package/packages/workers/block-authorship/epoch-authoring-slots.js +86 -0
- package/packages/workers/block-authorship/epoch-tracker.d.ts +29 -0
- package/packages/workers/block-authorship/epoch-tracker.d.ts.map +1 -0
- package/packages/workers/block-authorship/epoch-tracker.js +80 -0
- package/packages/workers/block-authorship/index.d.ts.map +1 -1
- package/packages/workers/block-authorship/index.js +1 -1
- package/packages/workers/block-authorship/main.d.ts +3 -0
- package/packages/workers/block-authorship/main.d.ts.map +1 -1
- package/packages/workers/block-authorship/main.js +197 -315
- package/packages/workers/block-authorship/ticket-generator/bootstrap-main.d.ts +2 -0
- package/packages/workers/block-authorship/ticket-generator/bootstrap-main.d.ts.map +1 -0
- package/packages/workers/block-authorship/ticket-generator/bootstrap-main.js +23 -0
- package/packages/workers/block-authorship/ticket-generator/index.d.ts +16 -0
- package/packages/workers/block-authorship/ticket-generator/index.d.ts.map +1 -0
- package/packages/workers/block-authorship/ticket-generator/index.js +62 -0
- package/packages/workers/block-authorship/ticket-generator/protocol.d.ts +50 -0
- package/packages/workers/block-authorship/ticket-generator/protocol.d.ts.map +1 -0
- package/packages/workers/block-authorship/ticket-generator/protocol.js +54 -0
- package/packages/workers/block-authorship/{ticket-generator.d.ts → ticket-generator/ticket-generator.d.ts} +4 -0
- package/packages/workers/block-authorship/ticket-generator/ticket-generator.d.ts.map +1 -0
- package/packages/workers/block-authorship/{ticket-generator.js → ticket-generator/ticket-generator.js} +19 -9
- package/packages/workers/block-authorship/ticket-generator/ticket-generator.test.d.ts.map +1 -0
- package/packages/workers/block-authorship/{ticket-generator.test.js → ticket-generator/ticket-generator.test.js} +13 -9
- package/packages/workers/block-authorship/ticket-generator/worker-pool.d.ts +36 -0
- package/packages/workers/block-authorship/ticket-generator/worker-pool.d.ts.map +1 -0
- package/packages/workers/block-authorship/ticket-generator/worker-pool.js +111 -0
- package/packages/workers/block-authorship/ticket-validator.d.ts +31 -0
- package/packages/workers/block-authorship/ticket-validator.d.ts.map +1 -0
- package/packages/workers/block-authorship/ticket-validator.js +59 -0
- package/packages/workers/comms-authorship-network/protocol.d.ts +14 -4
- package/packages/workers/comms-authorship-network/protocol.d.ts.map +1 -1
- package/packages/workers/comms-authorship-network/protocol.js +12 -6
- package/packages/workers/comms-authorship-network/tickets-message.d.ts +0 -14
- package/packages/workers/comms-authorship-network/tickets-message.d.ts.map +1 -1
- package/packages/workers/comms-authorship-network/tickets-message.js +0 -17
- package/packages/workers/importer/importer.d.ts +2 -2
- package/packages/workers/importer/importer.d.ts.map +1 -1
- package/packages/workers/importer/importer.js +5 -5
- package/packages/workers/importer/stats.d.ts +1 -3
- package/packages/workers/importer/stats.d.ts.map +1 -1
- package/packages/workers/importer/stats.js +12 -12
- package/packages/workers/jam-network/main.d.ts.map +1 -1
- package/packages/workers/jam-network/main.js +25 -4
- package/packages/workers/block-authorship/generator.d.ts.map +0 -1
- package/packages/workers/block-authorship/generator.test.d.ts +0 -2
- package/packages/workers/block-authorship/generator.test.d.ts.map +0 -1
- package/packages/workers/block-authorship/ticket-generator.d.ts.map +0 -1
- package/packages/workers/block-authorship/ticket-generator.test.d.ts.map +0 -1
- /package/packages/configs/{typeberry-dev.json → typeberry-dev-tiny.json} +0 -0
- /package/packages/workers/block-authorship/{ticket-generator.test.d.ts → ticket-generator/ticket-generator.test.d.ts} +0 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Bytes } from "#@typeberry/bytes";
|
|
2
|
+
import { HASH_SIZE } from "#@typeberry/hash";
|
|
3
|
+
import { Result } from "#@typeberry/utils";
|
|
4
|
+
/** Reasons a ticket may fail validation. */
|
|
5
|
+
export var ValidationError;
|
|
6
|
+
(function (ValidationError) {
|
|
7
|
+
/** Verifier rejected the signature / proof. */
|
|
8
|
+
ValidationError["InvalidProof"] = "invalid_proof";
|
|
9
|
+
/** Validator could not run (e.g. state unavailable, transient internal failure). */
|
|
10
|
+
ValidationError["ValidatorUnavailable"] = "validator_unavailable";
|
|
11
|
+
/** Ticket is for an epoch outside the validator's window of interest. */
|
|
12
|
+
ValidationError["WrongEpoch"] = "wrong_epoch";
|
|
13
|
+
})(ValidationError || (ValidationError = {}));
|
|
14
|
+
/**
|
|
15
|
+
* Accepts every ticket without inspection. Useful for unit tests where the validator
|
|
16
|
+
* isn't the subject under test. Must never be used in production.
|
|
17
|
+
*/
|
|
18
|
+
export class AcceptTicketsValidator {
|
|
19
|
+
async validate(_epochIndex, ticket) {
|
|
20
|
+
return Result.ok(ticket.map((ticket) => ({
|
|
21
|
+
ticket,
|
|
22
|
+
id: Bytes.zero(HASH_SIZE).asOpaque(),
|
|
23
|
+
})));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Rejects every ticket. Used as the default for any task that needs an explicit, real
|
|
28
|
+
* validator wired in before it will accept anything from the network.
|
|
29
|
+
*/
|
|
30
|
+
export class DenyTicketsValidator {
|
|
31
|
+
async validate(_epochIndex, _tickets) {
|
|
32
|
+
return Result.error(ValidationError.ValidatorUnavailable, () => "no ticket validator wired");
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ticket-validator.test.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/ticket-pool/ticket-validator.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, it } from "node:test";
|
|
3
|
+
import { tryAsEpoch } from "#@typeberry/block";
|
|
4
|
+
import { SignedTicket, tryAsTicketAttempt } from "#@typeberry/block/tickets.js";
|
|
5
|
+
import { Bytes } from "#@typeberry/bytes";
|
|
6
|
+
import { BANDERSNATCH_PROOF_BYTES } from "#@typeberry/crypto";
|
|
7
|
+
import { HASH_SIZE } from "#@typeberry/hash";
|
|
8
|
+
import { AcceptTicketsValidator, DenyTicketsValidator, ValidationError } from "./ticket-validator.js";
|
|
9
|
+
const E1 = tryAsEpoch(1);
|
|
10
|
+
function makeTicket() {
|
|
11
|
+
return SignedTicket.create({
|
|
12
|
+
attempt: tryAsTicketAttempt(0),
|
|
13
|
+
signature: Bytes.zero(BANDERSNATCH_PROOF_BYTES).asOpaque(),
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
describe("AcceptTicketsValidator", () => {
|
|
17
|
+
it("returns ok with zero id", async () => {
|
|
18
|
+
const v = new AcceptTicketsValidator();
|
|
19
|
+
const res = await v.validate(E1, [makeTicket()]);
|
|
20
|
+
assert.strictEqual(res.isOk, true);
|
|
21
|
+
if (res.isOk) {
|
|
22
|
+
assert.strictEqual(res.ok[0].id.toString(), Bytes.zero(HASH_SIZE).toString());
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
describe("DenyTicketsValidator", () => {
|
|
27
|
+
it("returns ValidatorUnavailable", async () => {
|
|
28
|
+
const v = new DenyTicketsValidator();
|
|
29
|
+
const res = await v.validate(E1, [makeTicket()]);
|
|
30
|
+
assert.strictEqual(res.isError, true);
|
|
31
|
+
if (res.isError) {
|
|
32
|
+
assert.strictEqual(res.error, ValidationError.ValidatorUnavailable);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { EntropyHash, Epoch } from "#@typeberry/block";
|
|
2
|
+
import type { SignedTicket } from "#@typeberry/block/tickets.js";
|
|
3
|
+
/** A ticket the validator already verified, paired with the entropy hash (ticket id). */
|
|
4
|
+
export type VerifiedTicket = {
|
|
5
|
+
ticket: SignedTicket;
|
|
6
|
+
id: EntropyHash;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* In-memory pool of verified tickets for the current epoch, keyed by ticket id.
|
|
10
|
+
*
|
|
11
|
+
* Used on the authorship side. Tickets are stored per epoch and deduplicated by their
|
|
12
|
+
* computed entropy hash (so duplicates arriving via different peers / paths are coalesced
|
|
13
|
+
* cheaply). The pool only ever needs to hold tickets for one epoch at a time; switching
|
|
14
|
+
* to a new epoch clears everything older.
|
|
15
|
+
*/
|
|
16
|
+
export declare class VerifiedTicketPool {
|
|
17
|
+
private readonly perEpoch;
|
|
18
|
+
private readonly idSets;
|
|
19
|
+
static new(): VerifiedTicketPool;
|
|
20
|
+
private constructor();
|
|
21
|
+
/** Add pre-verified tickets to the pool, deduping by id. */
|
|
22
|
+
add(epochIndex: Epoch, verifiedTickets: readonly VerifiedTicket[]): void;
|
|
23
|
+
/** Returns the verified tickets for the given epoch, or an empty array if none. */
|
|
24
|
+
getForEpoch(epochIndex: Epoch): readonly VerifiedTicket[];
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=verified-ticket-pool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verified-ticket-pool.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/ticket-pool/verified-ticket-pool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAGhE,yFAAyF;AACzF,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,YAAY,CAAC;IACrB,EAAE,EAAE,WAAW,CAAC;CACjB,CAAC;AAEF;;;;;;;GAOG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsC;IAC/D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0C;IAEjE,MAAM,CAAC,GAAG;IAIV,OAAO;IAEP,4DAA4D;IAC5D,GAAG,CAAC,UAAU,EAAE,KAAK,EAAE,eAAe,EAAE,SAAS,cAAc,EAAE,GAAG,IAAI;IAoBxE,mFAAmF;IACnF,WAAW,CAAC,UAAU,EAAE,KAAK,GAAG,SAAS,cAAc,EAAE;CAG1D"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { HashSet } from "#@typeberry/collections/hash-set.js";
|
|
2
|
+
/**
|
|
3
|
+
* In-memory pool of verified tickets for the current epoch, keyed by ticket id.
|
|
4
|
+
*
|
|
5
|
+
* Used on the authorship side. Tickets are stored per epoch and deduplicated by their
|
|
6
|
+
* computed entropy hash (so duplicates arriving via different peers / paths are coalesced
|
|
7
|
+
* cheaply). The pool only ever needs to hold tickets for one epoch at a time; switching
|
|
8
|
+
* to a new epoch clears everything older.
|
|
9
|
+
*/
|
|
10
|
+
export class VerifiedTicketPool {
|
|
11
|
+
perEpoch = new Map();
|
|
12
|
+
idSets = new Map();
|
|
13
|
+
static new() {
|
|
14
|
+
return new VerifiedTicketPool();
|
|
15
|
+
}
|
|
16
|
+
constructor() { }
|
|
17
|
+
/** Add pre-verified tickets to the pool, deduping by id. */
|
|
18
|
+
add(epochIndex, verifiedTickets) {
|
|
19
|
+
if (this.perEpoch.size > 0 && !this.perEpoch.has(epochIndex)) {
|
|
20
|
+
this.perEpoch.clear();
|
|
21
|
+
this.idSets.clear();
|
|
22
|
+
}
|
|
23
|
+
const existing = this.perEpoch.get(epochIndex) ?? [];
|
|
24
|
+
let idSet = this.idSets.get(epochIndex) ?? null;
|
|
25
|
+
if (idSet === null) {
|
|
26
|
+
idSet = HashSet.new();
|
|
27
|
+
this.idSets.set(epochIndex, idSet);
|
|
28
|
+
}
|
|
29
|
+
for (const entry of verifiedTickets) {
|
|
30
|
+
if (!idSet.has(entry.id)) {
|
|
31
|
+
existing.push(entry);
|
|
32
|
+
idSet.insert(entry.id);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
this.perEpoch.set(epochIndex, existing);
|
|
36
|
+
}
|
|
37
|
+
/** Returns the verified tickets for the given epoch, or an empty array if none. */
|
|
38
|
+
getForEpoch(epochIndex) {
|
|
39
|
+
return this.perEpoch.get(epochIndex) ?? [];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verified-ticket-pool.test.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/ticket-pool/verified-ticket-pool.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, it } from "node:test";
|
|
3
|
+
import { tryAsEpoch } from "#@typeberry/block";
|
|
4
|
+
import { SignedTicket, tryAsTicketAttempt } from "#@typeberry/block/tickets.js";
|
|
5
|
+
import { Bytes } from "#@typeberry/bytes";
|
|
6
|
+
import { BANDERSNATCH_PROOF_BYTES } from "#@typeberry/crypto";
|
|
7
|
+
import { HASH_SIZE } from "#@typeberry/hash";
|
|
8
|
+
import { VerifiedTicketPool } from "./verified-ticket-pool.js";
|
|
9
|
+
const E1 = tryAsEpoch(1);
|
|
10
|
+
const E2 = tryAsEpoch(2);
|
|
11
|
+
function makeTicket(seed) {
|
|
12
|
+
const sig = Bytes.zero(BANDERSNATCH_PROOF_BYTES);
|
|
13
|
+
sig.raw[0] = seed;
|
|
14
|
+
return SignedTicket.create({
|
|
15
|
+
attempt: tryAsTicketAttempt(0),
|
|
16
|
+
signature: sig.asOpaque(),
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function makeId(byte) {
|
|
20
|
+
return Bytes.fill(HASH_SIZE, byte).asOpaque();
|
|
21
|
+
}
|
|
22
|
+
describe("VerifiedTicketPool", () => {
|
|
23
|
+
it("starts empty", () => {
|
|
24
|
+
const pool = VerifiedTicketPool.new();
|
|
25
|
+
assert.deepStrictEqual(pool.getForEpoch(E1), []);
|
|
26
|
+
});
|
|
27
|
+
it("adds and retrieves tickets per epoch", () => {
|
|
28
|
+
const pool = VerifiedTicketPool.new();
|
|
29
|
+
pool.add(E1, [{ ticket: makeTicket(1), id: makeId(0xaa) }]);
|
|
30
|
+
assert.strictEqual(pool.getForEpoch(E1).length, 1);
|
|
31
|
+
assert.deepStrictEqual(pool.getForEpoch(E2), []);
|
|
32
|
+
});
|
|
33
|
+
it("dedups by id", () => {
|
|
34
|
+
const pool = VerifiedTicketPool.new();
|
|
35
|
+
const id = makeId(0x01);
|
|
36
|
+
pool.add(E1, [{ ticket: makeTicket(1), id }]);
|
|
37
|
+
pool.add(E1, [{ ticket: makeTicket(2), id }]);
|
|
38
|
+
assert.strictEqual(pool.getForEpoch(E1).length, 1);
|
|
39
|
+
assert.strictEqual(pool.getForEpoch(E1)[0].ticket.signature.raw[0], 1);
|
|
40
|
+
});
|
|
41
|
+
it("clears previous epochs when a new epoch is added", () => {
|
|
42
|
+
const pool = VerifiedTicketPool.new();
|
|
43
|
+
pool.add(E1, [{ ticket: makeTicket(1), id: makeId(1) }]);
|
|
44
|
+
pool.add(E2, [{ ticket: makeTicket(2), id: makeId(2) }]);
|
|
45
|
+
assert.deepStrictEqual(pool.getForEpoch(E1), []);
|
|
46
|
+
assert.strictEqual(pool.getForEpoch(E2).length, 1);
|
|
47
|
+
});
|
|
48
|
+
it("appends across multiple add() calls for the same epoch", () => {
|
|
49
|
+
const pool = VerifiedTicketPool.new();
|
|
50
|
+
pool.add(E1, [{ ticket: makeTicket(1), id: makeId(1) }]);
|
|
51
|
+
pool.add(E1, [{ ticket: makeTicket(2), id: makeId(2) }]);
|
|
52
|
+
assert.strictEqual(pool.getForEpoch(E1).length, 2);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -2,10 +2,12 @@ import type { MessagePort } from "node:worker_threads";
|
|
|
2
2
|
import { type Decode, type Encode } from "#@typeberry/codec";
|
|
3
3
|
import { ChainSpec } from "#@typeberry/config";
|
|
4
4
|
import { type BlocksDb, type RootDb, type SerializedStatesDb } from "#@typeberry/database";
|
|
5
|
+
import { FjallValuesSession } from "#@typeberry/database-fjall";
|
|
5
6
|
import { Blake2b } from "#@typeberry/hash";
|
|
6
7
|
import type { WorkerConfig } from "#@typeberry/workers-api";
|
|
7
8
|
import { ThreadPort, type TransferablePort } from "./port.js";
|
|
8
|
-
|
|
9
|
+
export { FjallValuesSession };
|
|
10
|
+
/** Worker config for node.js, backed by the LMDB database. */
|
|
9
11
|
export declare class LmdbWorkerConfig<T = void> implements WorkerConfig<T, BlocksDb, SerializedStatesDb> {
|
|
10
12
|
readonly nodeName: string;
|
|
11
13
|
readonly chainSpec: ChainSpec;
|
|
@@ -71,13 +73,18 @@ export declare class InMemWorkerConfig<T = undefined> implements WorkerConfig<T,
|
|
|
71
73
|
readonly: boolean;
|
|
72
74
|
}): RootDb<BlocksDb, SerializedStatesDb>;
|
|
73
75
|
}
|
|
76
|
+
/** Persistent values store backing the hybrid config. */
|
|
77
|
+
export type HybridBackend = "lmdb" | "fjall";
|
|
74
78
|
/**
|
|
75
79
|
* Hybrid worker config for the fuzz target: in-memory blocks and leaf sets,
|
|
76
|
-
* but large values persisted to
|
|
80
|
+
* but large values persisted to disk. The `backend` picks where the values go
|
|
81
|
+
* (lmdb or fjall).
|
|
82
|
+
*
|
|
83
|
+
* fjall opens its keyspace asynchronously, that is why `new` here is async.
|
|
77
84
|
*
|
|
78
85
|
* Like `InMemWorkerConfig`, the blocks and leaf sets are shared across the
|
|
79
86
|
* open/close/reopen dance that genesis init performs, so `openDatabase`
|
|
80
|
-
* returns the same instances and a no-op close. The
|
|
87
|
+
* returns the same instances and a no-op close. The values store is opened once
|
|
81
88
|
* here and closed by `HybridSerializedStates.close()` at importer teardown.
|
|
82
89
|
*
|
|
83
90
|
* In-process only: it holds shared mutable state (the in-memory leaf
|
|
@@ -92,7 +99,8 @@ export declare class HybridWorkerConfig<T = undefined> implements WorkerConfig<T
|
|
|
92
99
|
readonly dbPath: string;
|
|
93
100
|
readonly ephemeral: boolean;
|
|
94
101
|
readonly compression: boolean;
|
|
95
|
-
|
|
102
|
+
private readonly states;
|
|
103
|
+
static new<T>({ nodeName, chainSpec, workerParams, blake2b, dbPath, ephemeral, compression, backend, sharedFjallSession, }: {
|
|
96
104
|
nodeName: string;
|
|
97
105
|
chainSpec: ChainSpec;
|
|
98
106
|
workerParams: T;
|
|
@@ -100,9 +108,16 @@ export declare class HybridWorkerConfig<T = undefined> implements WorkerConfig<T
|
|
|
100
108
|
dbPath: string;
|
|
101
109
|
ephemeral?: boolean;
|
|
102
110
|
compression?: boolean;
|
|
103
|
-
|
|
111
|
+
backend?: HybridBackend;
|
|
112
|
+
/**
|
|
113
|
+
* Reuse an already-open fjall values session instead of opening a fresh
|
|
114
|
+
* keyspace. The fuzz target opens one per run and passes it on every reset,
|
|
115
|
+
* so only the in-memory blocks/leaf sets are rebuilt per vector. Ignored
|
|
116
|
+
* unless `backend === "fjall"`.
|
|
117
|
+
*/
|
|
118
|
+
sharedFjallSession?: FjallValuesSession;
|
|
119
|
+
}): Promise<HybridWorkerConfig<T>>;
|
|
104
120
|
private readonly blocks;
|
|
105
|
-
private readonly states;
|
|
106
121
|
private constructor();
|
|
107
122
|
openDatabase(_options?: {
|
|
108
123
|
readonly: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/api-node/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,KAAK,MAAM,EAAW,KAAK,MAAM,EAAW,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EACL,KAAK,QAAQ,EAGb,KAAK,MAAM,EACX,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/api-node/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,KAAK,MAAM,EAAW,KAAK,MAAM,EAAW,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EACL,KAAK,QAAQ,EAGb,KAAK,MAAM,EACX,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAyD,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAOtH,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAI9D,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAE9B,8DAA8D;AAC9D,qBAAa,gBAAgB,CAAC,CAAC,GAAG,IAAI,CAAE,YAAW,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,kBAAkB,CAAC;aAyC5E,QAAQ,EAAE,MAAM;aAChB,SAAS,EAAE,SAAS;aACpB,YAAY,EAAE,CAAC;aACf,MAAM,EAAE,MAAM;aACd,OAAO,EAAE,OAAO;aAChB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC;aAI9B,SAAS,EAAE,OAAO;IAjDpC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,EACZ,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,MAAM,EACN,OAAO,EACP,KAAiB,EACjB,SAAiB,GAClB,EAAE;QACD,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,SAAS,CAAC;QACrB,YAAY,EAAE,CAAC,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,OAAO,CAAC;QACjB,KAAK,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAChC,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB;IAID,6DAA6D;WAChD,gBAAgB,CAAC,CAAC,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,kBAAkB;IAkBpF,OAAO;IAaP,YAAY,CAAC,OAAO,GAAE;QAAE,QAAQ,EAAE,OAAO,CAAA;KAAuB,GAAG,MAAM,CAAC,QAAQ,EAAE,kBAAkB,CAAC;IAavG,6DAA6D;IAC7D,gBAAgB,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,kBAAkB;CAS7D;AAED,6DAA6D;AAC7D,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,SAAS,CAAC;IACrB,YAAY,EAAE,UAAU,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,CAAC,MAAM,EAAE,gBAAgB,CAAC,EAAE,CAAC;CAC3C,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,WAAW,EAAE,CAE5E;AAED;;;;GAIG;AACH,qBAAa,iBAAiB,CAAC,CAAC,GAAG,SAAS,CAAE,YAAW,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,kBAAkB,CAAC;aAmBlF,QAAQ,EAAE,MAAM;aAChB,SAAS,EAAE,SAAS;aACpB,YAAY,EAAE,CAAC;aACf,OAAO,EAAE,OAAO;IArBlC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,EACZ,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,OAAO,GACR,EAAE;QACD,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,SAAS,CAAC;QACrB,YAAY,EAAE,CAAC,CAAC;QAChB,OAAO,EAAE,OAAO,CAAC;KAClB;IAID,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA2B;IAElD,OAAO;IAUP,YAAY,CAAC,QAAQ,GAAE;QAAE,QAAQ,EAAE,OAAO,CAAA;KAAuB,GAAG,MAAM,CAAC,QAAQ,EAAE,kBAAkB,CAAC;CAQzG;AAED,yDAAyD;AACzD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,OAAO,CAAC;AAE7C;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,kBAAkB,CAAC,CAAC,GAAG,SAAS,CAAE,YAAW,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,kBAAkB,CAAC;aA0CnF,QAAQ,EAAE,MAAM;aAChB,SAAS,EAAE,SAAS;aACpB,YAAY,EAAE,CAAC;aACf,OAAO,EAAE,OAAO;aAChB,MAAM,EAAE,MAAM;aACd,SAAS,EAAE,OAAO;aAClB,WAAW,EAAE,OAAO;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM;WAhDZ,GAAG,CAAC,CAAC,EAAE,EAClB,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,OAAO,EACP,MAAM,EACN,SAAiB,EACjB,WAAkB,EAClB,OAAgB,EAChB,kBAAkB,GACnB,EAAE;QACD,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,SAAS,CAAC;QACrB,YAAY,EAAE,CAAC,CAAC;QAChB,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,OAAO,CAAC,EAAE,aAAa,CAAC;QACxB;;;;;WAKG;QACH,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;KACzC,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC;IAYlC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IAExC,OAAO;IAaP,YAAY,CAAC,QAAQ,GAAE;QAAE,QAAQ,EAAE,OAAO,CAAA;KAAuB,GAAG,MAAM,CAAC,QAAQ,EAAE,kBAAkB,CAAC;CASzG"}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { Decoder, Encoder } from "#@typeberry/codec";
|
|
2
2
|
import { ChainSpec } from "#@typeberry/config";
|
|
3
3
|
import { InMemoryBlocks, InMemorySerializedStates, } from "#@typeberry/database";
|
|
4
|
-
import { HybridSerializedStates
|
|
4
|
+
import { HybridSerializedStates as FjallHybridSerializedStates, FjallValuesSession } from "#@typeberry/database-fjall";
|
|
5
|
+
import { LmdbBlocks, HybridSerializedStates as LmdbHybridSerializedStates, LmdbRoot, LmdbStates, } from "#@typeberry/database-lmdb";
|
|
5
6
|
import { Blake2b } from "#@typeberry/hash";
|
|
6
7
|
import { ThreadPort } from "./port.js";
|
|
7
|
-
|
|
8
|
+
// Re-exported so the fuzz target can open one values session per run and reuse
|
|
9
|
+
// it across resets (see `HybridWorkerConfig` / `mainFuzz`).
|
|
10
|
+
export { FjallValuesSession };
|
|
11
|
+
/** Worker config for node.js, backed by the LMDB database. */
|
|
8
12
|
export class LmdbWorkerConfig {
|
|
9
13
|
nodeName;
|
|
10
14
|
chainSpec;
|
|
@@ -32,7 +36,7 @@ export class LmdbWorkerConfig {
|
|
|
32
36
|
});
|
|
33
37
|
}
|
|
34
38
|
constructor(nodeName, chainSpec, workerParams, dbPath, blake2b, ports,
|
|
35
|
-
// When set, the underlying
|
|
39
|
+
// When set, the underlying database skips fsync. Only safe for throwaway
|
|
36
40
|
// databases (the fuzz target wipes on reset). Not transferred to worker
|
|
37
41
|
// threads, so the durable main node path always gets the default.
|
|
38
42
|
ephemeral = false) {
|
|
@@ -110,11 +114,14 @@ export class InMemWorkerConfig {
|
|
|
110
114
|
}
|
|
111
115
|
/**
|
|
112
116
|
* Hybrid worker config for the fuzz target: in-memory blocks and leaf sets,
|
|
113
|
-
* but large values persisted to
|
|
117
|
+
* but large values persisted to disk. The `backend` picks where the values go
|
|
118
|
+
* (lmdb or fjall).
|
|
119
|
+
*
|
|
120
|
+
* fjall opens its keyspace asynchronously, that is why `new` here is async.
|
|
114
121
|
*
|
|
115
122
|
* Like `InMemWorkerConfig`, the blocks and leaf sets are shared across the
|
|
116
123
|
* open/close/reopen dance that genesis init performs, so `openDatabase`
|
|
117
|
-
* returns the same instances and a no-op close. The
|
|
124
|
+
* returns the same instances and a no-op close. The values store is opened once
|
|
118
125
|
* here and closed by `HybridSerializedStates.close()` at importer teardown.
|
|
119
126
|
*
|
|
120
127
|
* In-process only: it holds shared mutable state (the in-memory leaf
|
|
@@ -129,12 +136,19 @@ export class HybridWorkerConfig {
|
|
|
129
136
|
dbPath;
|
|
130
137
|
ephemeral;
|
|
131
138
|
compression;
|
|
132
|
-
|
|
133
|
-
|
|
139
|
+
states;
|
|
140
|
+
static async new({ nodeName, chainSpec, workerParams, blake2b, dbPath, ephemeral = false, compression = true, backend = "lmdb", sharedFjallSession, }) {
|
|
141
|
+
// The values store is created once here and shared across reopen. When a
|
|
142
|
+
// session is given (fuzz reset reuse) we wrap it instead of opening a new one.
|
|
143
|
+
const states = backend === "fjall"
|
|
144
|
+
? sharedFjallSession !== undefined
|
|
145
|
+
? FjallHybridSerializedStates.fromSession(chainSpec, blake2b, sharedFjallSession)
|
|
146
|
+
: await FjallHybridSerializedStates.new({ spec: chainSpec, blake2b, dbPath, ephemeral })
|
|
147
|
+
: LmdbHybridSerializedStates.new({ spec: chainSpec, blake2b, dbPath, ephemeral, compression, readOnly: false });
|
|
148
|
+
return new HybridWorkerConfig(nodeName, chainSpec, workerParams, blake2b, dbPath, ephemeral, compression, states);
|
|
134
149
|
}
|
|
135
150
|
blocks;
|
|
136
|
-
states
|
|
137
|
-
constructor(nodeName, chainSpec, workerParams, blake2b, dbPath, ephemeral, compression = true) {
|
|
151
|
+
constructor(nodeName, chainSpec, workerParams, blake2b, dbPath, ephemeral, compression, states) {
|
|
138
152
|
this.nodeName = nodeName;
|
|
139
153
|
this.chainSpec = chainSpec;
|
|
140
154
|
this.workerParams = workerParams;
|
|
@@ -142,22 +156,15 @@ export class HybridWorkerConfig {
|
|
|
142
156
|
this.dbPath = dbPath;
|
|
143
157
|
this.ephemeral = ephemeral;
|
|
144
158
|
this.compression = compression;
|
|
159
|
+
this.states = states;
|
|
145
160
|
this.blocks = InMemoryBlocks.new();
|
|
146
|
-
this.states = HybridSerializedStates.new({
|
|
147
|
-
spec: this.chainSpec,
|
|
148
|
-
blake2b: this.blake2b,
|
|
149
|
-
dbPath: this.dbPath,
|
|
150
|
-
ephemeral: this.ephemeral,
|
|
151
|
-
compression: this.compression,
|
|
152
|
-
readOnly: false,
|
|
153
|
-
});
|
|
154
161
|
}
|
|
155
162
|
openDatabase(_options = { readonly: true }) {
|
|
156
163
|
return {
|
|
157
164
|
getBlocksDb: () => this.blocks,
|
|
158
165
|
getStatesDb: () => this.states,
|
|
159
|
-
// Leaf sets and blocks live in memory; the
|
|
160
|
-
//
|
|
166
|
+
// Leaf sets and blocks live in memory; the values store is closed via
|
|
167
|
+
// states.close() at importer teardown, so this is a no-op.
|
|
161
168
|
close: async () => { },
|
|
162
169
|
};
|
|
163
170
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
|
+
import * as fs from "node:fs";
|
|
2
3
|
import { describe, it } from "node:test";
|
|
3
4
|
import { MessageChannel } from "node:worker_threads";
|
|
4
5
|
import { codec } from "#@typeberry/codec";
|
|
5
6
|
import { tinyChainSpec } from "#@typeberry/config";
|
|
6
7
|
import { Blake2b } from "#@typeberry/hash";
|
|
7
8
|
import { tryAsU32 } from "#@typeberry/numbers";
|
|
8
|
-
import { configTransferList, LmdbWorkerConfig } from "./config.js";
|
|
9
|
+
import { configTransferList, HybridWorkerConfig, LmdbWorkerConfig } from "./config.js";
|
|
9
10
|
import { ThreadPort } from "./port.js";
|
|
10
11
|
const spec = tinyChainSpec;
|
|
11
12
|
describe("LmdbWorkerConfig transfer list", () => {
|
|
@@ -39,3 +40,39 @@ describe("LmdbWorkerConfig transfer list", () => {
|
|
|
39
40
|
}
|
|
40
41
|
});
|
|
41
42
|
});
|
|
43
|
+
describe("HybridWorkerConfig", () => {
|
|
44
|
+
// Both persistent backends must construct asynchronously and hand out a
|
|
45
|
+
// working db. fjall is the experimental backend we want to benchmark.
|
|
46
|
+
for (const backend of ["lmdb", "fjall"]) {
|
|
47
|
+
it(`constructs and opens a ${backend}-backed hybrid db`, async () => {
|
|
48
|
+
const blake2b = await Blake2b.createHasher();
|
|
49
|
+
const dbPath = fs.mkdtempSync(`typeberry-hybrid-${backend}-`);
|
|
50
|
+
try {
|
|
51
|
+
const config = await HybridWorkerConfig.new({
|
|
52
|
+
nodeName: "node",
|
|
53
|
+
chainSpec: spec,
|
|
54
|
+
workerParams: undefined,
|
|
55
|
+
blake2b,
|
|
56
|
+
dbPath,
|
|
57
|
+
ephemeral: true,
|
|
58
|
+
backend,
|
|
59
|
+
});
|
|
60
|
+
const db = config.openDatabase({ readonly: false });
|
|
61
|
+
const states = db.getStatesDb();
|
|
62
|
+
try {
|
|
63
|
+
assert.notStrictEqual(db.getBlocksDb(), undefined);
|
|
64
|
+
assert.notStrictEqual(states, undefined);
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
// The values store owns the on-disk resources (the no-op db.close()
|
|
68
|
+
// does not), so close it explicitly to release the fjall keyspace.
|
|
69
|
+
await states.close();
|
|
70
|
+
await db.close();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
fs.rmSync(dbPath, { recursive: true, force: true });
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
});
|
|
@@ -18,7 +18,7 @@ import { type Opaque } from "#@typeberry/utils";
|
|
|
18
18
|
*
|
|
19
19
|
*/
|
|
20
20
|
export type BlockSealInput = Opaque<BytesBlob, "Seal">;
|
|
21
|
-
/** Construction arguments for
|
|
21
|
+
/** Construction arguments for `BlockGenerator`. */
|
|
22
22
|
export type GeneratorArgs = {
|
|
23
23
|
chainSpec: ChainSpec;
|
|
24
24
|
bandersnatch: BandernsatchWasm;
|
|
@@ -27,7 +27,7 @@ export type GeneratorArgs = {
|
|
|
27
27
|
blocks: BlocksDb;
|
|
28
28
|
states: StatesDb;
|
|
29
29
|
};
|
|
30
|
-
export declare class
|
|
30
|
+
export declare class BlockGenerator {
|
|
31
31
|
private readonly metrics;
|
|
32
32
|
readonly chainSpec: ChainSpec;
|
|
33
33
|
readonly bandersnatch: BandernsatchWasm;
|
|
@@ -35,8 +35,8 @@ export declare class Generator {
|
|
|
35
35
|
readonly blake2b: Blake2b;
|
|
36
36
|
private readonly blocks;
|
|
37
37
|
private readonly states;
|
|
38
|
-
/** Build a
|
|
39
|
-
static new(args: GeneratorArgs):
|
|
38
|
+
/** Build a block generator from its collaborators. */
|
|
39
|
+
static new(args: GeneratorArgs): BlockGenerator;
|
|
40
40
|
private constructor();
|
|
41
41
|
private getLastHeaderAndState;
|
|
42
42
|
nextBlockView(validatorIndex: ValidatorIndex, bandersnatchSecret: BandersnatchSecretSeed, sealPayload: BlockSealInput, timeSlot: TimeSlot, pendingTickets?: {
|
|
@@ -71,4 +71,4 @@ export declare class Generator {
|
|
|
71
71
|
id: EntropyHash;
|
|
72
72
|
}[]): Promise<Block>;
|
|
73
73
|
}
|
|
74
|
-
//# sourceMappingURL=generator.d.ts.map
|
|
74
|
+
//# sourceMappingURL=block-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-generator.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/block-authorship/block-generator.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EACL,KAAK,WAAW,EAIhB,KAAK,QAAQ,EACb,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,KAAK,SAAS,EAAa,MAAM,2BAA2B,CAAC;AAEtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAS,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAoC,KAAK,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAClG,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAIvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAGhF,OAAO,EAAqB,KAAK,MAAM,EAAU,MAAM,kBAAkB,CAAC;AAM1E;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AAEvD,mDAAmD;AACnD,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,EAAE,SAAS,CAAC;IACrB,YAAY,EAAE,gBAAgB,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC,YAAY,CAAC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,QAAQ,CAAC;IACjB,MAAM,EAAE,QAAQ,CAAC;CAClB,CAAC;AAEF,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2C;IAEnE,SAAgB,SAAS,EAAE,SAAS,CAAC;IACrC,SAAgB,YAAY,EAAE,gBAAgB,CAAC;IAC/C,SAAgB,YAAY,EAAE,MAAM,CAAC,YAAY,CAAC;IAClD,SAAgB,OAAO,EAAE,OAAO,CAAC;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAW;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAW;IAElC,sDAAsD;IACtD,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,aAAa;IAI9B,OAAO;IAUP,OAAO,CAAC,qBAAqB;IAYvB,aAAa,CACjB,cAAc,EAAE,cAAc,EAC9B,kBAAkB,EAAE,sBAAsB,EAC1C,WAAW,EAAE,cAAc,EAC3B,QAAQ,EAAE,QAAQ,EAClB,cAAc,GAAE;QAAE,MAAM,EAAE,YAAY,CAAC;QAAC,EAAE,EAAE,WAAW,CAAA;KAAE,EAAO,GAC/D,OAAO,CAAC,SAAS,CAAC;IAKrB;;;;;;;;;OASG;YACW,cAAc;IAiB5B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,uBAAuB;IA4BzB,SAAS,CACb,cAAc,EAAE,cAAc,EAC9B,kBAAkB,EAAE,sBAAsB,EAC1C,WAAW,EAAE,cAAc,EAC3B,QAAQ,EAAE,QAAQ,EAClB,cAAc,GAAE;QAAE,MAAM,EAAE,YAAY,CAAC;QAAC,EAAE,EAAE,WAAW,CAAA;KAAE,EAAO;CAqGnE"}
|
|
@@ -13,7 +13,7 @@ import { asOpaqueType, now, Result } from "#@typeberry/utils";
|
|
|
13
13
|
import * as metrics from "./metrics.js";
|
|
14
14
|
const EMPTY_AUX_DATA = BytesBlob.empty();
|
|
15
15
|
const logger = Logger.new(import.meta.filename, "author");
|
|
16
|
-
export class
|
|
16
|
+
export class BlockGenerator {
|
|
17
17
|
metrics;
|
|
18
18
|
chainSpec;
|
|
19
19
|
bandersnatch;
|
|
@@ -21,9 +21,9 @@ export class Generator {
|
|
|
21
21
|
blake2b;
|
|
22
22
|
blocks;
|
|
23
23
|
states;
|
|
24
|
-
/** Build a
|
|
24
|
+
/** Build a block generator from its collaborators. */
|
|
25
25
|
static new(args) {
|
|
26
|
-
return new
|
|
26
|
+
return new BlockGenerator(args);
|
|
27
27
|
}
|
|
28
28
|
constructor(args) {
|
|
29
29
|
this.chainSpec = args.chainSpec;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-generator.test.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/block-authorship/block-generator.test.ts"],"names":[],"mappings":""}
|
|
@@ -13,7 +13,7 @@ import { JAM_FALLBACK_SEAL } from "#@typeberry/safrole/constants.js";
|
|
|
13
13
|
import { VALIDATOR_META_BYTES, ValidatorData } from "#@typeberry/state";
|
|
14
14
|
import { SafroleSealingKeysKind } from "#@typeberry/state/safrole-data.js";
|
|
15
15
|
import { asOpaqueType, deepEqual, Result } from "#@typeberry/utils";
|
|
16
|
-
import {
|
|
16
|
+
import { BlockGenerator } from "./block-generator.js";
|
|
17
17
|
// Test validator data - need 6 validators to match tinyChainSpec.validatorsCount
|
|
18
18
|
const validatorDataArray = [
|
|
19
19
|
{
|
|
@@ -157,7 +157,7 @@ describe("Generator", () => {
|
|
|
157
157
|
const state = createMockState(0);
|
|
158
158
|
const blocksDb = createMockBlocksDb(MOCK_PARENT_HASH);
|
|
159
159
|
const statesDb = createMockStatesDb(state);
|
|
160
|
-
const generator =
|
|
160
|
+
const generator = BlockGenerator.new({
|
|
161
161
|
chainSpec: tinyChainSpec,
|
|
162
162
|
bandersnatch,
|
|
163
163
|
keccakHasher,
|
|
@@ -180,7 +180,7 @@ describe("Generator", () => {
|
|
|
180
180
|
const state = createMockState(0);
|
|
181
181
|
const blocksDb = createMockBlocksDb(MOCK_PARENT_HASH);
|
|
182
182
|
const statesDb = createMockStatesDb(state);
|
|
183
|
-
const generator =
|
|
183
|
+
const generator = BlockGenerator.new({
|
|
184
184
|
chainSpec: tinyChainSpec,
|
|
185
185
|
bandersnatch,
|
|
186
186
|
keccakHasher,
|
|
@@ -225,7 +225,7 @@ describe("Generator", () => {
|
|
|
225
225
|
const state = createMockState(9);
|
|
226
226
|
const blocksDb = createMockBlocksDb(MOCK_PARENT_HASH);
|
|
227
227
|
const statesDb = createMockStatesDb(state);
|
|
228
|
-
const generator =
|
|
228
|
+
const generator = BlockGenerator.new({
|
|
229
229
|
chainSpec: tinyChainSpec,
|
|
230
230
|
bandersnatch,
|
|
231
231
|
keccakHasher,
|
|
@@ -262,7 +262,7 @@ describe("Generator", () => {
|
|
|
262
262
|
};
|
|
263
263
|
const blocksDb = createMockBlocksDb(MOCK_PARENT_HASH);
|
|
264
264
|
const statesDb = createMockStatesDb(state);
|
|
265
|
-
const generator =
|
|
265
|
+
const generator = BlockGenerator.new({
|
|
266
266
|
chainSpec: tinyChainSpec,
|
|
267
267
|
bandersnatch,
|
|
268
268
|
keccakHasher,
|
|
@@ -300,7 +300,7 @@ describe("Generator", () => {
|
|
|
300
300
|
const state = createMockState(0);
|
|
301
301
|
const blocksDb = createMockBlocksDb(MOCK_PARENT_HASH);
|
|
302
302
|
const statesDb = createMockStatesDb(state);
|
|
303
|
-
const generator =
|
|
303
|
+
const generator = BlockGenerator.new({
|
|
304
304
|
chainSpec: tinyChainSpec,
|
|
305
305
|
bandersnatch,
|
|
306
306
|
keccakHasher,
|
|
@@ -338,7 +338,7 @@ describe("Generator", () => {
|
|
|
338
338
|
const state = createMockState(0);
|
|
339
339
|
const blocksDb = createMockBlocksDb(MOCK_PARENT_HASH);
|
|
340
340
|
const statesDb = createMockStatesDb(state);
|
|
341
|
-
const generator =
|
|
341
|
+
const generator = BlockGenerator.new({
|
|
342
342
|
chainSpec: tinyChainSpec,
|
|
343
343
|
bandersnatch,
|
|
344
344
|
keccakHasher,
|
|
@@ -384,7 +384,7 @@ describe("Generator", () => {
|
|
|
384
384
|
const state = createMockState(lastSlotOfEpoch0);
|
|
385
385
|
const blocksDb = createMockBlocksDb(MOCK_PARENT_HASH);
|
|
386
386
|
const statesDb = createMockStatesDb(state);
|
|
387
|
-
const generator =
|
|
387
|
+
const generator = BlockGenerator.new({
|
|
388
388
|
chainSpec: tinyChainSpec,
|
|
389
389
|
bandersnatch,
|
|
390
390
|
keccakHasher,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { EntropyHash } from "#@typeberry/block";
|
|
2
|
+
import type { ChainSpec } from "#@typeberry/config";
|
|
3
|
+
import type { BandersnatchKey, BandersnatchSecretSeed, Ed25519Key, Ed25519SecretSeed } from "#@typeberry/crypto";
|
|
4
|
+
import type { BandernsatchWasm } from "#@typeberry/safrole/bandersnatch-wasm.js";
|
|
5
|
+
import { type SafroleSealingKeys } from "#@typeberry/state";
|
|
6
|
+
import type { BlockSealInput } from "./block-generator.js";
|
|
7
|
+
import type { ValidatorSecrets } from "./protocol.js";
|
|
8
|
+
type ValidatorPrivateKeys = {
|
|
9
|
+
bandersnatchSecret: BandersnatchSecretSeed;
|
|
10
|
+
ed25519Secret: Ed25519SecretSeed;
|
|
11
|
+
};
|
|
12
|
+
type ValidatorPublicKeys = {
|
|
13
|
+
bandersnatchPublic: BandersnatchKey;
|
|
14
|
+
ed25519Public: Ed25519Key;
|
|
15
|
+
};
|
|
16
|
+
type ValidatorKeys = ValidatorPrivateKeys & ValidatorPublicKeys;
|
|
17
|
+
export type SlotSealData = {
|
|
18
|
+
key: ValidatorKeys;
|
|
19
|
+
sealPayload: BlockSealInput;
|
|
20
|
+
logId: string;
|
|
21
|
+
};
|
|
22
|
+
/** A helper class to figure out in which slots in the next epoch we are authoring blocks. */
|
|
23
|
+
export declare class EpochAuthoringSlots {
|
|
24
|
+
private readonly ticketsPerValidator;
|
|
25
|
+
private readonly bandersnatch;
|
|
26
|
+
private readonly keys;
|
|
27
|
+
static new(chainSpec: ChainSpec, bandersnatch: BandernsatchWasm, ownedSecrets: readonly ValidatorSecrets[]): Promise<EpochAuthoringSlots>;
|
|
28
|
+
private constructor();
|
|
29
|
+
getValidatorKeys(): ValidatorKeys[];
|
|
30
|
+
getBandersnatchPublicKeys(): (import("@typeberry/bytes").Bytes<32> & import("@typeberry/utils").WithOpaque<"BandersnatchKey">)[];
|
|
31
|
+
getOurSlotsInKeySeries(sealingKeySeries: SafroleSealingKeys, entropy: EntropyHash): Promise<Array<SlotSealData | null>>;
|
|
32
|
+
private getOwnTicketIds;
|
|
33
|
+
}
|
|
34
|
+
export {};
|
|
35
|
+
//# sourceMappingURL=epoch-authoring-slots.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"epoch-authoring-slots.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/block-authorship/epoch-authoring-slots.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,sBAAsB,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGhH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAEhF,OAAO,EAAE,KAAK,kBAAkB,EAA0B,MAAM,kBAAkB,CAAC;AAEnF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,KAAK,oBAAoB,GAAG;IAC1B,kBAAkB,EAAE,sBAAsB,CAAC;IAC3C,aAAa,EAAE,iBAAiB,CAAC;CAClC,CAAC;AAEF,KAAK,mBAAmB,GAAG;IACzB,kBAAkB,EAAE,eAAe,CAAC;IACpC,aAAa,EAAE,UAAU,CAAC;CAC3B,CAAC;AAEF,KAAK,aAAa,GAAG,oBAAoB,GAAG,mBAAmB,CAAC;AAEhE,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,aAAa,CAAC;IACnB,WAAW,EAAE,cAAc,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,6FAA6F;AAC7F,qBAAa,mBAAmB;IAY5B,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,IAAI;WAbV,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,SAAS,gBAAgB,EAAE;IAUhH,OAAO;IAMP,gBAAgB;IAIhB,yBAAyB;IAInB,sBAAsB,CAC1B,gBAAgB,EAAE,kBAAkB,EACpC,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;YAuBxB,eAAe;CAmB9B"}
|