@typeberry/lib 0.5.2 → 0.5.3

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 (61) hide show
  1. package/package.json +2 -2
  2. package/packages/core/collections/blob-dictionary.d.ts.map +1 -1
  3. package/packages/core/collections/blob-dictionary.js +3 -3
  4. package/packages/core/crypto/bandersnatch.d.ts +2 -1
  5. package/packages/core/crypto/bandersnatch.d.ts.map +1 -1
  6. package/packages/core/crypto/bandersnatch.js +9 -2
  7. package/packages/core/crypto/key-derivation.test.js +8 -7
  8. package/packages/core/networking/package.json +1 -1
  9. package/packages/core/pvm-host-calls/bin.js +6 -6
  10. package/packages/core/pvm-host-calls/ecalli-io-tracker.d.ts +32 -0
  11. package/packages/core/pvm-host-calls/ecalli-io-tracker.d.ts.map +1 -0
  12. package/packages/core/pvm-host-calls/ecalli-io-tracker.js +14 -0
  13. package/packages/core/pvm-host-calls/ecalli-trace-logger.d.ts +139 -0
  14. package/packages/core/pvm-host-calls/ecalli-trace-logger.d.ts.map +1 -0
  15. package/packages/core/pvm-host-calls/ecalli-trace-logger.js +209 -0
  16. package/packages/core/pvm-host-calls/ecalli-trace-logger.test.d.ts +2 -0
  17. package/packages/core/pvm-host-calls/ecalli-trace-logger.test.d.ts.map +1 -0
  18. package/packages/core/pvm-host-calls/ecalli-trace-logger.test.js +231 -0
  19. package/packages/core/pvm-host-calls/host-call-memory.d.ts +2 -0
  20. package/packages/core/pvm-host-calls/host-call-memory.d.ts.map +1 -1
  21. package/packages/core/pvm-host-calls/host-call-memory.js +12 -2
  22. package/packages/core/pvm-host-calls/host-call-registers.d.ts +6 -0
  23. package/packages/core/pvm-host-calls/host-call-registers.d.ts.map +1 -1
  24. package/packages/core/pvm-host-calls/host-call-registers.js +24 -0
  25. package/packages/core/pvm-host-calls/host-calls-executor.d.ts +31 -0
  26. package/packages/core/pvm-host-calls/host-calls-executor.d.ts.map +1 -0
  27. package/packages/core/pvm-host-calls/host-calls-executor.js +137 -0
  28. package/packages/core/pvm-host-calls/host-calls.d.ts +20 -26
  29. package/packages/core/pvm-host-calls/host-calls.d.ts.map +1 -1
  30. package/packages/core/pvm-host-calls/host-calls.js +40 -112
  31. package/packages/core/pvm-host-calls/index.d.ts +7 -6
  32. package/packages/core/pvm-host-calls/index.d.ts.map +1 -1
  33. package/packages/core/pvm-host-calls/index.js +7 -6
  34. package/packages/core/pvm-host-calls/{interpreter-instance-manager.d.ts → pvm-instance-manager.d.ts} +3 -3
  35. package/packages/core/pvm-host-calls/pvm-instance-manager.d.ts.map +1 -0
  36. package/packages/core/pvm-host-calls/{interpreter-instance-manager.js → pvm-instance-manager.js} +2 -2
  37. package/packages/core/telemetry/package.json +1 -1
  38. package/packages/jam/node/main-importer.d.ts.map +1 -1
  39. package/packages/jam/node/main-importer.js +3 -1
  40. package/packages/jam/node/package.json +1 -1
  41. package/packages/jam/safrole/bandersnatch-vrf.d.ts +2 -0
  42. package/packages/jam/safrole/bandersnatch-vrf.d.ts.map +1 -1
  43. package/packages/jam/safrole/bandersnatch-vrf.js +11 -0
  44. package/packages/jam/safrole/bandersnatch-vrf.test.js +3 -3
  45. package/packages/jam/safrole/bandersnatch-wasm.d.ts +1 -0
  46. package/packages/jam/safrole/bandersnatch-wasm.d.ts.map +1 -1
  47. package/packages/jam/safrole/bandersnatch-wasm.js +8 -5
  48. package/packages/jam/safrole/safrole-seal.d.ts +1 -3
  49. package/packages/jam/safrole/safrole-seal.d.ts.map +1 -1
  50. package/packages/jam/safrole/safrole-seal.js +14 -25
  51. package/packages/jam/safrole/safrole-seal.test.js +4 -10
  52. package/packages/jam/transition/accumulate/pvm-executor.d.ts.map +1 -1
  53. package/packages/jam/transition/accumulate/pvm-executor.js +2 -2
  54. package/packages/jam/transition/disputes/disputes.d.ts.map +1 -1
  55. package/packages/jam/transition/disputes/disputes.js +5 -4
  56. package/packages/workers/block-authorship/package.json +1 -1
  57. package/packages/workers/importer/package.json +1 -1
  58. package/packages/core/pvm-host-calls/host-calls-manager.d.ts +0 -23
  59. package/packages/core/pvm-host-calls/host-calls-manager.d.ts.map +0 -1
  60. package/packages/core/pvm-host-calls/host-calls-manager.js +0 -44
  61. package/packages/core/pvm-host-calls/interpreter-instance-manager.d.ts.map +0 -1
@@ -0,0 +1,231 @@
1
+ import assert from "node:assert";
2
+ import { describe, it } from "node:test";
3
+ import { tryAsU32, tryAsU64 } from "#@typeberry/numbers";
4
+ import { NO_OF_REGISTERS, REGISTER_BYTE_SIZE, tryAsSmallGas } from "#@typeberry/pvm-interface";
5
+ import { EcalliTraceLogger, IoTraceTracker } from "./ecalli-trace-logger.js";
6
+ import { tryAsHostCallIndex } from "./host-call-handler.js";
7
+ import { HostCallRegisters } from "./host-call-registers.js";
8
+ /** Helper to create HostCallRegisters with specific values set. */
9
+ function createRegisters(values) {
10
+ const bytes = new Uint8Array(NO_OF_REGISTERS * REGISTER_BYTE_SIZE);
11
+ const view = new DataView(bytes.buffer);
12
+ for (const [idx, value] of values) {
13
+ view.setBigUint64(idx * REGISTER_BYTE_SIZE, value, true);
14
+ }
15
+ return new HostCallRegisters(bytes);
16
+ }
17
+ describe("IoTraceLogger", () => {
18
+ describe("logProgram", () => {
19
+ it("formats program blob as hex", () => {
20
+ const lines = [];
21
+ const logger = EcalliTraceLogger.new((line) => lines.push(line));
22
+ logger.logProgram(new Uint8Array([0x01, 0x02, 0xaa, 0xbb]), new Uint8Array());
23
+ assert.strictEqual(lines.length, 1);
24
+ assert.strictEqual(lines[0], "program 0x0102aabb");
25
+ });
26
+ it("handles empty program", () => {
27
+ const lines = [];
28
+ const logger = EcalliTraceLogger.new((line) => lines.push(line));
29
+ logger.logProgram(new Uint8Array([]), new Uint8Array());
30
+ assert.strictEqual(lines[0], "program 0x");
31
+ });
32
+ it("logs args as initial memory write", () => {
33
+ const lines = [];
34
+ const logger = EcalliTraceLogger.new((line) => lines.push(line));
35
+ logger.logProgram(new Uint8Array([0x01]), new Uint8Array([0x00, 0x01, 0x02]));
36
+ assert.strictEqual(lines.length, 2);
37
+ assert.strictEqual(lines[0], "program 0x01");
38
+ assert.strictEqual(lines[1], "memwrite 0xfeff0000 len=3 <- 0x000102");
39
+ });
40
+ });
41
+ describe("logStart", () => {
42
+ it("formats start with register dump", () => {
43
+ const lines = [];
44
+ const logger = EcalliTraceLogger.new((line) => lines.push(line));
45
+ const regs = createRegisters(new Map([
46
+ [7, 0x10n],
47
+ [9, 0x10000n],
48
+ ]));
49
+ logger.logStart(0, tryAsSmallGas(10000), regs);
50
+ assert.strictEqual(lines[0], "start pc=0 gas=10000 r07=10 r09=10000");
51
+ });
52
+ it("handles no non-zero registers", () => {
53
+ const lines = [];
54
+ const logger = EcalliTraceLogger.new((line) => lines.push(line));
55
+ const regs = createRegisters(new Map());
56
+ logger.logStart(42, tryAsSmallGas(5000), regs);
57
+ assert.strictEqual(lines[0], "start pc=42 gas=5000 ");
58
+ });
59
+ });
60
+ describe("logEcalli", () => {
61
+ it("formats ecalli with register dump", () => {
62
+ const lines = [];
63
+ const logger = EcalliTraceLogger.new((line) => lines.push(line));
64
+ const regs = createRegisters(new Map([
65
+ [1, 0x1n],
66
+ [3, 0x1000n],
67
+ ]));
68
+ logger.logEcalli(tryAsHostCallIndex(10), 42, tryAsSmallGas(10000), regs);
69
+ assert.strictEqual(lines[0], "ecalli=10 pc=42 gas=10000 r01=1 r03=1000");
70
+ });
71
+ it("omits zero registers", () => {
72
+ const lines = [];
73
+ const logger = EcalliTraceLogger.new((line) => lines.push(line));
74
+ const regs = createRegisters(new Map([
75
+ [0, 0n],
76
+ [1, 1n],
77
+ ]));
78
+ logger.logEcalli(tryAsHostCallIndex(5), 0, tryAsSmallGas(5000), regs);
79
+ assert.strictEqual(lines[0], "ecalli=5 pc=0 gas=5000 r01=1");
80
+ });
81
+ it("handles no non-zero registers", () => {
82
+ const lines = [];
83
+ const logger = EcalliTraceLogger.new((line) => lines.push(line));
84
+ const regs = createRegisters(new Map());
85
+ logger.logEcalli(tryAsHostCallIndex(0), 0, tryAsSmallGas(100), regs);
86
+ assert.strictEqual(lines[0], "ecalli=0 pc=0 gas=100 ");
87
+ });
88
+ });
89
+ describe("logMemRead", () => {
90
+ it("formats memory read", () => {
91
+ const lines = [];
92
+ const logger = EcalliTraceLogger.new((line) => lines.push(line));
93
+ logger.logMemRead(0x1000, 4, "0x01020304");
94
+ assert.strictEqual(lines[0], "memread 0x00001000 len=4 -> 0x01020304");
95
+ });
96
+ });
97
+ describe("logMemWrite", () => {
98
+ it("formats memory write", () => {
99
+ const lines = [];
100
+ const logger = EcalliTraceLogger.new((line) => lines.push(line));
101
+ logger.logMemWrite(0x2000, 2, "0xffee");
102
+ assert.strictEqual(lines[0], "memwrite 0x00002000 len=2 <- 0xffee");
103
+ });
104
+ });
105
+ describe("logSetReg", () => {
106
+ it("formats register write with padded index", () => {
107
+ const lines = [];
108
+ const logger = EcalliTraceLogger.new((line) => lines.push(line));
109
+ logger.logSetReg(0, 0x100n);
110
+ assert.strictEqual(lines[0], "setreg r00 <- 100");
111
+ });
112
+ it("formats two-digit register index", () => {
113
+ const lines = [];
114
+ const logger = EcalliTraceLogger.new((line) => lines.push(line));
115
+ logger.logSetReg(12, 0x4n);
116
+ assert.strictEqual(lines[0], "setreg r12 <- 4");
117
+ });
118
+ });
119
+ describe("logSetGas", () => {
120
+ it("formats gas write", () => {
121
+ const lines = [];
122
+ const logger = EcalliTraceLogger.new((line) => lines.push(line));
123
+ logger.logSetGas(tryAsSmallGas(9950));
124
+ assert.strictEqual(lines[0], "setgas <- 9950");
125
+ });
126
+ });
127
+ describe("logHostActions", () => {
128
+ it("logs actions in correct order: reads, writes, regs, gas", () => {
129
+ const lines = [];
130
+ const logger = EcalliTraceLogger.new((line) => lines.push(line));
131
+ const tracker = new IoTraceTracker();
132
+ tracker.memWrite(tryAsU32(0x2000), new Uint8Array([0xab]));
133
+ tracker.memRead(tryAsU32(0x1000), new Uint8Array([0xcd]));
134
+ tracker.setReg(0, tryAsU64(0x100n));
135
+ logger.logHostActions(tracker, tryAsSmallGas(10000), tryAsSmallGas(9950));
136
+ assert.strictEqual(lines.length, 4);
137
+ assert.strictEqual(lines[0], "memread 0x00001000 len=1 -> 0xcd");
138
+ assert.strictEqual(lines[1], "memwrite 0x00002000 len=1 <- 0xab");
139
+ assert.strictEqual(lines[2], "setreg r00 <- 100");
140
+ assert.strictEqual(lines[3], "setgas <- 9950");
141
+ });
142
+ it("sorts reads by address", () => {
143
+ const lines = [];
144
+ const logger = EcalliTraceLogger.new((line) => lines.push(line));
145
+ const tracker = new IoTraceTracker();
146
+ tracker.memRead(tryAsU32(0x2000), new Uint8Array([0x01]));
147
+ tracker.memRead(tryAsU32(0x1000), new Uint8Array([0x02]));
148
+ logger.logHostActions(tracker, tryAsSmallGas(100), tryAsSmallGas(100));
149
+ assert.strictEqual(lines[0], "memread 0x00001000 len=1 -> 0x02");
150
+ assert.strictEqual(lines[1], "memread 0x00002000 len=1 -> 0x01");
151
+ });
152
+ it("skips setgas if unchanged", () => {
153
+ const lines = [];
154
+ const logger = EcalliTraceLogger.new((line) => lines.push(line));
155
+ const tracker = new IoTraceTracker();
156
+ logger.logHostActions(tracker, tryAsSmallGas(100), tryAsSmallGas(100));
157
+ assert.strictEqual(lines.length, 0);
158
+ });
159
+ });
160
+ describe("termination logging", () => {
161
+ it("logs HALT", () => {
162
+ const lines = [];
163
+ const logger = EcalliTraceLogger.new((line) => lines.push(line));
164
+ const regs = createRegisters(new Map([[0, 0x100n]]));
165
+ logger.logHalt(42, tryAsSmallGas(9920), regs);
166
+ assert.strictEqual(lines[0], "HALT pc=42 gas=9920 r00=100");
167
+ });
168
+ it("logs OOG", () => {
169
+ const lines = [];
170
+ const logger = EcalliTraceLogger.new((line) => lines.push(line));
171
+ const regs = createRegisters(new Map());
172
+ logger.logOog(100, tryAsSmallGas(0), regs);
173
+ assert.strictEqual(lines[0], "OOG pc=100 gas=0 ");
174
+ });
175
+ it("logs PANIC with argument", () => {
176
+ const lines = [];
177
+ const logger = EcalliTraceLogger.new((line) => lines.push(line));
178
+ const regs = createRegisters(new Map());
179
+ logger.logPanic(1, 50, tryAsSmallGas(500), regs);
180
+ assert.strictEqual(lines[0], "PANIC=1 pc=50 gas=500 ");
181
+ });
182
+ });
183
+ describe("noop logger", () => {
184
+ it("does not throw and produces no output", () => {
185
+ const logger = EcalliTraceLogger.noop();
186
+ logger.logContext("test");
187
+ logger.logProgram(new Uint8Array([1, 2, 3]), new Uint8Array());
188
+ logger.logSetGas(tryAsSmallGas(100));
189
+ });
190
+ it("returns null tracker", () => {
191
+ const logger = EcalliTraceLogger.noop();
192
+ const tracker = logger.tracker();
193
+ assert.strictEqual(tracker, null);
194
+ });
195
+ });
196
+ describe("IoTraceTracker", () => {
197
+ it("tracks memory reads", () => {
198
+ const tracker = new IoTraceTracker();
199
+ tracker.memRead(tryAsU32(0x1000), new Uint8Array([0x01, 0x02]));
200
+ tracker.memRead(tryAsU32(0x2000), new Uint8Array([0x03]));
201
+ assert.strictEqual(tracker.reads.length, 2);
202
+ assert.strictEqual(tracker.reads[0].address, 0x1000);
203
+ assert.strictEqual(tracker.reads[0].hex, "0x0102");
204
+ });
205
+ it("tracks memory writes", () => {
206
+ const tracker = new IoTraceTracker();
207
+ tracker.memWrite(tryAsU32(0x3000), new Uint8Array([0xaa, 0xbb]));
208
+ assert.strictEqual(tracker.writes.length, 1);
209
+ assert.strictEqual(tracker.writes[0].address, 0x3000);
210
+ assert.strictEqual(tracker.writes[0].hex, "0xaabb");
211
+ });
212
+ it("tracks register writes", () => {
213
+ const tracker = new IoTraceTracker();
214
+ tracker.setReg(5, tryAsU64(0x42n));
215
+ tracker.setReg(7, tryAsU64(0x100n));
216
+ assert.strictEqual(tracker.registers.size, 2);
217
+ assert.strictEqual(tracker.registers.get(5), tryAsU64(0x42n));
218
+ assert.strictEqual(tracker.registers.get(7), tryAsU64(0x100n));
219
+ });
220
+ it("clears all tracked data", () => {
221
+ const tracker = new IoTraceTracker();
222
+ tracker.memRead(tryAsU32(0x1000), new Uint8Array([0x01]));
223
+ tracker.memWrite(tryAsU32(0x2000), new Uint8Array([0x02]));
224
+ tracker.setReg(0, tryAsU64(0x100n));
225
+ tracker.clear();
226
+ assert.strictEqual(tracker.reads.length, 0);
227
+ assert.strictEqual(tracker.writes.length, 0);
228
+ assert.strictEqual(tracker.registers.size, 0);
229
+ });
230
+ });
231
+ });
@@ -1,8 +1,10 @@
1
1
  import { type U64 } from "#@typeberry/numbers";
2
2
  import type { IMemory, PageFault } from "#@typeberry/pvm-interface";
3
3
  import { OK, Result } from "#@typeberry/utils";
4
+ import type { IoTracker } from "./ecalli-io-tracker.js";
4
5
  export declare class HostCallMemory {
5
6
  private readonly memory;
7
+ ioTracker: IoTracker | null;
6
8
  constructor(memory: IMemory);
7
9
  /**
8
10
  * Save some bytes into memory under given address.
@@ -1 +1 @@
1
- {"version":3,"file":"host-call-memory.d.ts","sourceRoot":"","sources":["../../../../../packages/core/pvm-host-calls/host-call-memory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,GAAG,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE9C,qBAAa,cAAc;IACb,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,OAAO;IAE5C;;;;;OAKG;IACH,SAAS,CAAC,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,GAAG,MAAM,CAAC,EAAE,EAAE,SAAS,CAAC;IAYpE;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,GAAG,MAAM,CAAC,EAAE,EAAE,SAAS,CAAC;CAWrE"}
1
+ {"version":3,"file":"host-call-memory.d.ts","sourceRoot":"","sources":["../../../../../packages/core/pvm-host-calls/host-call-memory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,GAAG,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAExD,qBAAa,cAAc;IAIb,OAAO,CAAC,QAAQ,CAAC,MAAM;IAF5B,SAAS,EAAE,SAAS,GAAG,IAAI,CAAQ;gBAEb,MAAM,EAAE,OAAO;IAE5C;;;;;OAKG;IACH,SAAS,CAAC,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,GAAG,MAAM,CAAC,EAAE,EAAE,SAAS,CAAC;IAgBpE;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,GAAG,MAAM,CAAC,EAAE,EAAE,SAAS,CAAC;CAerE"}
@@ -2,6 +2,8 @@ import { tryAsU32 } from "#@typeberry/numbers";
2
2
  import { OK, Result } from "#@typeberry/utils";
3
3
  export class HostCallMemory {
4
4
  memory;
5
+ // Track successful memory reads and writes.
6
+ ioTracker = null;
5
7
  constructor(memory) {
6
8
  this.memory = memory;
7
9
  }
@@ -19,7 +21,11 @@ export class HostCallMemory {
19
21
  //
20
22
  // https://graypaper.fluffylabs.dev/#/ab2cdbd/25ed0025ed00?v=0.7.2
21
23
  const address = tryAsU32(Number(regAddress & 0xffffffffn));
22
- return this.memory.store(address, bytes);
24
+ const result = this.memory.store(address, bytes);
25
+ if (result.isOk && this.ioTracker !== null) {
26
+ this.ioTracker.memWrite(address, bytes);
27
+ }
28
+ return result;
23
29
  }
24
30
  /**
25
31
  * Read some bytes from memory under given address.
@@ -35,6 +41,10 @@ export class HostCallMemory {
35
41
  //
36
42
  // NOTE we are taking the the lower U32 part of the register, hence it's safe.
37
43
  const address = tryAsU32(Number(regAddress & 0xffffffffn));
38
- return this.memory.read(address, output);
44
+ const result = this.memory.read(address, output);
45
+ if (result.isOk && this.ioTracker !== null) {
46
+ this.ioTracker.memRead(address, output);
47
+ }
48
+ return result;
39
49
  }
40
50
  }
@@ -1,7 +1,10 @@
1
1
  import { type U64 } from "#@typeberry/numbers";
2
+ import type { IoTracker } from "./ecalli-io-tracker.js";
2
3
  export declare class HostCallRegisters {
3
4
  private readonly bytes;
5
+ private readonly raw;
4
6
  private readonly registers;
7
+ ioTracker: IoTracker | null;
5
8
  constructor(bytes: Uint8Array);
6
9
  /** Get U64 register value. */
7
10
  get(registerIndex: number): U64;
@@ -9,5 +12,8 @@ export declare class HostCallRegisters {
9
12
  set(registerIndex: number, value: U64): void;
10
13
  /** Get all registers encoded into little-endian bytes. */
11
14
  getEncoded(): Uint8Array;
15
+ /** Ovewrite all encoded registers. */
16
+ setEncoded(bytes: Uint8Array): void;
17
+ toString(): string;
12
18
  }
13
19
  //# sourceMappingURL=host-call-registers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"host-call-registers.d.ts","sourceRoot":"","sources":["../../../../../packages/core/pvm-host-calls/host-call-registers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAGxD,qBAAa,iBAAiB;IAGhB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAFlC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;gBAER,KAAK,EAAE,UAAU;IAI9C,8BAA8B;IAC9B,GAAG,CAAC,aAAa,EAAE,MAAM,GAAG,GAAG;IAI/B,8BAA8B;IAC9B,GAAG,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG;IAIrC,0DAA0D;IAC1D,UAAU,IAAI,UAAU;CAGzB"}
1
+ {"version":3,"file":"host-call-registers.d.ts","sourceRoot":"","sources":["../../../../../packages/core/pvm-host-calls/host-call-registers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAGxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAExD,qBAAa,iBAAiB;IAMhB,OAAO,CAAC,QAAQ,CAAC,KAAK;IALlC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAa;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;IAE9B,SAAS,EAAE,SAAS,GAAG,IAAI,CAAQ;gBAEb,KAAK,EAAE,UAAU;IAK9C,8BAA8B;IAC9B,GAAG,CAAC,aAAa,EAAE,MAAM,GAAG,GAAG;IAI/B,8BAA8B;IAC9B,GAAG,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG;IAOrC,0DAA0D;IAC1D,UAAU,IAAI,UAAU;IAIxB,sCAAsC;IACtC,UAAU,CAAC,KAAK,EAAE,UAAU;IAK5B,QAAQ;CAWT"}
@@ -1,10 +1,15 @@
1
1
  import { tryAsU64 } from "#@typeberry/numbers";
2
2
  import { REGISTER_BYTE_SIZE } from "#@typeberry/pvm-interface";
3
+ import { check } from "#@typeberry/utils";
3
4
  export class HostCallRegisters {
4
5
  bytes;
6
+ raw;
5
7
  registers;
8
+ // Track register modifications.
9
+ ioTracker = null;
6
10
  constructor(bytes) {
7
11
  this.bytes = bytes;
12
+ this.raw = bytes;
8
13
  this.registers = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
9
14
  }
10
15
  /** Get U64 register value. */
@@ -14,9 +19,28 @@ export class HostCallRegisters {
14
19
  /** Set U64 register value. */
15
20
  set(registerIndex, value) {
16
21
  this.registers.setBigUint64(registerIndex * REGISTER_BYTE_SIZE, value, true);
22
+ if (this.ioTracker !== null) {
23
+ this.ioTracker.setReg(registerIndex, value);
24
+ }
17
25
  }
18
26
  /** Get all registers encoded into little-endian bytes. */
19
27
  getEncoded() {
20
28
  return this.bytes;
21
29
  }
30
+ /** Ovewrite all encoded registers. */
31
+ setEncoded(bytes) {
32
+ check `${bytes.length === this.raw.length} Invalid registers array: ${bytes.length} vs ${this.raw.length}`;
33
+ this.raw.set(bytes, 0);
34
+ }
35
+ toString() {
36
+ const elementCount = this.raw.byteLength / REGISTER_BYTE_SIZE;
37
+ const values = new BigUint64Array(this.raw.buffer, this.raw.byteOffset, elementCount);
38
+ const entries = [];
39
+ for (const [idx, value] of values.entries()) {
40
+ if (value !== 0n) {
41
+ entries.push(`r${idx.toString().padStart(2, "0")}=${value.toString(16)}`);
42
+ }
43
+ }
44
+ return entries.join(" ");
45
+ }
22
46
  }
@@ -0,0 +1,31 @@
1
+ import { type Gas, Status } from "#@typeberry/pvm-interface";
2
+ import { EcalliTraceLogger } from "./ecalli-trace-logger.js";
3
+ import type { HostCalls } from "./host-calls.js";
4
+ import type { PvmInstanceManager } from "./pvm-instance-manager.js";
5
+ declare class ReturnValue {
6
+ consumedGas: Gas;
7
+ status: Status | null;
8
+ memorySlice: Uint8Array | null;
9
+ private constructor();
10
+ static fromStatus(consumedGas: Gas, status: Status): ReturnValue;
11
+ static fromMemorySlice(consumedGas: Gas, memorySlice: Uint8Array): ReturnValue;
12
+ hasMemorySlice(): this is this & {
13
+ status: null;
14
+ memorySlice: Uint8Array;
15
+ };
16
+ hasStatus(): this is this & {
17
+ status: Status;
18
+ memorySlice: null;
19
+ };
20
+ }
21
+ export declare class HostCallsExecutor {
22
+ private pvmInstanceManager;
23
+ private hostCalls;
24
+ private ioTracer;
25
+ constructor(pvmInstanceManager: PvmInstanceManager, hostCalls: HostCalls, ioTracer?: EcalliTraceLogger | null);
26
+ private getReturnValue;
27
+ private execute;
28
+ runProgram(program: Uint8Array, args: Uint8Array, initialPc: number, initialGas: Gas): Promise<ReturnValue>;
29
+ }
30
+ export {};
31
+ //# sourceMappingURL=host-calls-executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-calls-executor.d.ts","sourceRoot":"","sources":["../../../../../packages/core/pvm-host-calls/host-calls-executor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,GAAG,EAAwB,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAElF,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAI7D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAEpE,cAAM,WAAW;IAEN,WAAW,EAAE,GAAG;IAChB,MAAM,EAAE,MAAM,GAAG,IAAI;IACrB,WAAW,EAAE,UAAU,GAAG,IAAI;IAHvC,OAAO;IAWP,MAAM,CAAC,UAAU,CAAC,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM;IAIlD,MAAM,CAAC,eAAe,CAAC,WAAW,EAAE,GAAG,EAAE,WAAW,EAAE,UAAU;IAIhE,cAAc,IAAI,IAAI,IAAI,IAAI,GAAG;QAAE,MAAM,EAAE,IAAI,CAAC;QAAC,WAAW,EAAE,UAAU,CAAA;KAAE;IAI1E,SAAS,IAAI,IAAI,IAAI,IAAI,GAAG;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,IAAI,CAAA;KAAE;CAGlE;AAED,qBAAa,iBAAiB;IAE1B,OAAO,CAAC,kBAAkB;IAC1B,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,QAAQ;gBAFR,kBAAkB,EAAE,kBAAkB,EACtC,SAAS,EAAE,SAAS,EACpB,QAAQ,GAAE,iBAAiB,GAAG,IAAiC;IAGzE,OAAO,CAAC,cAAc;YAoCR,OAAO;IAiFf,UAAU,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC;CAWlH"}
@@ -0,0 +1,137 @@
1
+ import { Status } from "#@typeberry/pvm-interface";
2
+ import { assertNever, check, safeAllocUint8Array } from "#@typeberry/utils";
3
+ import { EcalliTraceLogger } from "./ecalli-trace-logger.js";
4
+ import { PvmExecution, tryAsHostCallIndex } from "./host-call-handler.js";
5
+ import { HostCallMemory } from "./host-call-memory.js";
6
+ import { HostCallRegisters } from "./host-call-registers.js";
7
+ class ReturnValue {
8
+ consumedGas;
9
+ status;
10
+ memorySlice;
11
+ constructor(consumedGas, status, memorySlice) {
12
+ this.consumedGas = consumedGas;
13
+ this.status = status;
14
+ this.memorySlice = memorySlice;
15
+ check `
16
+ ${(status === null && memorySlice !== null) || (status !== null && memorySlice === null)}
17
+ 'status' and 'memorySlice' must not both be null or both be non-null — exactly one must be provided
18
+ `;
19
+ }
20
+ static fromStatus(consumedGas, status) {
21
+ return new ReturnValue(consumedGas, status, null);
22
+ }
23
+ static fromMemorySlice(consumedGas, memorySlice) {
24
+ return new ReturnValue(consumedGas, null, memorySlice);
25
+ }
26
+ hasMemorySlice() {
27
+ return this.memorySlice instanceof Uint8Array && this.status === null;
28
+ }
29
+ hasStatus() {
30
+ return !this.hasMemorySlice();
31
+ }
32
+ }
33
+ export class HostCallsExecutor {
34
+ pvmInstanceManager;
35
+ hostCalls;
36
+ ioTracer;
37
+ constructor(pvmInstanceManager, hostCalls, ioTracer = EcalliTraceLogger.create()) {
38
+ this.pvmInstanceManager = pvmInstanceManager;
39
+ this.hostCalls = hostCalls;
40
+ this.ioTracer = ioTracer;
41
+ }
42
+ getReturnValue(status, pvmInstance, registers, memory) {
43
+ const gasConsumed = pvmInstance.gas.used();
44
+ const pc = pvmInstance.getPC();
45
+ const gas = pvmInstance.gas.get();
46
+ if (status === Status.OOG) {
47
+ this.ioTracer?.logOog(pc, gas, registers);
48
+ return ReturnValue.fromStatus(gasConsumed, status);
49
+ }
50
+ if (status === Status.HALT) {
51
+ this.ioTracer?.logHalt(pc, gas, registers);
52
+ const address = registers.get(7);
53
+ const length = Number(registers.get(8) & 0xffffffffn);
54
+ const result = safeAllocUint8Array(length);
55
+ const loadResult = memory.loadInto(result, address);
56
+ if (loadResult.isError) {
57
+ return ReturnValue.fromMemorySlice(gasConsumed, new Uint8Array());
58
+ }
59
+ return ReturnValue.fromMemorySlice(gasConsumed, result);
60
+ }
61
+ this.ioTracer?.logPanic(pvmInstance.getExitParam() ?? 0, pc, gas, registers);
62
+ return ReturnValue.fromStatus(gasConsumed, Status.PANIC);
63
+ }
64
+ async execute(pvmInstance) {
65
+ const ioTracker = this.ioTracer?.tracker() ?? null;
66
+ const registers = new HostCallRegisters(pvmInstance.registers.getAllEncoded());
67
+ registers.ioTracker = ioTracker;
68
+ const memory = new HostCallMemory(pvmInstance.memory);
69
+ memory.ioTracker = ioTracker;
70
+ const gas = pvmInstance.gas;
71
+ // log start of execution (note the PVM initialisation should be logged already)
72
+ this.ioTracer?.logStart(pvmInstance.getPC(), pvmInstance.gas.get(), registers);
73
+ for (;;) {
74
+ // execute program as much as we can
75
+ pvmInstance.runProgram();
76
+ // and update the PVM state
77
+ registers.setEncoded(pvmInstance.registers.getAllEncoded());
78
+ const status = pvmInstance.getStatus();
79
+ const pc = pvmInstance.getPC();
80
+ const exitParam = pvmInstance.getExitParam() ?? -1;
81
+ if (status !== Status.HOST) {
82
+ return this.getReturnValue(status, pvmInstance, registers, memory);
83
+ }
84
+ // get the PVM state now
85
+ check `
86
+ ${exitParam !== -1}
87
+ "We know that the exit param is not null, because the status is 'Status.HOST'
88
+ `;
89
+ const hostCallIndex = tryAsHostCallIndex(exitParam);
90
+ // retrieve the host call
91
+ const hostCall = this.hostCalls.get(hostCallIndex);
92
+ const basicGasCost = typeof hostCall.basicGasCost === "number" ? hostCall.basicGasCost : hostCall.basicGasCost(registers);
93
+ // calculate gas
94
+ const gasBefore = gas.get();
95
+ const underflow = gas.sub(basicGasCost);
96
+ const pcLog = `[PC: ${pc}]`;
97
+ if (underflow) {
98
+ const gasAfterBasicGas = gas.get();
99
+ this.hostCalls.traceHostCall(`${pcLog} OOG`, hostCallIndex, hostCall, registers, gasAfterBasicGas);
100
+ this.ioTracer?.logSetGas(gasAfterBasicGas);
101
+ return this.getReturnValue(Status.OOG, pvmInstance, registers, memory);
102
+ }
103
+ this.ioTracer?.logEcalli(hostCallIndex, pc, gasBefore, registers);
104
+ this.hostCalls.traceHostCall(`${pcLog} Invoking`, hostCallIndex, hostCall, registers, gasBefore);
105
+ ioTracker?.clear();
106
+ const result = await hostCall.execute(gas, registers, memory);
107
+ const gasAfter = gas.get();
108
+ this.ioTracer?.logHostActions(ioTracker, gasBefore, gasAfter);
109
+ this.hostCalls.traceHostCall(result === undefined ? `${pcLog} Result` : `${pcLog} Status(${PvmExecution[result]})`, hostCallIndex, hostCall, registers, gasAfter);
110
+ pvmInstance.registers.setAllEncoded(registers.getEncoded());
111
+ if (result === PvmExecution.Halt) {
112
+ return this.getReturnValue(Status.HALT, pvmInstance, registers, memory);
113
+ }
114
+ if (result === PvmExecution.Panic) {
115
+ return this.getReturnValue(Status.PANIC, pvmInstance, registers, memory);
116
+ }
117
+ if (result === PvmExecution.OOG) {
118
+ return this.getReturnValue(Status.OOG, pvmInstance, registers, memory);
119
+ }
120
+ if (result === undefined) {
121
+ continue;
122
+ }
123
+ assertNever(result);
124
+ }
125
+ }
126
+ async runProgram(program, args, initialPc, initialGas) {
127
+ const pvmInstance = await this.pvmInstanceManager.getInstance();
128
+ pvmInstance.resetJam(program, args, initialPc, initialGas);
129
+ try {
130
+ this.ioTracer?.logProgram(program, args);
131
+ return await this.execute(pvmInstance);
132
+ }
133
+ finally {
134
+ this.pvmInstanceManager.releaseInstance(pvmInstance);
135
+ }
136
+ }
137
+ }
@@ -1,29 +1,23 @@
1
- import { type Gas, Status } from "#@typeberry/pvm-interface";
2
- import type { HostCallsManager } from "./host-calls-manager.js";
3
- import type { InterpreterInstanceManager } from "./interpreter-instance-manager.js";
4
- declare class ReturnValue {
5
- consumedGas: Gas;
6
- status: Status | null;
7
- memorySlice: Uint8Array | null;
8
- private constructor();
9
- static fromStatus(consumedGas: Gas, status: Status): ReturnValue;
10
- static fromMemorySlice(consumedGas: Gas, memorySlice: Uint8Array): ReturnValue;
11
- hasMemorySlice(): this is this & {
12
- status: null;
13
- memorySlice: Uint8Array;
14
- };
15
- hasStatus(): this is this & {
16
- status: Status;
17
- memorySlice: null;
18
- };
19
- }
1
+ import { type Gas } from "#@typeberry/pvm-interface";
2
+ import { type HostCallHandler, type HostCallIndex, type PvmExecution } from "./host-call-handler.js";
3
+ import type { HostCallRegisters } from "./host-call-registers.js";
4
+ /** Container for all available host calls. */
20
5
  export declare class HostCalls {
21
- private pvmInstanceManager;
22
- private hostCalls;
23
- constructor(pvmInstanceManager: InterpreterInstanceManager, hostCalls: HostCallsManager);
24
- private getReturnValue;
25
- private execute;
26
- runProgram(program: Uint8Array, args: Uint8Array, initialPc: number, initialGas: Gas): Promise<ReturnValue>;
6
+ private readonly hostCalls;
7
+ private readonly missing;
8
+ constructor({ missing, handlers, }: {
9
+ missing: HostCallHandler;
10
+ handlers?: HostCallHandler[];
11
+ });
12
+ /** Get a host call by index. */
13
+ get(hostCallIndex: HostCallIndex): HostCallHandler;
14
+ traceHostCall(context: string, hostCallIndex: HostCallIndex, hostCallHandler: HostCallHandler, registers: HostCallRegisters, gas: Gas): void;
15
+ }
16
+ export declare class NoopMissing implements HostCallHandler {
17
+ index: number & import("@typeberry/numbers").WithBytesRepresentation<4> & import("@typeberry/utils").WithOpaque<"HostCallIndex[U32]">;
18
+ basicGasCost: number & import("@typeberry/numbers").WithBytesRepresentation<4> & import("@typeberry/utils").WithOpaque<"SmallGas[U32]">;
19
+ currentServiceId: import("@typeberry/numbers").U32;
20
+ tracedRegisters: never[];
21
+ execute(): Promise<undefined | PvmExecution>;
27
22
  }
28
- export {};
29
23
  //# sourceMappingURL=host-calls.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"host-calls.d.ts","sourceRoot":"","sources":["../../../../../packages/core/pvm-host-calls/host-calls.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,GAAG,EAAwB,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAKlF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,mCAAmC,CAAC;AAEpF,cAAM,WAAW;IAEN,WAAW,EAAE,GAAG;IAChB,MAAM,EAAE,MAAM,GAAG,IAAI;IACrB,WAAW,EAAE,UAAU,GAAG,IAAI;IAHvC,OAAO;IAWP,MAAM,CAAC,UAAU,CAAC,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM;IAIlD,MAAM,CAAC,eAAe,CAAC,WAAW,EAAE,GAAG,EAAE,WAAW,EAAE,UAAU;IAIhE,cAAc,IAAI,IAAI,IAAI,IAAI,GAAG;QAAE,MAAM,EAAE,IAAI,CAAC;QAAC,WAAW,EAAE,UAAU,CAAA;KAAE;IAI1E,SAAS,IAAI,IAAI,IAAI,IAAI,GAAG;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,IAAI,CAAA;KAAE;CAGlE;AACD,qBAAa,SAAS;IAElB,OAAO,CAAC,kBAAkB;IAC1B,OAAO,CAAC,SAAS;gBADT,kBAAkB,EAAE,0BAA0B,EAC9C,SAAS,EAAE,gBAAgB;IAGrC,OAAO,CAAC,cAAc;YA2BR,OAAO;IAiEf,UAAU,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC;CASlH"}
1
+ {"version":3,"file":"host-calls.d.ts","sourceRoot":"","sources":["../../../../../packages/core/pvm-host-calls/host-calls.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,GAAG,EAAiB,MAAM,0BAA0B,CAAC;AAEnE,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,YAAY,EAElB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAIlE,8CAA8C;AAC9C,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA6C;IACvE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAEb,EACV,OAAO,EACP,QAAa,GACd,EAAE;QACD,OAAO,EAAE,eAAe,CAAC;QACzB,QAAQ,CAAC,EAAE,eAAe,EAAE,CAAC;KAC9B;IASD,gCAAgC;IAChC,GAAG,CAAC,aAAa,EAAE,aAAa,GAAG,eAAe;IAIlD,aAAa,CACX,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,aAAa,EAC5B,eAAe,EAAE,eAAe,EAChC,SAAS,EAAE,iBAAiB,EAC5B,GAAG,EAAE,GAAG;CAkBX;AAED,qBAAa,WAAY,YAAW,eAAe;IACjD,KAAK,iIAAmC;IACxC,YAAY,4HAAoB;IAChC,gBAAgB,mCAAe;IAC/B,eAAe,UAAM;IAEf,OAAO,IAAI,OAAO,CAAC,SAAS,GAAG,YAAY,CAAC;CAGnD"}