@typeberry/lib 0.5.10-6cb1bd5 → 0.5.10-7338c21

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.
Files changed (71) hide show
  1. package/package.json +1 -1
  2. package/packages/core/codec/encoder.d.ts +1 -1
  3. package/packages/core/codec/encoder.d.ts.map +1 -1
  4. package/packages/core/codec/encoder.js +3 -2
  5. package/packages/core/pvm-interface/pvm.d.ts +2 -0
  6. package/packages/core/pvm-interface/pvm.d.ts.map +1 -1
  7. package/packages/jam/block/work-package.d.ts +7 -7
  8. package/packages/jam/block/work-package.d.ts.map +1 -1
  9. package/packages/jam/block/work-package.js +12 -12
  10. package/packages/jam/executor/pvm-executor.d.ts +9 -2
  11. package/packages/jam/executor/pvm-executor.d.ts.map +1 -1
  12. package/packages/jam/executor/pvm-executor.js +15 -0
  13. package/packages/jam/in-core/externalities/refine.d.ts +18 -8
  14. package/packages/jam/in-core/externalities/refine.d.ts.map +1 -1
  15. package/packages/jam/in-core/externalities/refine.js +86 -7
  16. package/packages/jam/in-core/externalities/refine.test.js +167 -2
  17. package/packages/jam/in-core/in-core.d.ts +7 -22
  18. package/packages/jam/in-core/in-core.d.ts.map +1 -1
  19. package/packages/jam/in-core/in-core.js +16 -186
  20. package/packages/jam/in-core/in-core.test.js +47 -15
  21. package/packages/jam/in-core/is-authorized.d.ts +33 -0
  22. package/packages/jam/in-core/is-authorized.d.ts.map +1 -0
  23. package/packages/jam/in-core/is-authorized.js +72 -0
  24. package/packages/jam/in-core/is-authorized.test.d.ts +2 -0
  25. package/packages/jam/in-core/is-authorized.test.d.ts.map +1 -0
  26. package/packages/jam/in-core/is-authorized.test.js +125 -0
  27. package/packages/jam/in-core/refine.d.ts +34 -0
  28. package/packages/jam/in-core/refine.d.ts.map +1 -0
  29. package/packages/jam/in-core/refine.js +176 -0
  30. package/packages/jam/in-core/refine.test.d.ts +2 -0
  31. package/packages/jam/in-core/refine.test.d.ts.map +1 -0
  32. package/packages/jam/in-core/refine.test.js +6 -0
  33. package/packages/jam/jam-host-calls/accumulate/bless.js +9 -9
  34. package/packages/jam/jam-host-calls/externalities/partial-state.d.ts +1 -1
  35. package/packages/jam/jam-host-calls/externalities/refine-externalities.d.ts +1 -1
  36. package/packages/jam/jam-host-calls/externalities/refine-externalities.d.ts.map +1 -1
  37. package/packages/jam/jam-host-calls/general/fetch.d.ts +164 -103
  38. package/packages/jam/jam-host-calls/general/fetch.d.ts.map +1 -1
  39. package/packages/jam/jam-host-calls/general/fetch.js +117 -23
  40. package/packages/jam/jam-host-calls/general/fetch.test.js +100 -66
  41. package/packages/jam/jamnp-s/protocol/ce-133-work-package-submission.d.ts +2 -2
  42. package/packages/jam/transition/accumulate/accumulate.js +2 -2
  43. package/packages/jam/transition/accumulate/accumulation-result-merge-utils.js +48 -39
  44. package/packages/jam/transition/externalities/accumulate-externalities.d.ts +2 -2
  45. package/packages/jam/transition/externalities/accumulate-externalities.d.ts.map +1 -1
  46. package/packages/jam/transition/externalities/accumulate-externalities.js +20 -7
  47. package/packages/jam/transition/externalities/accumulate-externalities.test.js +74 -4
  48. package/packages/jam/transition/externalities/accumulate-fetch-externalities.d.ts +19 -0
  49. package/packages/jam/transition/externalities/accumulate-fetch-externalities.d.ts.map +1 -0
  50. package/packages/jam/transition/externalities/accumulate-fetch-externalities.js +45 -0
  51. package/packages/jam/transition/externalities/accumulate-fetch-externalities.test.d.ts +2 -0
  52. package/packages/jam/transition/externalities/accumulate-fetch-externalities.test.d.ts.map +1 -0
  53. package/packages/jam/transition/externalities/accumulate-fetch-externalities.test.js +192 -0
  54. package/packages/jam/transition/externalities/fetch-externalities.d.ts +3 -39
  55. package/packages/jam/transition/externalities/fetch-externalities.d.ts.map +1 -1
  56. package/packages/jam/transition/externalities/fetch-externalities.js +2 -88
  57. package/packages/jam/transition/externalities/index.d.ts +3 -0
  58. package/packages/jam/transition/externalities/index.d.ts.map +1 -1
  59. package/packages/jam/transition/externalities/index.js +3 -0
  60. package/packages/jam/transition/externalities/is-authorized-fetch-externalities.d.ts +22 -0
  61. package/packages/jam/transition/externalities/is-authorized-fetch-externalities.d.ts.map +1 -0
  62. package/packages/jam/transition/externalities/is-authorized-fetch-externalities.js +41 -0
  63. package/packages/jam/transition/externalities/refine-fetch-externalities.d.ts +23 -0
  64. package/packages/jam/transition/externalities/refine-fetch-externalities.d.ts.map +1 -0
  65. package/packages/jam/transition/externalities/refine-fetch-externalities.js +56 -0
  66. package/packages/jam/transition/externalities/refine-fetch-externalities.test.d.ts +2 -0
  67. package/packages/jam/transition/externalities/refine-fetch-externalities.test.d.ts.map +1 -0
  68. package/packages/jam/transition/externalities/refine-fetch-externalities.test.js +32 -0
  69. package/packages/jam/transition/externalities/fetch-externalities.test.d.ts +0 -2
  70. package/packages/jam/transition/externalities/fetch-externalities.test.d.ts.map +0 -1
  71. package/packages/jam/transition/externalities/fetch-externalities.test.js +0 -254
@@ -3,12 +3,15 @@ import { describe, it } from "node:test";
3
3
  import { MAX_NUMBER_OF_EXPORTS_WP, SEGMENT_BYTES, tryAsServiceGas, tryAsServiceId, tryAsTimeSlot, } from "#@typeberry/block";
4
4
  import { Bytes, BytesBlob } from "#@typeberry/bytes";
5
5
  import { HashDictionary } from "#@typeberry/collections";
6
- import { tinyChainSpec } from "#@typeberry/config";
6
+ import { PvmBackend, tinyChainSpec } from "#@typeberry/config";
7
7
  import { HASH_SIZE } from "#@typeberry/hash";
8
- import { SegmentExportError } from "#@typeberry/jam-host-calls";
8
+ import { SegmentExportError, tryAsMachineId, tryAsProgramCounter } from "#@typeberry/jam-host-calls";
9
9
  import { tryAsU32, tryAsU64 } from "#@typeberry/numbers";
10
+ import { HostCallRegisters } from "#@typeberry/pvm-host-calls";
11
+ import { NO_OF_REGISTERS, REGISTER_BYTE_SIZE, Status, tryAsBigGas } from "#@typeberry/pvm-interface";
10
12
  import { InMemoryService, InMemoryState, PreimageItem, ServiceAccountInfo } from "#@typeberry/state";
11
13
  import { RefineExternalitiesImpl } from "./refine.js";
14
+ const MINIMAL_PROGRAM = new Uint8Array([0, 1, 1, 0, 0x00]);
12
15
  function createSegment(byte = 0xab) {
13
16
  return Bytes.fill(SEGMENT_BYTES, byte);
14
17
  }
@@ -59,9 +62,13 @@ function createExt(overrides = {}) {
59
62
  currentServiceId: tryAsServiceId(42),
60
63
  lookupState: overrides.lookupState ?? defaultState,
61
64
  exportOffset: overrides.exportOffset ?? 0,
65
+ pvmBackend: PvmBackend.BuiltIn,
62
66
  ...overrides,
63
67
  });
64
68
  }
69
+ function emptyRegisters() {
70
+ return new HostCallRegisters(new Uint8Array(NO_OF_REGISTERS * REGISTER_BYTE_SIZE));
71
+ }
65
72
  describe("RefineExternalitiesImpl", () => {
66
73
  describe("historicalLookup", () => {
67
74
  const PREIMAGE_HASH = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
@@ -160,4 +167,162 @@ describe("RefineExternalitiesImpl", () => {
160
167
  assert.deepStrictEqual(exported[0].raw.subarray(0, 5), new Uint8Array([1, 2, 3, 4, 5]));
161
168
  });
162
169
  });
170
+ describe("machineInit", () => {
171
+ it("should create a new inner PVM and return a machine ID", async () => {
172
+ const ext = createExt();
173
+ const code = BytesBlob.blobFrom(MINIMAL_PROGRAM);
174
+ const pc = tryAsProgramCounter(0);
175
+ const result = await ext.machineInit(code, pc);
176
+ assert.strictEqual(result.isOk, true);
177
+ assert.strictEqual(result.ok, tryAsMachineId(0));
178
+ });
179
+ it("should assign sequential machine IDs", async () => {
180
+ const ext = createExt();
181
+ const code = BytesBlob.blobFrom(MINIMAL_PROGRAM);
182
+ const pc = tryAsProgramCounter(0);
183
+ const r1 = await ext.machineInit(code, pc);
184
+ const r2 = await ext.machineInit(code, pc);
185
+ const r3 = await ext.machineInit(code, pc);
186
+ assert.strictEqual(r1.isOk, true);
187
+ assert.strictEqual(r1.ok, tryAsMachineId(0));
188
+ assert.strictEqual(r2.isOk, true);
189
+ assert.strictEqual(r2.ok, tryAsMachineId(1));
190
+ assert.strictEqual(r3.isOk, true);
191
+ assert.strictEqual(r3.ok, tryAsMachineId(2));
192
+ });
193
+ it("should return error for invalid program blob", async () => {
194
+ const ext = createExt();
195
+ const invalidCode = BytesBlob.blobFrom(new Uint8Array([0xff, 0xff, 0xff]));
196
+ const pc = tryAsProgramCounter(0);
197
+ const result = await ext.machineInit(invalidCode, pc);
198
+ assert.strictEqual(result.isError, true);
199
+ });
200
+ it("should return error for empty program blob", async () => {
201
+ const ext = createExt();
202
+ const emptyCode = BytesBlob.blobFrom(new Uint8Array([]));
203
+ const pc = tryAsProgramCounter(0);
204
+ const result = await ext.machineInit(emptyCode, pc);
205
+ assert.strictEqual(result.isError, true);
206
+ });
207
+ it("should accept a non-zero program counter", async () => {
208
+ const ext = createExt();
209
+ const code = BytesBlob.blobFrom(MINIMAL_PROGRAM);
210
+ const pc = tryAsProgramCounter(1);
211
+ const result = await ext.machineInit(code, pc);
212
+ assert.strictEqual(result.isOk, true);
213
+ });
214
+ });
215
+ describe("machineExpunge", () => {
216
+ it("should remove machine and return its program counter (0)", async () => {
217
+ const ext = createExt();
218
+ const code = BytesBlob.blobFrom(MINIMAL_PROGRAM);
219
+ const initResult = await ext.machineInit(code, tryAsProgramCounter(0));
220
+ assert.strictEqual(initResult.isOk, true);
221
+ const machineId = initResult.ok;
222
+ const result = await ext.machineExpunge(machineId);
223
+ assert.strictEqual(result.isOk, true);
224
+ // PC should be 0 since we initialized with PC=0
225
+ assert.strictEqual(result.ok, tryAsProgramCounter(0));
226
+ });
227
+ it("should remove machine and return its program counter (10)", async () => {
228
+ const ext = createExt();
229
+ const code = BytesBlob.blobFrom(MINIMAL_PROGRAM);
230
+ const initResult = await ext.machineInit(code, tryAsProgramCounter(10));
231
+ assert.strictEqual(initResult.isOk, true);
232
+ const machineId = initResult.ok;
233
+ const result = await ext.machineExpunge(machineId);
234
+ assert.strictEqual(result.isOk, true);
235
+ // PC should be 10 since we initialized with PC=10
236
+ assert.strictEqual(result.ok, tryAsProgramCounter(10));
237
+ });
238
+ it("should return NoMachineError for non-existent machine", async () => {
239
+ const ext = createExt();
240
+ const result = await ext.machineExpunge(tryAsMachineId(999));
241
+ assert.strictEqual(result.isError, true);
242
+ });
243
+ it("should not allow double expunge", async () => {
244
+ const ext = createExt();
245
+ const code = BytesBlob.blobFrom(MINIMAL_PROGRAM);
246
+ const initResult = await ext.machineInit(code, tryAsProgramCounter(0));
247
+ assert.strictEqual(initResult.isOk, true);
248
+ const machineId = initResult.ok;
249
+ const r1 = await ext.machineExpunge(machineId);
250
+ assert.strictEqual(r1.isOk, true);
251
+ const r2 = await ext.machineExpunge(machineId);
252
+ assert.strictEqual(r2.isError, true);
253
+ });
254
+ it("should remove exact machine from multiple and return its program counter (10)", async () => {
255
+ const ext = createExt();
256
+ const code = BytesBlob.blobFrom(MINIMAL_PROGRAM);
257
+ await ext.machineInit(code, tryAsProgramCounter(0));
258
+ const initResult = await ext.machineInit(code, tryAsProgramCounter(10));
259
+ await ext.machineInit(code, tryAsProgramCounter(20));
260
+ assert.strictEqual(initResult.isOk, true);
261
+ const machineId = initResult.ok;
262
+ const result = await ext.machineExpunge(machineId);
263
+ assert.strictEqual(result.isOk, true);
264
+ // PC should be 10 since we initialized with PC=10
265
+ assert.strictEqual(result.ok, tryAsProgramCounter(10));
266
+ });
267
+ });
268
+ describe("machineInvoke", () => {
269
+ it("should return NoMachineError for non-existent machine", async () => {
270
+ const ext = createExt();
271
+ const regs = emptyRegisters();
272
+ const result = await ext.machineInvoke(tryAsMachineId(999), tryAsBigGas(1000n), regs);
273
+ assert.strictEqual(result.isError, true);
274
+ });
275
+ it("should execute inner PVM and return PANIC for TRAP instruction", async () => {
276
+ const ext = createExt();
277
+ const code = BytesBlob.blobFrom(MINIMAL_PROGRAM);
278
+ const initResult = await ext.machineInit(code, tryAsProgramCounter(0));
279
+ assert.strictEqual(initResult.isOk, true);
280
+ const machineId = initResult.ok;
281
+ const regs = emptyRegisters();
282
+ const result = await ext.machineInvoke(machineId, tryAsBigGas(1000n), regs);
283
+ assert.strictEqual(result.isOk, true);
284
+ assert.strictEqual(result.ok.result.status, Status.PANIC);
285
+ });
286
+ it("should return OOG when gas is exhausted", async () => {
287
+ const ext = createExt();
288
+ const code = BytesBlob.blobFrom(MINIMAL_PROGRAM);
289
+ const initResult = await ext.machineInit(code, tryAsProgramCounter(0));
290
+ assert.strictEqual(initResult.isOk, true);
291
+ const machineId = initResult.ok;
292
+ const regs = emptyRegisters();
293
+ // With 0 gas, should immediately OOG
294
+ const result = await ext.machineInvoke(machineId, tryAsBigGas(0n), regs);
295
+ assert.strictEqual(result.isOk, true);
296
+ assert.strictEqual(result.ok.result.status, Status.OOG);
297
+ });
298
+ it("should pass registers to inner PVM and return them back", async () => {
299
+ const ext = createExt();
300
+ const code = BytesBlob.blobFrom(MINIMAL_PROGRAM);
301
+ const initResult = await ext.machineInit(code, tryAsProgramCounter(0));
302
+ assert.strictEqual(initResult.isOk, true);
303
+ const machineId = initResult.ok;
304
+ const regs = emptyRegisters();
305
+ regs.set(0, tryAsU64(0xdeadbeefn));
306
+ regs.set(5, tryAsU64(0xcafebaben));
307
+ const result = await ext.machineInvoke(machineId, tryAsBigGas(1000n), regs);
308
+ assert.strictEqual(result.isOk, true);
309
+ // Registers should be returned (TRAP doesn't modify registers)
310
+ assert.strictEqual(result.ok.registers.get(0), tryAsU64(0xdeadbeefn));
311
+ assert.strictEqual(result.ok.registers.get(5), tryAsU64(0xcafebaben));
312
+ });
313
+ it("should return remaining gas after execution", async () => {
314
+ const ext = createExt();
315
+ const code = BytesBlob.blobFrom(MINIMAL_PROGRAM);
316
+ const initResult = await ext.machineInit(code, tryAsProgramCounter(0));
317
+ assert.strictEqual(initResult.isOk, true);
318
+ const machineId = initResult.ok;
319
+ const regs = emptyRegisters();
320
+ const result = await ext.machineInvoke(machineId, tryAsBigGas(1000n), regs);
321
+ assert.strictEqual(result.isOk, true);
322
+ // TRAP costs 1 gas, so remaining should be 999
323
+ const remaining = Number(result.ok.gas);
324
+ assert.ok(remaining < 1000);
325
+ assert.ok(remaining >= 0);
326
+ });
327
+ });
163
328
  });
@@ -1,23 +1,18 @@
1
- import { type CoreIndex, type Segment, type SegmentIndex, type ServiceGas } from "#@typeberry/block";
1
+ import type { CoreIndex, Segment } from "#@typeberry/block";
2
2
  import { type WorkPackageHash } from "#@typeberry/block/refine-context.js";
3
3
  import type { WorkItemExtrinsic } from "#@typeberry/block/work-item.js";
4
4
  import type { WorkPackage } from "#@typeberry/block/work-package.js";
5
5
  import { WorkReport } from "#@typeberry/block/work-report.js";
6
- import { WorkExecResult, WorkResult } from "#@typeberry/block/work-result.js";
7
- import { type KnownSizeArray } from "#@typeberry/collections";
8
6
  import type { ChainSpec, PvmBackend } from "#@typeberry/config";
9
7
  import type { StatesDb } from "#@typeberry/database";
10
- import { type ReturnValue } from "#@typeberry/executor";
11
- import { type Blake2b, type WithHash } from "#@typeberry/hash";
8
+ import type { Blake2b, WithHash } from "#@typeberry/hash";
12
9
  import { Result } from "#@typeberry/utils";
10
+ import { type ImportedSegment, type PerWorkItem } from "./refine.js";
11
+ export type { ImportedSegment, PerWorkItem, RefineItemResult } from "./refine.js";
13
12
  export type RefineResult = {
14
13
  report: WorkReport;
15
14
  exports: PerWorkItem<Segment[]>;
16
15
  };
17
- export type RefineItemResult = {
18
- result: WorkResult;
19
- exports: readonly Segment[];
20
- };
21
16
  export declare enum RefineError {
22
17
  /** State for context anchor block or lookup anchor is not found in the DB. */
23
18
  StateMissing = 0,
@@ -28,16 +23,11 @@ export declare enum RefineError {
28
23
  /** Authorization error. */
29
24
  AuthorizationError = 3
30
25
  }
31
- export type PerWorkItem<T> = KnownSizeArray<T, "for each work item">;
32
- export type ImportedSegment = {
33
- index: SegmentIndex;
34
- data: Segment;
35
- };
36
26
  export declare class InCore {
37
27
  readonly chainSpec: ChainSpec;
38
28
  private readonly states;
39
- private readonly pvmBackend;
40
- private readonly blake2b;
29
+ private readonly isAuthorized;
30
+ private readonly refineItem;
41
31
  constructor(chainSpec: ChainSpec, states: StatesDb, pvmBackend: PvmBackend, blake2b: Blake2b);
42
32
  /**
43
33
  * Work-report computation function.
@@ -50,11 +40,6 @@ export declare class InCore {
50
40
  * https://graypaper.fluffylabs.dev/#/ab2cdbd/1b7f021b7f02?v=0.7.2
51
41
  */
52
42
  refine(workPackageAndHash: WithHash<WorkPackageHash, WorkPackage>, core: CoreIndex, imports: PerWorkItem<ImportedSegment[]>, extrinsics: PerWorkItem<WorkItemExtrinsic[]>): Promise<Result<RefineResult, RefineError>>;
53
- private amalgamateWorkReport;
54
- private authorizePackage;
55
- private refineItem;
56
- extractWorkResult(execResult: ReturnValue<ServiceGas>): WorkExecResult;
57
- private getServiceCode;
58
- private createRefineExternalities;
43
+ private static amalgamateWorkReport;
59
44
  }
60
45
  //# sourceMappingURL=in-core.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"in-core.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/in-core/in-core.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,OAAO,EACZ,KAAK,YAAY,EACjB,KAAK,UAAU,EAIhB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAGL,KAAK,eAAe,EAErB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAY,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACjF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAmB,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAC9E,OAAO,EAAE,cAAc,EAAsC,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAGjH,OAAO,EAA+B,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC1F,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAA+D,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACpH,OAAO,EAAE,KAAK,OAAO,EAAa,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAKzE,OAAO,EAA4B,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAGpE,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,SAAS,OAAO,EAAE,CAAC;CAC7B,CAAC;AAEF,oBAAY,WAAW;IACrB,8EAA8E;IAC9E,YAAY,IAAI;IAChB,qFAAqF;IACrF,iBAAiB,IAAI;IACrB,wEAAwE;IACxE,uBAAuB,IAAI;IAC3B,2BAA2B;IAC3B,kBAAkB,IAAI;CACvB;AAqBD,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,cAAc,CAAC,CAAC,EAAE,oBAAoB,CAAC,CAAC;AAErE,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,YAAY,CAAC;IACpB,IAAI,EAAE,OAAO,CAAC;CACf,CAAC;AAgBF,qBAAa,MAAM;aAEC,SAAS,EAAE,SAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAHR,SAAS,EAAE,SAAS,EACnB,MAAM,EAAE,QAAQ,EAChB,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,OAAO;IAGnC;;;;;;;;;OASG;IACG,MAAM,CACV,kBAAkB,EAAE,QAAQ,CAAC,eAAe,EAAE,WAAW,CAAC,EAC1D,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,WAAW,CAAC,eAAe,EAAE,CAAC,EACvC,UAAU,EAAE,WAAW,CAAC,iBAAiB,EAAE,CAAC,GAC3C,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;IAgF7C,OAAO,CAAC,oBAAoB;YAuDd,gBAAgB;YAkBhB,UAAU;IAoGxB,iBAAiB,CAAC,UAAU,EAAE,WAAW,CAAC,UAAU,CAAC;IAiBrD,OAAO,CAAC,cAAc;IAyCtB,OAAO,CAAC,yBAAyB;CA2BlC"}
1
+ {"version":3,"file":"in-core.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/in-core/in-core.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAsB,KAAK,eAAe,EAAmB,MAAM,oCAAoC,CAAC;AAC/G,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAmB,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAG9E,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAIzD,OAAO,EAAe,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,WAAW,EAAiC,MAAM,aAAa,CAAC;AAEpG,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAElF,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC;CACjC,CAAC;AAEF,oBAAY,WAAW;IACrB,8EAA8E;IAC9E,YAAY,IAAI;IAChB,qFAAqF;IACrF,iBAAiB,IAAI;IACrB,wEAAwE;IACxE,uBAAuB,IAAI;IAC3B,2BAA2B;IAC3B,kBAAkB,IAAI;CACvB;AAID,qBAAa,MAAM;aAKC,SAAS,EAAE,SAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM;IALzB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAGlB,SAAS,EAAE,SAAS,EACnB,MAAM,EAAE,QAAQ,EACjC,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,OAAO;IAMlB;;;;;;;;;OASG;IACG,MAAM,CACV,kBAAkB,EAAE,QAAQ,CAAC,eAAe,EAAE,WAAW,CAAC,EAC1D,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,WAAW,CAAC,eAAe,EAAE,CAAC,EACvC,UAAU,EAAE,WAAW,CAAC,iBAAiB,EAAE,CAAC,GAC3C,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;IAwF7C,OAAO,CAAC,MAAM,CAAC,oBAAoB;CAsDpC"}
@@ -1,18 +1,13 @@
1
- import { tryAsCoreIndex, tryAsServiceGas, } from "#@typeberry/block";
2
- import { W_C } from "#@typeberry/block/gp-constants.js";
3
- import { WorkPackageInfo, } from "#@typeberry/block/refine-context.js";
1
+ import { WorkPackageInfo } from "#@typeberry/block/refine-context.js";
4
2
  import { WorkPackageSpec, WorkReport } from "#@typeberry/block/work-report.js";
5
- import { WorkExecResult, WorkExecResultKind, WorkRefineLoad, WorkResult } from "#@typeberry/block/work-result.js";
6
- import { Bytes, BytesBlob } from "#@typeberry/bytes";
7
- import { codec, Encoder } from "#@typeberry/codec";
3
+ import { Bytes } from "#@typeberry/bytes";
8
4
  import { asKnownSize, FixedSizeArray } from "#@typeberry/collections";
9
- import { PvmExecutor, ReturnStatus } from "#@typeberry/executor";
10
5
  import { HASH_SIZE } from "#@typeberry/hash";
11
6
  import { Logger } from "#@typeberry/logger";
12
7
  import { tryAsU8, tryAsU16, tryAsU32 } from "#@typeberry/numbers";
13
- import { FetchExternalities } from "#@typeberry/transition/externalities/fetch-externalities.js";
14
- import { assertEmpty, assertNever, Result } from "#@typeberry/utils";
15
- import { RefineExternalitiesImpl } from "./externalities/refine.js";
8
+ import { assertEmpty, Result } from "#@typeberry/utils";
9
+ import { AuthorizationError, IsAuthorized } from "./is-authorized.js";
10
+ import { Refine } from "./refine.js";
16
11
  export var RefineError;
17
12
  (function (RefineError) {
18
13
  /** State for context anchor block or lookup anchor is not found in the DB. */
@@ -24,39 +19,17 @@ export var RefineError;
24
19
  /** Authorization error. */
25
20
  RefineError[RefineError["AuthorizationError"] = 3] = "AuthorizationError";
26
21
  })(RefineError || (RefineError = {}));
27
- var ServiceCodeError;
28
- (function (ServiceCodeError) {
29
- /** Service id is not found in the state. */
30
- ServiceCodeError[ServiceCodeError["ServiceNotFound"] = 0] = "ServiceNotFound";
31
- /** Expected service code does not match the state one. */
32
- ServiceCodeError[ServiceCodeError["ServiceCodeMismatch"] = 1] = "ServiceCodeMismatch";
33
- /** Code preimage missing. */
34
- ServiceCodeError[ServiceCodeError["ServiceCodeMissing"] = 2] = "ServiceCodeMissing";
35
- /** Code blob is too big. */
36
- ServiceCodeError[ServiceCodeError["ServiceCodeTooBig"] = 3] = "ServiceCodeTooBig";
37
- })(ServiceCodeError || (ServiceCodeError = {}));
38
- var AuthorizationError;
39
- (function (AuthorizationError) {
40
- })(AuthorizationError || (AuthorizationError = {}));
41
22
  const logger = Logger.new(import.meta.filename, "refine");
42
- /** https://graypaper.fluffylabs.dev/#/ab2cdbd/2ffe002ffe00?v=0.7.2 */
43
- const ARGS_CODEC = codec.object({
44
- core: codec.varU32.convert((x) => tryAsU32(x), (x) => tryAsCoreIndex(x)),
45
- workItemIndex: codec.varU32,
46
- serviceId: codec.varU32.asOpaque(),
47
- payloadLength: codec.varU32,
48
- packageHash: codec.bytes(HASH_SIZE).asOpaque(),
49
- });
50
23
  export class InCore {
51
24
  chainSpec;
52
25
  states;
53
- pvmBackend;
54
- blake2b;
26
+ isAuthorized;
27
+ refineItem;
55
28
  constructor(chainSpec, states, pvmBackend, blake2b) {
56
29
  this.chainSpec = chainSpec;
57
30
  this.states = states;
58
- this.pvmBackend = pvmBackend;
59
- this.blake2b = blake2b;
31
+ this.isAuthorized = new IsAuthorized(chainSpec, pvmBackend, blake2b);
32
+ this.refineItem = new Refine(chainSpec, pvmBackend, blake2b);
60
33
  }
61
34
  /**
62
35
  * Work-report computation function.
@@ -70,13 +43,14 @@ export class InCore {
70
43
  */
71
44
  async refine(workPackageAndHash, core, imports, extrinsics) {
72
45
  const workPackageHash = workPackageAndHash.hash;
73
- const { context, authorization, authCodeHash, authCodeHost, parametrization, items, ...rest } = workPackageAndHash.data;
46
+ const { context, authToken, authCodeHash, authCodeHost, authConfiguration, items, ...rest } = workPackageAndHash.data;
74
47
  assertEmpty(rest);
75
48
  // TODO [ToDr] Verify BEEFY root
76
49
  // TODO [ToDr] Verify prerequisites
77
50
  logger.log `[core:${core}] Attempting to refine work package with ${items.length} items.`;
78
- // TODO [ToDr] GP link
79
51
  // Verify anchor block
52
+ // https://graypaper.fluffylabs.dev/#/ab2cdbd/15cd0215cd02?v=0.7.2
53
+ // TODO [ToDr] Validation
80
54
  const state = this.states.getState(context.anchor);
81
55
  if (state === null) {
82
56
  return Result.error(RefineError.StateMissing, () => `State at anchor block ${context.anchor} is missing.`);
@@ -96,7 +70,7 @@ export class InCore {
96
70
  return Result.error(RefineError.InvalidLookupAnchorSlot, () => `Lookup anchor slot does not match the one is state. Ours: ${lookupState.timeslot}, expected: ${context.lookupAnchorSlot}`);
97
71
  }
98
72
  // Check authorization
99
- const authResult = await this.authorizePackage(authorization, authCodeHost, authCodeHash, parametrization);
73
+ const authResult = await this.isAuthorized.invoke(state, core, authToken, authCodeHost, authCodeHash, authConfiguration);
100
74
  if (authResult.isError) {
101
75
  return Result.error(RefineError.AuthorizationError, () => `Authorization error: ${AuthorizationError[authResult.error]}: ${authResult.details()}.`);
102
76
  }
@@ -106,14 +80,14 @@ export class InCore {
106
80
  const refineResults = [];
107
81
  for (const [idx, item] of items.entries()) {
108
82
  logger.info `[core:${core}][i:${idx}] Refining item for service ${item.service}.`;
109
- const result = await this.refineItem(state, lookupState, idx, item, imports, extrinsics, core, workPackageHash, exportOffset);
83
+ const result = await this.refineItem.invoke(state, lookupState, idx, item, imports, extrinsics, core, workPackageHash, exportOffset);
110
84
  refineResults.push(result);
111
85
  exportOffset += result.exports.length;
112
86
  }
113
87
  // amalgamate the work report now
114
- return Result.ok(this.amalgamateWorkReport(asKnownSize(refineResults), authResult.ok, workPackageHash, context, core));
88
+ return Result.ok(InCore.amalgamateWorkReport(asKnownSize(refineResults), authResult.ok, workPackageHash, context, core));
115
89
  }
116
- amalgamateWorkReport(refineResults, authResult, workPackageHash, context, coreIndex) {
90
+ static amalgamateWorkReport(refineResults, authResult, workPackageHash, context, coreIndex) {
117
91
  // unzip exports and work results for each work item
118
92
  const exports = refineResults.map((x) => x.exports);
119
93
  const results = refineResults.map((x) => x.result);
@@ -156,148 +130,4 @@ export class InCore {
156
130
  exports: asKnownSize(exports),
157
131
  };
158
132
  }
159
- async authorizePackage(_authorization, _authCodeHost, _authCodeHash, _parametrization) {
160
- // TODO [ToDr] Check authorization?
161
- const authorizerHash = Bytes.zero(HASH_SIZE).asOpaque();
162
- const authorizationGasUsed = tryAsServiceGas(0);
163
- const authorizationOutput = BytesBlob.empty();
164
- return Result.ok({
165
- authorizerHash,
166
- authorizationGasUsed,
167
- authorizationOutput,
168
- });
169
- }
170
- async refineItem(state, lookupState, idx, item, allImports, allExtrinsics, coreIndex, workPackageHash, exportOffset) {
171
- const payloadHash = this.blake2b.hashBytes(item.payload);
172
- const baseResult = {
173
- serviceId: item.service,
174
- codeHash: item.codeHash,
175
- payloadHash,
176
- gas: item.refineGasLimit,
177
- };
178
- const imports = allImports[idx];
179
- const extrinsics = allExtrinsics[idx];
180
- const baseLoad = {
181
- importedSegments: tryAsU32(imports.length),
182
- extrinsicCount: tryAsU32(extrinsics.length),
183
- extrinsicSize: tryAsU32(extrinsics.reduce((acc, x) => acc + x.length, 0)),
184
- };
185
- const maybeCode = this.getServiceCode(state, idx, item);
186
- if (maybeCode.isError) {
187
- const error = maybeCode.error === ServiceCodeError.ServiceCodeTooBig
188
- ? WorkExecResultKind.codeOversize
189
- : WorkExecResultKind.badCode;
190
- return {
191
- exports: [],
192
- result: WorkResult.create({
193
- ...baseResult,
194
- result: WorkExecResult.error(error),
195
- load: WorkRefineLoad.create({
196
- ...baseLoad,
197
- gasUsed: tryAsServiceGas(item.refineGasLimit),
198
- exportedSegments: tryAsU32(0),
199
- }),
200
- }),
201
- };
202
- }
203
- const code = maybeCode.ok;
204
- const externalities = this.createRefineExternalities({
205
- payload: item.payload,
206
- imports: allImports,
207
- extrinsics: allExtrinsics,
208
- currentServiceId: item.service,
209
- lookupState,
210
- exportOffset,
211
- });
212
- const executor = await PvmExecutor.createRefineExecutor(item.service, code, externalities, this.pvmBackend);
213
- const args = Encoder.encodeObject(ARGS_CODEC, {
214
- serviceId: item.service,
215
- core: coreIndex,
216
- workItemIndex: tryAsU32(idx),
217
- payloadLength: tryAsU32(item.payload.length),
218
- packageHash: workPackageHash,
219
- });
220
- const execResult = await executor.run(args, item.refineGasLimit);
221
- const exports = externalities.refine.getExportedSegments();
222
- if (exports.length !== item.exportCount) {
223
- return {
224
- exports,
225
- result: WorkResult.create({
226
- ...baseResult,
227
- result: WorkExecResult.error(WorkExecResultKind.incorrectNumberOfExports),
228
- load: WorkRefineLoad.create({
229
- ...baseLoad,
230
- gasUsed: tryAsServiceGas(item.refineGasLimit),
231
- exportedSegments: tryAsU32(0),
232
- }),
233
- }),
234
- };
235
- }
236
- const result = this.extractWorkResult(execResult);
237
- return {
238
- exports,
239
- result: WorkResult.create({
240
- ...baseResult,
241
- result,
242
- load: WorkRefineLoad.create({
243
- ...baseLoad,
244
- gasUsed: tryAsServiceGas(execResult.consumedGas),
245
- exportedSegments: tryAsU32(exports.length),
246
- }),
247
- }),
248
- };
249
- }
250
- extractWorkResult(execResult) {
251
- if (execResult.status === ReturnStatus.OK) {
252
- const slice = execResult.memorySlice;
253
- // TODO [ToDr] Verify the output size and change digestTooBig?
254
- return WorkExecResult.ok(BytesBlob.blobFrom(slice));
255
- }
256
- switch (execResult.status) {
257
- case ReturnStatus.OOG:
258
- return WorkExecResult.error(WorkExecResultKind.outOfGas);
259
- case ReturnStatus.PANIC:
260
- return WorkExecResult.error(WorkExecResultKind.panic);
261
- default:
262
- assertNever(execResult);
263
- }
264
- }
265
- getServiceCode(state, idx, item) {
266
- const serviceId = item.service;
267
- const service = state.getService(serviceId);
268
- // TODO [ToDr] GP link
269
- // missing service
270
- if (service === null) {
271
- return Result.error(ServiceCodeError.ServiceNotFound, () => `[i:${idx}] Service ${serviceId} is missing in state.`);
272
- }
273
- // TODO [ToDr] GP link
274
- // TODO [ToDr] shall we rather use the old codehash instead
275
- if (!service.getInfo().codeHash.isEqualTo(item.codeHash)) {
276
- return Result.error(ServiceCodeError.ServiceCodeMismatch, () => `[i:${idx}] Service ${serviceId} has invalid code hash. Ours: ${service.getInfo().codeHash}, expected: ${item.codeHash}`);
277
- }
278
- const code = service.getPreimage(item.codeHash.asOpaque());
279
- if (code === null) {
280
- return Result.error(ServiceCodeError.ServiceCodeMissing, () => `[i:${idx}] Code ${item.codeHash} for service ${serviceId} was not found.`);
281
- }
282
- if (code.length > W_C) {
283
- return Result.error(ServiceCodeError.ServiceCodeTooBig, () => `[i:${idx}] Code ${item.codeHash} for service ${serviceId} is too big! ${code.length} bytes vs ${W_C} bytes max.`);
284
- }
285
- return Result.ok(code);
286
- }
287
- createRefineExternalities(args) {
288
- // TODO [ToDr] Pass all required fetch data
289
- const fetchExternalities = FetchExternalities.createForRefine({
290
- entropy: undefined,
291
- ...args,
292
- }, this.chainSpec);
293
- const refine = RefineExternalitiesImpl.create({
294
- currentServiceId: args.currentServiceId,
295
- lookupState: args.lookupState,
296
- exportOffset: args.exportOffset,
297
- });
298
- return {
299
- fetchExternalities,
300
- refine,
301
- };
302
- }
303
133
  }
@@ -1,4 +1,6 @@
1
1
  import assert from "node:assert";
2
+ import { readFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
2
4
  import { before, describe, it } from "node:test";
3
5
  import { tryAsCoreIndex, tryAsServiceGas, tryAsServiceId, tryAsTimeSlot } from "#@typeberry/block";
4
6
  import { RefineContext } from "#@typeberry/block/refine-context.js";
@@ -6,21 +8,46 @@ import { WorkItem } from "#@typeberry/block/work-item.js";
6
8
  import { tryAsWorkItemsCount, WorkPackage } from "#@typeberry/block/work-package.js";
7
9
  import { Bytes, BytesBlob } from "#@typeberry/bytes";
8
10
  import { Encoder } from "#@typeberry/codec";
9
- import { asKnownSize, FixedSizeArray } from "#@typeberry/collections";
11
+ import { asKnownSize, FixedSizeArray, HashDictionary } from "#@typeberry/collections";
10
12
  import { PvmBackend, tinyChainSpec } from "#@typeberry/config";
11
13
  import { InMemoryStates } from "#@typeberry/database";
12
14
  import { Blake2b, HASH_SIZE, WithHash } from "#@typeberry/hash";
13
- import { tryAsU16 } from "#@typeberry/numbers";
14
- import { testState } from "#@typeberry/state/test.utils.js";
15
+ import { tryAsU16, tryAsU32, tryAsU64 } from "#@typeberry/numbers";
16
+ import { InMemoryService, InMemoryState, PreimageItem, ServiceAccountInfo } from "#@typeberry/state";
15
17
  import { InCore, RefineError } from "./in-core.js";
18
+ // Load the authorizer PVM fixture (checks authToken === authConfiguration).
19
+ const AUTHORIZER_PVM = BytesBlob.blobFrom(readFileSync(resolve(import.meta.dirname, "fixtures/authorizer.pvm")));
20
+ const AUTH_SERVICE_ID = tryAsServiceId(1);
16
21
  let blake2b;
17
22
  before(async () => {
18
23
  blake2b = await Blake2b.createHasher();
19
24
  });
20
- function createWorkItem(serviceId = 1) {
25
+ function getAuthCodeHash() {
26
+ return blake2b.hashBytes(AUTHORIZER_PVM).asOpaque();
27
+ }
28
+ function createService(serviceId, codeHash, code) {
29
+ return new InMemoryService(serviceId, {
30
+ info: ServiceAccountInfo.create({
31
+ codeHash: codeHash.asOpaque(),
32
+ balance: tryAsU64(10_000_000_000),
33
+ accumulateMinGas: tryAsServiceGas(0n),
34
+ onTransferMinGas: tryAsServiceGas(0n),
35
+ storageUtilisationBytes: tryAsU64(0),
36
+ storageUtilisationCount: tryAsU32(0),
37
+ gratisStorage: tryAsU64(0),
38
+ created: tryAsTimeSlot(0),
39
+ lastAccumulation: tryAsTimeSlot(0),
40
+ parentService: tryAsServiceId(0),
41
+ }),
42
+ preimages: HashDictionary.fromEntries([PreimageItem.create({ hash: codeHash.asOpaque(), blob: code })].map((x) => [x.hash, x])),
43
+ lookupHistory: HashDictionary.fromEntries([]),
44
+ storage: new Map(),
45
+ });
46
+ }
47
+ function createWorkItem(codeHash, serviceId = 1) {
21
48
  return WorkItem.create({
22
49
  service: tryAsServiceId(serviceId),
23
- codeHash: Bytes.zero(HASH_SIZE).asOpaque(),
50
+ codeHash,
24
51
  payload: BytesBlob.empty(),
25
52
  refineGasLimit: tryAsServiceGas(1_000_000),
26
53
  accumulateGasLimit: tryAsServiceGas(1_000_000),
@@ -29,12 +56,12 @@ function createWorkItem(serviceId = 1) {
29
56
  exportCount: tryAsU16(0),
30
57
  });
31
58
  }
32
- function createWorkPackage(anchorHash, stateRoot, lookupAnchorSlot = 0) {
59
+ function createWorkPackage(anchorHash, stateRoot, authCodeHash, lookupAnchorSlot = 0) {
33
60
  return WorkPackage.create({
34
- authorization: BytesBlob.empty(),
35
- authCodeHost: tryAsServiceId(1),
36
- authCodeHash: Bytes.zero(HASH_SIZE).asOpaque(),
37
- parametrization: BytesBlob.empty(),
61
+ authToken: BytesBlob.empty(),
62
+ authCodeHost: AUTH_SERVICE_ID,
63
+ authCodeHash,
64
+ authConfiguration: BytesBlob.empty(),
38
65
  context: RefineContext.create({
39
66
  anchor: anchorHash,
40
67
  stateRoot,
@@ -43,7 +70,7 @@ function createWorkPackage(anchorHash, stateRoot, lookupAnchorSlot = 0) {
43
70
  lookupAnchorSlot: tryAsTimeSlot(lookupAnchorSlot),
44
71
  prerequisites: [],
45
72
  }),
46
- items: FixedSizeArray.new([createWorkItem()], tryAsWorkItemsCount(1)),
73
+ items: FixedSizeArray.new([createWorkItem(authCodeHash)], tryAsWorkItemsCount(1)),
47
74
  });
48
75
  }
49
76
  function hashWorkPackage(spec, workPackage) {
@@ -59,7 +86,8 @@ describe("InCore", () => {
59
86
  const inCore = new InCore(spec, states, PvmBackend.BuiltIn, blake2b);
60
87
  const anchorHash = Bytes.fill(HASH_SIZE, 1).asOpaque();
61
88
  const stateRoot = Bytes.zero(HASH_SIZE).asOpaque();
62
- const workPackage = createWorkPackage(anchorHash, stateRoot);
89
+ const authCodeHash = getAuthCodeHash();
90
+ const workPackage = createWorkPackage(anchorHash, stateRoot, authCodeHash);
63
91
  const result = await inCore.refine(hashWorkPackage(spec, workPackage), tryAsCoreIndex(0), asKnownSize([[]]), asKnownSize([[]]));
64
92
  assert.strictEqual(result.isError, true);
65
93
  assert.strictEqual(result.error, RefineError.StateMissing);
@@ -68,13 +96,17 @@ describe("InCore", () => {
68
96
  const spec = tinyChainSpec;
69
97
  const states = new InMemoryStates(spec);
70
98
  const inCore = new InCore(spec, states, PvmBackend.BuiltIn, blake2b);
99
+ const authCodeHash = getAuthCodeHash();
71
100
  const anchorHash = Bytes.fill(HASH_SIZE, 1).asOpaque();
72
- const state = testState();
101
+ const state = InMemoryState.partial(spec, {
102
+ timeslot: tryAsTimeSlot(16),
103
+ services: new Map([[AUTH_SERVICE_ID, createService(AUTH_SERVICE_ID, authCodeHash, AUTHORIZER_PVM)]]),
104
+ });
73
105
  await states.insertInitialState(anchorHash, state);
74
106
  const correctStateRoot = await states.getStateRoot(state);
75
- const workPackage = createWorkPackage(anchorHash, correctStateRoot, state.timeslot);
107
+ const workPackage = createWorkPackage(anchorHash, correctStateRoot, authCodeHash, state.timeslot);
76
108
  const result = await inCore.refine(hashWorkPackage(spec, workPackage), tryAsCoreIndex(0), asKnownSize([[]]), asKnownSize([[]]));
77
- assert.strictEqual(result.isOk, true);
109
+ assert.strictEqual(result.isOk, true, `Expected OK but got error: ${result.isError ? result.details() : ""}`);
78
110
  assert.strictEqual(result.ok.report.coreIndex, 0);
79
111
  assert.strictEqual(result.ok.report.results.length, 1);
80
112
  });