@typeberry/lib 0.8.4-2e4ce67 → 0.8.4-9502983

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typeberry/lib",
3
- "version": "0.8.4-2e4ce67",
3
+ "version": "0.8.4-9502983",
4
4
  "description": "Typeberry Library",
5
5
  "main": "./bin/lib/index.js",
6
6
  "types": "./bin/lib/index.d.ts",
@@ -267,7 +267,7 @@
267
267
  "@fluffylabs/anan-as": "^1.4.0",
268
268
  "@noble/ed25519": "2.2.3",
269
269
  "hash-wasm": "4.12.0",
270
- "@typeberry/native": "0.5.1",
270
+ "@typeberry/native": "0.3.0-5dae93e",
271
271
  "eventemitter3": "^5.0.1",
272
272
  "@opentelemetry/api": "1.9.0"
273
273
  },
@@ -21,8 +21,8 @@ declare function verifySeal(bandersnatch: BandernsatchWasm, authorKey: Bandersna
21
21
  declare function getRingCommitment(bandersnatch: BandernsatchWasm, validators: BandersnatchKey[]): Promise<Result<BandersnatchRingRoot, null>>;
22
22
  declare function verifyTickets(bandersnatch: BandernsatchWasm, numberOfValidators: number, epochRoot: BandersnatchRingRoot, tickets: readonly SignedTicket[], entropy: EntropyHash): Promise<{
23
23
  isValid: boolean;
24
- tickets: EntropyHash[];
25
- }>;
24
+ entropyHash: EntropyHash;
25
+ }[]>;
26
26
  declare function generateSeal(bandersnatch: BandernsatchWasm, authorKey: BandersnatchSecretSeed, input: BytesBlob, auxData: BytesBlob): Promise<Result<BandersnatchVrfSignature, null>>;
27
27
  export type VrfOutputHash = Opaque<OpaqueHash, "VRF Output Hash">;
28
28
  declare function getVrfOutputHash(bandersnatch: BandernsatchWasm, authorKey: BandersnatchSecretSeed, input: BytesBlob): Promise<Result<VrfOutputHash, null>>;
@@ -1 +1 @@
1
- {"version":3,"file":"bandersnatch-vrf.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/safrole/bandersnatch-vrf.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAsB,MAAM,6BAA6B,CAAC;AAC/E,OAAO,EAAS,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AACjF,OAAO,EAIL,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC9B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAwC/D,QAAA,MAAM,SAAS;;;;;;;;CAQd,CAAC;AAKF,eAAe,SAAS,CAAC;AAIzB,iBAAe,iBAAiB,CAC9B,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,eAAe,EAC1B,SAAS,EAAE,wBAAwB,EACnC,OAAO,EAAE,SAAS,EAClB,qBAAqB,EAAE,SAAS,EAChC,gBAAgB,EAAE,wBAAwB,EAC1C,oBAAoB,EAAE,SAAS,GAC9B,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,WAAW,CAAC,EAAE,IAAI,CAAC,CAAC,CAkBnD;AAED,iBAAe,UAAU,CACvB,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,eAAe,EAC1B,SAAS,EAAE,wBAAwB,EACnC,OAAO,EAAE,SAAS,EAClB,qBAAqB,EAAE,SAAS,GAC/B,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,CAapC;AAED,iBAAS,iBAAiB,CACxB,YAAY,EAAE,gBAAgB,EAC9B,UAAU,EAAE,eAAe,EAAE,GAC5B,OAAO,CAAC,MAAM,CAAC,oBAAoB,EAAE,IAAI,CAAC,CAAC,CAe7C;AAgBD,iBAAe,aAAa,CAC1B,YAAY,EAAE,gBAAgB,EAC9B,kBAAkB,EAAE,MAAM,EAC1B,SAAS,EAAE,oBAAoB,EAC/B,OAAO,EAAE,SAAS,YAAY,EAAE,EAChC,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,WAAW,EAAE,CAAA;CAAE,CAAC,CA0BvD;AAGD,iBAAe,YAAY,CACzB,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,sBAAsB,EACjC,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,SAAS,GACjB,OAAO,CAAC,MAAM,CAAC,wBAAwB,EAAE,IAAI,CAAC,CAAC,CAQjD;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAC;AAGlE,iBAAe,gBAAgB,CAC7B,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,sBAAsB,EACjC,KAAK,EAAE,SAAS,GACf,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC,CAQtC;AAKD;;GAEG;AACH,iBAAe,eAAe,CAC5B,YAAY,EAAE,gBAAgB,EAC9B,QAAQ,EAAE,eAAe,EAAE,EAC3B,cAAc,EAAE,MAAM,EACtB,GAAG,EAAE,sBAAsB,EAC3B,OAAO,EAAE,WAAW,EACpB,mBAAmB,EAAE,MAAM,GAC1B,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE,IAAI,CAAC,CAAC,CA0CvC"}
1
+ {"version":3,"file":"bandersnatch-vrf.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/safrole/bandersnatch-vrf.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAsB,MAAM,6BAA6B,CAAC;AAC/E,OAAO,EAAS,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AACjF,OAAO,EAIL,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC9B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAwC/D,QAAA,MAAM,SAAS;;;;;;;;CAQd,CAAC;AAKF,eAAe,SAAS,CAAC;AAIzB,iBAAe,iBAAiB,CAC9B,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,eAAe,EAC1B,SAAS,EAAE,wBAAwB,EACnC,OAAO,EAAE,SAAS,EAClB,qBAAqB,EAAE,SAAS,EAChC,gBAAgB,EAAE,wBAAwB,EAC1C,oBAAoB,EAAE,SAAS,GAC9B,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,WAAW,CAAC,EAAE,IAAI,CAAC,CAAC,CAkBnD;AAED,iBAAe,UAAU,CACvB,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,eAAe,EAC1B,SAAS,EAAE,wBAAwB,EACnC,OAAO,EAAE,SAAS,EAClB,qBAAqB,EAAE,SAAS,GAC/B,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,CAapC;AAED,iBAAS,iBAAiB,CACxB,YAAY,EAAE,gBAAgB,EAC9B,UAAU,EAAE,eAAe,EAAE,GAC5B,OAAO,CAAC,MAAM,CAAC,oBAAoB,EAAE,IAAI,CAAC,CAAC,CAe7C;AAmBD,iBAAe,aAAa,CAC1B,YAAY,EAAE,gBAAgB,EAC9B,kBAAkB,EAAE,MAAM,EAC1B,SAAS,EAAE,oBAAoB,EAC/B,OAAO,EAAE,SAAS,YAAY,EAAE,EAChC,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,WAAW,CAAA;CAAE,EAAE,CAAC,CAqB3D;AAGD,iBAAe,YAAY,CACzB,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,sBAAsB,EACjC,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,SAAS,GACjB,OAAO,CAAC,MAAM,CAAC,wBAAwB,EAAE,IAAI,CAAC,CAAC,CAQjD;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAC;AAGlE,iBAAe,gBAAgB,CAC7B,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,sBAAsB,EACjC,KAAK,EAAE,SAAS,GACf,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC,CAQtC;AAKD;;GAEG;AACH,iBAAe,eAAe,CAC5B,YAAY,EAAE,gBAAgB,EAC9B,QAAQ,EAAE,eAAe,EAAE,EAC3B,cAAc,EAAE,MAAM,EACtB,GAAG,EAAE,sBAAsB,EAC3B,OAAO,EAAE,WAAW,EACpB,mBAAmB,EAAE,MAAM,GAC1B,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE,IAAI,CAAC,CAAC,CA0CvC"}
@@ -88,20 +88,17 @@ async function getRingCommitmentNoCache(bandersnatch, keys) {
88
88
  }
89
89
  return Result.ok(Bytes.fromBlob(commitmentResult.subarray(1), BANDERSNATCH_RING_ROOT_BYTES).asOpaque());
90
90
  }
91
+ // One byte for result discriminator (`ResultValues`) and the rest is entropy hash.
92
+ const TICKET_RESULT_LENGTH = 1 + HASH_SIZE;
91
93
  async function verifyTickets(bandersnatch, numberOfValidators, epochRoot, tickets, entropy) {
92
94
  const contextLength = entropy.length + JAM_TICKET_SEAL.length + 1;
93
95
  const ticketsData = BytesBlob.blobFromParts(tickets.map((ticket) => BytesBlob.blobFromParts([ticket.signature.raw, JAM_TICKET_SEAL, entropy.raw, Uint8Array.of(ticket.attempt)])
94
96
  .raw)).raw;
95
97
  const verificationResult = await bandersnatch.batchVerifyTicket(numberOfValidators, epochRoot.raw, ticketsData, contextLength);
96
- const isValid = verificationResult[RESULT_INDEX] === ResultValues.Ok;
97
- // NOTE: in case of failure, the hashes will be all zeros, but we can safely
98
- // keep the same code path.
99
- const chunks = BytesBlob.blobFrom(verificationResult.subarray(1)).chunks(HASH_SIZE);
100
- const results = [];
101
- for (const entropyHash of chunks) {
102
- results.push(Bytes.fromBlob(entropyHash.raw, HASH_SIZE).asOpaque());
103
- }
104
- return { isValid, tickets: results };
98
+ return Array.from(BytesBlob.blobFrom(verificationResult).chunks(TICKET_RESULT_LENGTH)).map((result) => ({
99
+ isValid: result.raw[RESULT_INDEX] === ResultValues.Ok,
100
+ entropyHash: Bytes.fromBlob(result.raw.subarray(1, TICKET_RESULT_LENGTH), HASH_SIZE).asOpaque(),
101
+ }));
105
102
  }
106
103
  const SEAL_FAILED_ERROR = () => "Seal generation failed";
107
104
  async function generateSeal(bandersnatch, authorKey, input, auxData) {
@@ -71,8 +71,8 @@ describe("Bandersnatch verification", () => {
71
71
  "0x3a5d10abc80dda33fe3f40b3bb2e3eefd3e97dda3d617a860c9d94eb70b832ad",
72
72
  ].map((x) => Bytes.parseBytes(x, HASH_SIZE));
73
73
  const result = await bandersnatchVrf.verifyTickets(await bandersnatchWasm, bandersnatchKeys.length, commitment, tickets, entropy);
74
- assert.strictEqual(result.isValid, true);
75
- assert.deepStrictEqual(result.tickets.map((x) => x.toString()), expectedIds.map((x) => x.toString()));
74
+ assert.strictEqual(result.every((x) => x.isValid), true);
75
+ assert.deepStrictEqual(result.map((x) => x.entropyHash.toString()), expectedIds.map((x) => x.toString()));
76
76
  });
77
77
  it("should detect that one signature is incorrect", async () => {
78
78
  const tickets = [
@@ -90,16 +90,14 @@ describe("Bandersnatch verification", () => {
90
90
  },
91
91
  ];
92
92
  const entropy = Bytes.parseBytes("0xbb30a42c1e62f0afda5f0a4e8a562f7a13a24cea00ee81917b86b89e801314aa", HASH_SIZE).asOpaque();
93
- // Batch verification fails as a whole when any signature is invalid,
94
- // and in that case all returned entropy hashes are zeroed out.
95
93
  const expectedIds = [
96
94
  "0x0000000000000000000000000000000000000000000000000000000000000000",
97
- "0x0000000000000000000000000000000000000000000000000000000000000000",
98
- "0x0000000000000000000000000000000000000000000000000000000000000000",
95
+ "0x13fecb426e0a73b84b58b9a0832b11582dc971e79c5399e69f0baf1a244c7787",
96
+ "0x3a5d10abc80dda33fe3f40b3bb2e3eefd3e97dda3d617a860c9d94eb70b832ad",
99
97
  ].map((x) => Bytes.parseBytes(x, HASH_SIZE));
100
98
  const result = await bandersnatchVrf.verifyTickets(await bandersnatchWasm, bandersnatchKeys.length, commitment, tickets, entropy);
101
- assert.strictEqual(result.isValid, false);
102
- assert.deepStrictEqual(result.tickets.map((x) => x.toString()), expectedIds.map((x) => x.toString()));
99
+ assert.deepStrictEqual(result.map((x) => x.isValid), [false, true, true]);
100
+ assert.deepStrictEqual(result.map((x) => x.entropyHash.toString()), expectedIds.map((x) => x.toString()));
103
101
  });
104
102
  });
105
103
  describe("verifySeal", () => {
@@ -170,7 +168,7 @@ describe("Bandersnatch verification", () => {
170
168
  const commitment = await bandersnatchVrf.getRingCommitment(await bandersnatchWasm, ringKeys);
171
169
  assert.ok(commitment.isOk);
172
170
  const verifyResult = await bandersnatchVrf.verifyTickets(await bandersnatchWasm, ringKeys.length, commitment.ok, genResult.ok, entropy);
173
- assert.ok(verifyResult.isValid, "Generated tickets should pass verification");
171
+ assert.ok(verifyResult.every((r) => r.isValid), "Generated tickets should pass verification");
174
172
  });
175
173
  });
176
174
  });
@@ -260,15 +260,15 @@ export class Safrole {
260
260
  */
261
261
  // TODO [ToDr] Verify that ticket attempt is in correct range.
262
262
  const verificationResult = extrinsic.length === 0
263
- ? { isValid: true, tickets: [] }
263
+ ? []
264
264
  : await bandersnatchVrf.verifyTickets(await this.bandersnatch, validators.length, epochRoot, extrinsic, entropy);
265
- if (!verificationResult.isValid) {
266
- return Result.error(SafroleErrorCode.BadTicketProof, () => "Safrole: invalid ticket proof in extrinsic");
267
- }
268
265
  const tickets = extrinsic.map((ticket, i) => ({
269
- id: verificationResult.tickets[i],
266
+ id: verificationResult[i].entropyHash,
270
267
  attempt: ticket.attempt,
271
268
  }));
269
+ if (!verificationResult.every((x) => x.isValid)) {
270
+ return Result.error(SafroleErrorCode.BadTicketProof, () => "Safrole: invalid ticket proof in extrinsic");
271
+ }
272
272
  /**
273
273
  * Verify if tickets are sorted and unique
274
274
  *
@@ -67,10 +67,10 @@ const fakeSealingKeys = {
67
67
  describe("Safrole", () => {
68
68
  let blake2b;
69
69
  beforeEach(async () => {
70
- mock.method(bandersnatchVrf, "verifyTickets", () => Promise.resolve({
71
- isValid: true,
72
- tickets: [Bytes.zero(HASH_SIZE), Bytes.fill(HASH_SIZE, 1)],
73
- }));
70
+ mock.method(bandersnatchVrf, "verifyTickets", () => Promise.resolve([
71
+ { isValid: true, entropyHash: Bytes.zero(HASH_SIZE) },
72
+ { isValid: true, entropyHash: Bytes.fill(HASH_SIZE, 1) },
73
+ ]));
74
74
  blake2b = await Blake2b.createHasher();
75
75
  });
76
76
  afterEach(() => {
@@ -146,7 +146,7 @@ describe("Safrole", () => {
146
146
  }
147
147
  });
148
148
  it("should return bad ticket proof error", async () => {
149
- mock.method(bandersnatchVrf, "verifyTickets", () => Promise.resolve({ isValid: false, tickets: [Bytes.zero(HASH_SIZE)] }));
149
+ mock.method(bandersnatchVrf, "verifyTickets", () => Promise.resolve([{ isValid: false, entropyHash: Bytes.zero(HASH_SIZE) }]));
150
150
  const punishSet = SortedSet.fromArray(hashComparator);
151
151
  const state = {
152
152
  timeslot: tryAsTimeSlot(1),
@@ -188,10 +188,10 @@ describe("Safrole", () => {
188
188
  }
189
189
  });
190
190
  it("should return duplicated ticket error", async () => {
191
- mock.method(bandersnatchVrf, "verifyTickets", () => Promise.resolve({
192
- isValid: true,
193
- tickets: [Bytes.zero(HASH_SIZE), Bytes.zero(HASH_SIZE)],
194
- }));
191
+ mock.method(bandersnatchVrf, "verifyTickets", () => Promise.resolve([
192
+ { isValid: true, entropyHash: Bytes.zero(HASH_SIZE) },
193
+ { isValid: true, entropyHash: Bytes.zero(HASH_SIZE) },
194
+ ]));
195
195
  const punishSet = SortedSet.fromArray(hashComparator);
196
196
  const state = {
197
197
  timeslot: tryAsTimeSlot(1),
@@ -237,10 +237,10 @@ describe("Safrole", () => {
237
237
  }
238
238
  });
239
239
  it("should return bad ticket order error", async () => {
240
- mock.method(bandersnatchVrf, "verifyTickets", () => Promise.resolve({
241
- isValid: true,
242
- tickets: [Bytes.fill(HASH_SIZE, 1), Bytes.zero(HASH_SIZE)],
243
- }));
240
+ mock.method(bandersnatchVrf, "verifyTickets", () => Promise.resolve([
241
+ { isValid: true, entropyHash: Bytes.fill(HASH_SIZE, 1) },
242
+ { isValid: true, entropyHash: Bytes.zero(HASH_SIZE) },
243
+ ]));
244
244
  const punishSet = SortedSet.fromArray(hashComparator);
245
245
  const state = {
246
246
  timeslot: tryAsTimeSlot(1),
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/block-authorship/main.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAiB3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,KAAK,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAK9E,KAAK,MAAM,GAAG,YAAY,CAAC,qBAAqB,CAAC,CAAC;AAwBlD,wBAAsB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,iBAmapG"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/block-authorship/main.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAiB3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,KAAK,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAK9E,KAAK,MAAM,GAAG,YAAY,CAAC,qBAAqB,CAAC,CAAC;AAwBlD,wBAAsB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,iBAiapG"}
@@ -232,15 +232,13 @@ export async function main(config, comms, networkingComms) {
232
232
  */
233
233
  async function verifyAndAddToPool(epochIndex, tickets, state) {
234
234
  const results = await bandersnatchVrf.verifyTickets(bandersnatch, state.designatedValidatorData.length, state.epochRoot, tickets, getTicketEntropy(epochIndex, state));
235
- if (results.tickets.length !== tickets.length) {
236
- logger.error `verifyTickets returned ${results.tickets.length} results for ${tickets.length} tickets`;
235
+ if (results.length !== tickets.length) {
236
+ logger.error `verifyTickets returned ${results.length} results for ${tickets.length} tickets`;
237
237
  return false;
238
238
  }
239
- // Batch verification: either the whole batch is valid or none of the tickets are.
240
- if (!results.isValid) {
241
- return false;
242
- }
243
- const verified = tickets.map((ticket, i) => ({ ticket, id: results.tickets[i] }));
239
+ const verified = tickets
240
+ .map((ticket, i) => ({ ticket, id: results[i].entropyHash }))
241
+ .filter((_, i) => results[i].isValid);
244
242
  addToPool(epochIndex, verified);
245
243
  return verified.length > 0;
246
244
  }