@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 +2 -2
- package/packages/jam/safrole/bandersnatch-vrf.d.ts +2 -2
- package/packages/jam/safrole/bandersnatch-vrf.d.ts.map +1 -1
- package/packages/jam/safrole/bandersnatch-vrf.js +6 -9
- package/packages/jam/safrole/bandersnatch-vrf.test.js +7 -9
- package/packages/jam/safrole/safrole.js +5 -5
- package/packages/jam/safrole/safrole.test.js +13 -13
- package/packages/workers/block-authorship/main.d.ts.map +1 -1
- package/packages/workers/block-authorship/main.js +5 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typeberry/lib",
|
|
3
|
-
"version": "0.8.4-
|
|
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.
|
|
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
|
-
|
|
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;
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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.
|
|
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
|
-
"
|
|
98
|
-
"
|
|
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.
|
|
102
|
-
assert.deepStrictEqual(result.
|
|
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
|
-
?
|
|
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
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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.
|
|
236
|
-
logger.error `verifyTickets returned ${results.
|
|
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
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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
|
}
|