@typeberry/lib 0.5.2-9afefa7 → 0.5.2-ab56433
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 +1 -1
- package/packages/core/pvm-host-calls/bin.js +6 -6
- package/packages/core/pvm-host-calls/ecalli-io-tracker.d.ts +32 -0
- package/packages/core/pvm-host-calls/ecalli-io-tracker.d.ts.map +1 -0
- package/packages/core/pvm-host-calls/ecalli-io-tracker.js +14 -0
- package/packages/core/pvm-host-calls/ecalli-trace-logger.d.ts +139 -0
- package/packages/core/pvm-host-calls/ecalli-trace-logger.d.ts.map +1 -0
- package/packages/core/pvm-host-calls/ecalli-trace-logger.js +209 -0
- package/packages/core/pvm-host-calls/ecalli-trace-logger.test.d.ts +2 -0
- package/packages/core/pvm-host-calls/ecalli-trace-logger.test.d.ts.map +1 -0
- package/packages/core/pvm-host-calls/ecalli-trace-logger.test.js +231 -0
- package/packages/core/pvm-host-calls/host-call-memory.d.ts +2 -0
- package/packages/core/pvm-host-calls/host-call-memory.d.ts.map +1 -1
- package/packages/core/pvm-host-calls/host-call-memory.js +12 -2
- package/packages/core/pvm-host-calls/host-call-registers.d.ts +6 -0
- package/packages/core/pvm-host-calls/host-call-registers.d.ts.map +1 -1
- package/packages/core/pvm-host-calls/host-call-registers.js +24 -0
- package/packages/core/pvm-host-calls/host-calls-executor.d.ts +31 -0
- package/packages/core/pvm-host-calls/host-calls-executor.d.ts.map +1 -0
- package/packages/core/pvm-host-calls/host-calls-executor.js +137 -0
- package/packages/core/pvm-host-calls/host-calls.d.ts +20 -26
- package/packages/core/pvm-host-calls/host-calls.d.ts.map +1 -1
- package/packages/core/pvm-host-calls/host-calls.js +40 -112
- package/packages/core/pvm-host-calls/index.d.ts +7 -6
- package/packages/core/pvm-host-calls/index.d.ts.map +1 -1
- package/packages/core/pvm-host-calls/index.js +7 -6
- package/packages/core/pvm-host-calls/{interpreter-instance-manager.d.ts → pvm-instance-manager.d.ts} +3 -3
- package/packages/core/pvm-host-calls/pvm-instance-manager.d.ts.map +1 -0
- package/packages/core/pvm-host-calls/{interpreter-instance-manager.js → pvm-instance-manager.js} +2 -2
- package/packages/jam/safrole/bandersnatch-vrf.d.ts +2 -0
- package/packages/jam/safrole/bandersnatch-vrf.d.ts.map +1 -1
- package/packages/jam/safrole/bandersnatch-vrf.js +11 -0
- package/packages/jam/safrole/bandersnatch-wasm.d.ts +1 -0
- package/packages/jam/safrole/bandersnatch-wasm.d.ts.map +1 -1
- package/packages/jam/safrole/bandersnatch-wasm.js +3 -0
- package/packages/jam/safrole/safrole-seal.d.ts +1 -3
- package/packages/jam/safrole/safrole-seal.d.ts.map +1 -1
- package/packages/jam/safrole/safrole-seal.js +14 -25
- package/packages/jam/transition/accumulate/pvm-executor.d.ts.map +1 -1
- package/packages/jam/transition/accumulate/pvm-executor.js +2 -2
- package/packages/core/pvm-host-calls/host-calls-manager.d.ts +0 -23
- package/packages/core/pvm-host-calls/host-calls-manager.d.ts.map +0 -1
- package/packages/core/pvm-host-calls/host-calls-manager.js +0 -44
- package/packages/core/pvm-host-calls/interpreter-instance-manager.d.ts.map +0 -1
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { PvmBackend } from "#@typeberry/config";
|
|
2
2
|
import { tryAsGas } from "#@typeberry/pvm-interface";
|
|
3
|
-
import { HostCalls } from "./host-calls.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
const hostCalls = new
|
|
7
|
-
const pvmInstanceManager = await
|
|
8
|
-
const pvmHostCallExtension = new
|
|
3
|
+
import { HostCalls, NoopMissing } from "./host-calls.js";
|
|
4
|
+
import { HostCallsExecutor } from "./host-calls-executor.js";
|
|
5
|
+
import { PvmInstanceManager } from "./pvm-instance-manager.js";
|
|
6
|
+
const hostCalls = new HostCalls({ missing: new NoopMissing(), handlers: [] });
|
|
7
|
+
const pvmInstanceManager = await PvmInstanceManager.new(PvmBackend.BuiltIn);
|
|
8
|
+
const pvmHostCallExtension = new HostCallsExecutor(pvmInstanceManager, hostCalls);
|
|
9
9
|
const program = new Uint8Array([
|
|
10
10
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x1, 0x0, 0x0, 0xf9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0xd9, 0x5, 0x12,
|
|
11
11
|
0x0, 0x0, 0x0, 0x5, 0x11, 0x0, 0x0, 0x0, 0x5, 0xa3, 0x0, 0x0, 0x0, 0x5, 0xc6, 0x0, 0x4, 0x7, 0x13, 0x0, 0x2, 0x11,
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { U32, U64 } from "#@typeberry/numbers";
|
|
2
|
+
import type { RegisterIndex } from "#@typeberry/pvm-interpreter";
|
|
3
|
+
/**
|
|
4
|
+
* Interface for tracking PVM I/O operations during host call execution.
|
|
5
|
+
*
|
|
6
|
+
* Implementations record memory reads/writes and register modifications
|
|
7
|
+
* for debugging, tracing, or replay purposes.
|
|
8
|
+
*/
|
|
9
|
+
export interface IoTracker {
|
|
10
|
+
/** Record a register write operation. */
|
|
11
|
+
setReg(idx: number, val: U64): void;
|
|
12
|
+
/** Record a memory read operation. */
|
|
13
|
+
memRead(address: U32, data: Uint8Array): void;
|
|
14
|
+
/** Record a memory write operation. */
|
|
15
|
+
memWrite(address: U32, data: Uint8Array): void;
|
|
16
|
+
/** Clear all recorded operations. */
|
|
17
|
+
clear(): void;
|
|
18
|
+
}
|
|
19
|
+
/** Create a no-op tracker that discards all operations. */
|
|
20
|
+
export declare function noopTracker(): NoopIoTracker;
|
|
21
|
+
/**
|
|
22
|
+
* No-op implementation that discards all tracked operations.
|
|
23
|
+
* Used when I/O tracing is disabled.
|
|
24
|
+
*/
|
|
25
|
+
declare class NoopIoTracker implements IoTracker {
|
|
26
|
+
clear(): void;
|
|
27
|
+
setReg(_idx: RegisterIndex, _val: U64): void;
|
|
28
|
+
memRead(_address: U32, _data: Uint8Array): void;
|
|
29
|
+
memWrite(_address: U32, _data: Uint8Array): void;
|
|
30
|
+
}
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=ecalli-io-tracker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ecalli-io-tracker.d.ts","sourceRoot":"","sources":["../../../../../packages/core/pvm-host-calls/ecalli-io-tracker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAEhE;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,yCAAyC;IACzC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC;IACpC,sCAAsC;IACtC,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9C,uCAAuC;IACvC,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;IAC/C,qCAAqC;IACrC,KAAK,IAAI,IAAI,CAAC;CACf;AAED,2DAA2D;AAC3D,wBAAgB,WAAW,kBAE1B;AAED;;;GAGG;AACH,cAAM,aAAc,YAAW,SAAS;IACtC,KAAK,IAAI,IAAI;IACb,MAAM,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,GAAG,IAAI;IAC5C,OAAO,CAAC,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI;IAC/C,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI;CACjD"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Create a no-op tracker that discards all operations. */
|
|
2
|
+
export function noopTracker() {
|
|
3
|
+
return new NoopIoTracker();
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* No-op implementation that discards all tracked operations.
|
|
7
|
+
* Used when I/O tracing is disabled.
|
|
8
|
+
*/
|
|
9
|
+
class NoopIoTracker {
|
|
10
|
+
clear() { }
|
|
11
|
+
setReg(_idx, _val) { }
|
|
12
|
+
memRead(_address, _data) { }
|
|
13
|
+
memWrite(_address, _data) { }
|
|
14
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type { U32, U64 } from "#@typeberry/numbers";
|
|
2
|
+
import type { Gas } from "#@typeberry/pvm-interface";
|
|
3
|
+
import type { IoTracker } from "./ecalli-io-tracker.js";
|
|
4
|
+
import type { HostCallIndex } from "./host-call-handler.js";
|
|
5
|
+
import type { HostCallRegisters } from "./host-call-registers.js";
|
|
6
|
+
/**
|
|
7
|
+
* Output function type for IO trace logging.
|
|
8
|
+
* Each call should output a single line.
|
|
9
|
+
*/
|
|
10
|
+
export type IoTraceOutput = (line: string) => void;
|
|
11
|
+
/**
|
|
12
|
+
* Ecalli PVM IO Trace Logger.
|
|
13
|
+
*
|
|
14
|
+
* Implements the logging format specified for PVM execution tracing.
|
|
15
|
+
* This format is designed to be:
|
|
16
|
+
* - Human-readable, newline-delimited text
|
|
17
|
+
* - Self-contained for stateless re-execution
|
|
18
|
+
* - Comparable using simple textual diff tools
|
|
19
|
+
*
|
|
20
|
+
* @see https://github.com/tomusdrw/JIPs/pull/2
|
|
21
|
+
*/
|
|
22
|
+
export declare class EcalliTraceLogger {
|
|
23
|
+
private readonly output;
|
|
24
|
+
/** Returns a tracker for IO operations. */
|
|
25
|
+
tracker(): IoTraceTracker | null;
|
|
26
|
+
/**
|
|
27
|
+
* Create an IoTraceLogger that outputs to the `ecalli` module logger.
|
|
28
|
+
*
|
|
29
|
+
* Returns `null` if the `ecalli` logger is not configured for at least TRACE level.
|
|
30
|
+
* Enable with: `JAM_LOG=ecalli=trace` or `JAM_LOG=trace`
|
|
31
|
+
*/
|
|
32
|
+
static create(): EcalliTraceLogger | null;
|
|
33
|
+
/**
|
|
34
|
+
* Create a no-op IoTraceLogger that discards all output.
|
|
35
|
+
* Used when tracing is disabled.
|
|
36
|
+
*/
|
|
37
|
+
static noop(): EcalliTraceLogger;
|
|
38
|
+
static new(output: IoTraceOutput): EcalliTraceLogger;
|
|
39
|
+
private constructor();
|
|
40
|
+
/**
|
|
41
|
+
* Log optional context lines (implementation metadata, execution environment).
|
|
42
|
+
*/
|
|
43
|
+
logContext(context: string): void;
|
|
44
|
+
/**
|
|
45
|
+
* Log the program blob being executed and the write data (if any)
|
|
46
|
+
*
|
|
47
|
+
* Format: `program {hex-encoded-program-with-metadata}`
|
|
48
|
+
* Format: `memwrite {hex-encoded-address} len={blob-byte-length} <- {hex-encoded-bytes}`
|
|
49
|
+
*/
|
|
50
|
+
logProgram(program: Uint8Array, args: Uint8Array): void;
|
|
51
|
+
/**
|
|
52
|
+
* Log initial execution state (prelude).
|
|
53
|
+
*
|
|
54
|
+
* Format: `start pc={pc} gas={gas} {register-dump}`
|
|
55
|
+
*/
|
|
56
|
+
logStart(pc: number, gas: Gas, registers: HostCallRegisters): void;
|
|
57
|
+
/**
|
|
58
|
+
* Log ecalli invocation with register dump.
|
|
59
|
+
*
|
|
60
|
+
* Format: `ecalli={index} pc={pc} gas={gas} {register-dump}`
|
|
61
|
+
*/
|
|
62
|
+
logEcalli(index: HostCallIndex, pc: number, gas: Gas, registers: HostCallRegisters): void;
|
|
63
|
+
/**
|
|
64
|
+
* Log memory read operation.
|
|
65
|
+
*
|
|
66
|
+
* Format: `memread {hex-encoded-address} len={blob-byte-length} -> {hex-encoded-data-read}`
|
|
67
|
+
*/
|
|
68
|
+
logMemRead(address: number, len: number, data: string): void;
|
|
69
|
+
/**
|
|
70
|
+
* Log memory write operation.
|
|
71
|
+
*
|
|
72
|
+
* Format: `memwrite {hex-encoded-address} len={blob-byte-length} <- {hex-encoded-bytes}`
|
|
73
|
+
*/
|
|
74
|
+
logMemWrite(address: number, len: number, data: string): void;
|
|
75
|
+
/**
|
|
76
|
+
* Log register write operation.
|
|
77
|
+
*
|
|
78
|
+
* Format: `setreg r{idx} <- {hex-encoded-value}`
|
|
79
|
+
*/
|
|
80
|
+
logSetReg(index: number, value: bigint): void;
|
|
81
|
+
/**
|
|
82
|
+
* Log gas overwrite operation.
|
|
83
|
+
*
|
|
84
|
+
* Format: `setgas <- {gas}`
|
|
85
|
+
*/
|
|
86
|
+
logSetGas(gas: Gas): void;
|
|
87
|
+
/**
|
|
88
|
+
* Log all host actions from a single ecalli invocation.
|
|
89
|
+
* Actions are logged in the order specified by JIP-6:
|
|
90
|
+
* 1. Memory reads (sorted by address)
|
|
91
|
+
* 2. Memory writes (sorted by address)
|
|
92
|
+
* 3. Register writes (sorted by index)
|
|
93
|
+
* 4. Gas overwrite
|
|
94
|
+
*/
|
|
95
|
+
logHostActions(ioTracker: IoTraceTracker | null, gasBefore: Gas, gasAfter: Gas): void;
|
|
96
|
+
/**
|
|
97
|
+
* Log PANIC termination.
|
|
98
|
+
*
|
|
99
|
+
* Format: `PANIC={argument} pc={pc} gas={gas} {register-dump}`
|
|
100
|
+
*/
|
|
101
|
+
logPanic(argument: number, pc: number, gas: Gas, registers: HostCallRegisters): void;
|
|
102
|
+
/**
|
|
103
|
+
* Log OOG (out of gas) termination.
|
|
104
|
+
*
|
|
105
|
+
* Format: `OOG pc={pc} gas={gas} {register-dump}`
|
|
106
|
+
*/
|
|
107
|
+
logOog(pc: number, gas: Gas, registers: HostCallRegisters): void;
|
|
108
|
+
/**
|
|
109
|
+
* Log HALT termination.
|
|
110
|
+
*
|
|
111
|
+
* Format: `HALT pc={pc} gas={gas} {register-dump}`
|
|
112
|
+
*/
|
|
113
|
+
logHalt(pc: number, gas: Gas, registers: HostCallRegisters): void;
|
|
114
|
+
}
|
|
115
|
+
type MemoryOperation = {
|
|
116
|
+
address: number;
|
|
117
|
+
hex: string;
|
|
118
|
+
len: number;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* IoTracker implementation that records all I/O operations for trace logging.
|
|
122
|
+
*
|
|
123
|
+
* Stores memory reads, writes, and register modifications as hex-encoded strings
|
|
124
|
+
* for output via IoTraceLogger.
|
|
125
|
+
*/
|
|
126
|
+
export declare class IoTraceTracker implements IoTracker {
|
|
127
|
+
/** Recorded memory read operations (address + hex data + len). */
|
|
128
|
+
reads: MemoryOperation[];
|
|
129
|
+
/** Recorded memory write operations (address + hex data + len). */
|
|
130
|
+
writes: MemoryOperation[];
|
|
131
|
+
/** Recorded register write operations (index -> value). */
|
|
132
|
+
registers: Map<number, U64>;
|
|
133
|
+
setReg(idx: number, val: U64): void;
|
|
134
|
+
memRead(address: U32, data: Uint8Array): void;
|
|
135
|
+
memWrite(address: U32, data: Uint8Array): void;
|
|
136
|
+
clear(): void;
|
|
137
|
+
}
|
|
138
|
+
export {};
|
|
139
|
+
//# sourceMappingURL=ecalli-trace-logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ecalli-trace-logger.d.ts","sourceRoot":"","sources":["../../../../../packages/core/pvm-host-calls/ecalli-trace-logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { BytesBlob } from "#@typeberry/bytes";
|
|
2
|
+
import { Level, Logger } from "#@typeberry/logger";
|
|
3
|
+
const ecalliLogger = Logger.new(import.meta.filename, "ecalli");
|
|
4
|
+
const defaultOutput = (line) => {
|
|
5
|
+
ecalliLogger.trace `${line}`;
|
|
6
|
+
};
|
|
7
|
+
const emptyOutput = () => { };
|
|
8
|
+
/**
|
|
9
|
+
* Ecalli PVM IO Trace Logger.
|
|
10
|
+
*
|
|
11
|
+
* Implements the logging format specified for PVM execution tracing.
|
|
12
|
+
* This format is designed to be:
|
|
13
|
+
* - Human-readable, newline-delimited text
|
|
14
|
+
* - Self-contained for stateless re-execution
|
|
15
|
+
* - Comparable using simple textual diff tools
|
|
16
|
+
*
|
|
17
|
+
* @see https://github.com/tomusdrw/JIPs/pull/2
|
|
18
|
+
*/
|
|
19
|
+
export class EcalliTraceLogger {
|
|
20
|
+
output;
|
|
21
|
+
/** Returns a tracker for IO operations. */
|
|
22
|
+
tracker() {
|
|
23
|
+
return this.output === emptyOutput ? null : new IoTraceTracker();
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Create an IoTraceLogger that outputs to the `ecalli` module logger.
|
|
27
|
+
*
|
|
28
|
+
* Returns `null` if the `ecalli` logger is not configured for at least TRACE level.
|
|
29
|
+
* Enable with: `JAM_LOG=ecalli=trace` or `JAM_LOG=trace`
|
|
30
|
+
*/
|
|
31
|
+
static create() {
|
|
32
|
+
if (ecalliLogger.getLevel() > Level.TRACE) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
return EcalliTraceLogger.new(defaultOutput);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Create a no-op IoTraceLogger that discards all output.
|
|
39
|
+
* Used when tracing is disabled.
|
|
40
|
+
*/
|
|
41
|
+
static noop() {
|
|
42
|
+
return new EcalliTraceLogger(emptyOutput);
|
|
43
|
+
}
|
|
44
|
+
static new(output) {
|
|
45
|
+
return new EcalliTraceLogger(output);
|
|
46
|
+
}
|
|
47
|
+
constructor(output) {
|
|
48
|
+
this.output = output;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Log optional context lines (implementation metadata, execution environment).
|
|
52
|
+
*/
|
|
53
|
+
logContext(context) {
|
|
54
|
+
this.output(context);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Log the program blob being executed and the write data (if any)
|
|
58
|
+
*
|
|
59
|
+
* Format: `program {hex-encoded-program-with-metadata}`
|
|
60
|
+
* Format: `memwrite {hex-encoded-address} len={blob-byte-length} <- {hex-encoded-bytes}`
|
|
61
|
+
*/
|
|
62
|
+
logProgram(program, args) {
|
|
63
|
+
const SPI_ARGS_SEGMENT = 0xfe_ff_00_00;
|
|
64
|
+
this.output(`program ${BytesBlob.blobFrom(program)}`);
|
|
65
|
+
if (args.length > 0) {
|
|
66
|
+
this.output(`memwrite ${toHexAddress(SPI_ARGS_SEGMENT)} len=${args.length} <- ${BytesBlob.blobFrom(args)}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Log initial execution state (prelude).
|
|
71
|
+
*
|
|
72
|
+
* Format: `start pc={pc} gas={gas} {register-dump}`
|
|
73
|
+
*/
|
|
74
|
+
logStart(pc, gas, registers) {
|
|
75
|
+
const line = `start pc=${pc} gas=${gas} ${registers}`;
|
|
76
|
+
this.output(line);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Log ecalli invocation with register dump.
|
|
80
|
+
*
|
|
81
|
+
* Format: `ecalli={index} pc={pc} gas={gas} {register-dump}`
|
|
82
|
+
*/
|
|
83
|
+
logEcalli(index, pc, gas, registers) {
|
|
84
|
+
const line = `ecalli=${index} pc=${pc} gas=${gas} ${registers}`;
|
|
85
|
+
this.output(line);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Log memory read operation.
|
|
89
|
+
*
|
|
90
|
+
* Format: `memread {hex-encoded-address} len={blob-byte-length} -> {hex-encoded-data-read}`
|
|
91
|
+
*/
|
|
92
|
+
logMemRead(address, len, data) {
|
|
93
|
+
this.output(`memread ${toHexAddress(address)} len=${len} -> ${data}`);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Log memory write operation.
|
|
97
|
+
*
|
|
98
|
+
* Format: `memwrite {hex-encoded-address} len={blob-byte-length} <- {hex-encoded-bytes}`
|
|
99
|
+
*/
|
|
100
|
+
logMemWrite(address, len, data) {
|
|
101
|
+
this.output(`memwrite ${toHexAddress(address)} len=${len} <- ${data}`);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Log register write operation.
|
|
105
|
+
*
|
|
106
|
+
* Format: `setreg r{idx} <- {hex-encoded-value}`
|
|
107
|
+
*/
|
|
108
|
+
logSetReg(index, value) {
|
|
109
|
+
const paddedIdx = index.toString().padStart(2, "0");
|
|
110
|
+
this.output(`setreg r${paddedIdx} <- ${value.toString(16)}`);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Log gas overwrite operation.
|
|
114
|
+
*
|
|
115
|
+
* Format: `setgas <- {gas}`
|
|
116
|
+
*/
|
|
117
|
+
logSetGas(gas) {
|
|
118
|
+
this.output(`setgas <- ${gas}`);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Log all host actions from a single ecalli invocation.
|
|
122
|
+
* Actions are logged in the order specified by JIP-6:
|
|
123
|
+
* 1. Memory reads (sorted by address)
|
|
124
|
+
* 2. Memory writes (sorted by address)
|
|
125
|
+
* 3. Register writes (sorted by index)
|
|
126
|
+
* 4. Gas overwrite
|
|
127
|
+
*/
|
|
128
|
+
logHostActions(ioTracker, gasBefore, gasAfter) {
|
|
129
|
+
if (ioTracker === null) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const reads = ioTracker.reads.sort((a, b) => a.address - b.address);
|
|
133
|
+
for (const op of reads) {
|
|
134
|
+
this.logMemRead(op.address, op.len, op.hex);
|
|
135
|
+
}
|
|
136
|
+
const writes = ioTracker.writes.sort((a, b) => a.address - b.address);
|
|
137
|
+
for (const op of writes) {
|
|
138
|
+
this.logMemWrite(op.address, op.len, op.hex);
|
|
139
|
+
}
|
|
140
|
+
const sortedRegWrites = [...ioTracker.registers.entries()].sort((a, b) => a[0] - b[0]);
|
|
141
|
+
for (const op of sortedRegWrites) {
|
|
142
|
+
this.logSetReg(op[0], op[1]);
|
|
143
|
+
}
|
|
144
|
+
if (gasBefore !== gasAfter) {
|
|
145
|
+
this.logSetGas(gasAfter);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Log PANIC termination.
|
|
150
|
+
*
|
|
151
|
+
* Format: `PANIC={argument} pc={pc} gas={gas} {register-dump}`
|
|
152
|
+
*/
|
|
153
|
+
logPanic(argument, pc, gas, registers) {
|
|
154
|
+
const line = `PANIC=${argument} pc=${pc} gas=${gas} ${registers}`;
|
|
155
|
+
this.output(line);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Log OOG (out of gas) termination.
|
|
159
|
+
*
|
|
160
|
+
* Format: `OOG pc={pc} gas={gas} {register-dump}`
|
|
161
|
+
*/
|
|
162
|
+
logOog(pc, gas, registers) {
|
|
163
|
+
const line = `OOG pc=${pc} gas=${gas} ${registers}`;
|
|
164
|
+
this.output(line);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Log HALT termination.
|
|
168
|
+
*
|
|
169
|
+
* Format: `HALT pc={pc} gas={gas} {register-dump}`
|
|
170
|
+
*/
|
|
171
|
+
logHalt(pc, gas, registers) {
|
|
172
|
+
const line = `HALT pc=${pc} gas=${gas} ${registers}`;
|
|
173
|
+
this.output(line);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Convert 32-bit address to 0x-prefixed hex string.
|
|
178
|
+
*/
|
|
179
|
+
function toHexAddress(address) {
|
|
180
|
+
return `0x${address.toString(16).padStart(8, "0")}`;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* IoTracker implementation that records all I/O operations for trace logging.
|
|
184
|
+
*
|
|
185
|
+
* Stores memory reads, writes, and register modifications as hex-encoded strings
|
|
186
|
+
* for output via IoTraceLogger.
|
|
187
|
+
*/
|
|
188
|
+
export class IoTraceTracker {
|
|
189
|
+
/** Recorded memory read operations (address + hex data + len). */
|
|
190
|
+
reads = [];
|
|
191
|
+
/** Recorded memory write operations (address + hex data + len). */
|
|
192
|
+
writes = [];
|
|
193
|
+
/** Recorded register write operations (index -> value). */
|
|
194
|
+
registers = new Map();
|
|
195
|
+
setReg(idx, val) {
|
|
196
|
+
this.registers.set(idx, val);
|
|
197
|
+
}
|
|
198
|
+
memRead(address, data) {
|
|
199
|
+
this.reads.push({ address, hex: BytesBlob.blobFrom(data).toString(), len: data.length });
|
|
200
|
+
}
|
|
201
|
+
memWrite(address, data) {
|
|
202
|
+
this.writes.push({ address, hex: BytesBlob.blobFrom(data).toString(), len: data.length });
|
|
203
|
+
}
|
|
204
|
+
clear() {
|
|
205
|
+
this.reads.length = 0;
|
|
206
|
+
this.writes.length = 0;
|
|
207
|
+
this.registers.clear();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ecalli-trace-logger.test.d.ts","sourceRoot":"","sources":["../../../../../packages/core/pvm-host-calls/ecalli-trace-logger.test.ts"],"names":[],"mappings":""}
|
|
@@ -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
|
+
});
|