@fluffylabs/anan-as 1.1.3-c185e54 → 1.1.3-c81d96c

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/dist/bin/index.js CHANGED
@@ -1,13 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import { readFileSync } from "node:fs";
3
3
  import minimist from "minimist";
4
- import { disassemble, HasMetadata, InputKind, prepareProgram, runProgram } from "../build/release.js";
4
+ import { disassemble, HasMetadata, InputKind, prepareProgram, pvmDestroy, pvmResume, pvmSetRegisters, pvmStart, } from "../build/release.js";
5
+ import { LOG_GAS_COST, LOG_HOST_CALL_INDEX, printLogHostCall, WHAT } from "./src/log-host-call.js";
6
+ import { STATUS } from "./src/trace-parse.js";
5
7
  import { replayTraceFile } from "./src/trace-replay.js";
6
8
  import { hexDecode, hexEncode } from "./src/utils.js";
7
9
  const HELP_TEXT = `Usage:
8
10
  anan-as disassemble [--spi] [--no-metadata] <file.(jam|pvm|spi|bin)>
9
- anan-as run [--spi] [--no-logs] [--no-metadata] [--pc <number>] [--gas <number>] <file.jam> [spi-args.bin or hex]
10
- anan-as replay-trace [--no-metadata] [--no-verify] [--no-logs] <trace.log>
11
+ anan-as run [--spi] [--no-logs] [--no-metadata] [--no-log-host-call] [--pc <number>] [--gas <number>] <file.jam> [spi-args.bin or hex]
12
+ anan-as replay-trace [--no-metadata] [--no-verify] [--no-logs] [--no-log-host-call] <trace.log>
11
13
 
12
14
  Commands:
13
15
  disassemble Disassemble PVM bytecode to assembly
@@ -15,13 +17,14 @@ Commands:
15
17
  replay-trace Re-execute a ecalli IO trace
16
18
 
17
19
  Flags:
18
- --spi Treat input as JAM SPI format
19
- --no-metadata Input does not contain metadata
20
- --no-logs Disable execution logs
21
- --no-verify Skip verification against trace data (replay-trace only)
22
- --pc <number> Set initial program counter (default: 0)
23
- --gas <number> Set initial gas amount (default: 10_000)
24
- --help, -h Show this help message`;
20
+ --spi Treat input as JAM SPI format
21
+ --no-metadata Input does not contain metadata
22
+ --no-logs Disable execution logs
23
+ --no-log-host-call Disable built-in handling of JIP-1 log host call (ecalli 100)
24
+ --no-verify Skip verification against trace data (replay-trace only)
25
+ --pc <number> Set initial program counter (default: 0)
26
+ --gas <number> Set initial gas amount (default: 10_000)
27
+ --help, -h Show this help message`;
25
28
  main();
26
29
  function main() {
27
30
  const args = process.argv.slice(2);
@@ -93,11 +96,11 @@ function handleDisassemble(args) {
93
96
  }
94
97
  function handleRun(args) {
95
98
  const parsed = minimist(args, {
96
- boolean: ["spi", "logs", "metadata", "help"],
99
+ boolean: ["spi", "logs", "metadata", "help", "log-host-call"],
97
100
  /** Prevents parsing hex values as numbers. */
98
101
  string: ["pc", "gas", "_"],
99
102
  alias: { h: "help" },
100
- default: { metadata: true, logs: true },
103
+ default: { metadata: true, logs: true, "log-host-call": true },
101
104
  });
102
105
  if (parsed.help) {
103
106
  console.log(HELP_TEXT);
@@ -134,6 +137,7 @@ function handleRun(args) {
134
137
  // Validate SPI args file if provided
135
138
  const spiArgs = parseSpiArgs(spiArgsStr);
136
139
  const logs = parsed.logs;
140
+ const logHostCall = parsed["log-host-call"];
137
141
  const hasMetadata = parsed.metadata ? HasMetadata.Yes : HasMetadata.No;
138
142
  // Parse and validate PC and gas options
139
143
  const initialPc = parsePc(parsed);
@@ -143,13 +147,36 @@ function handleRun(args) {
143
147
  console.log(`🚀 Running ${programFile} (as ${name})`);
144
148
  try {
145
149
  const program = prepareProgram(kind, hasMetadata, programCode, [], [], [], spiArgs);
146
- const result = runProgram(program, initialGas, initialPc, logs, false);
147
- console.log(`Status: ${result.status}`);
148
- console.log(`Exit code: ${result.exitCode}`);
149
- console.log(`Program counter: ${result.pc}`);
150
- console.log(`Gas remaining: ${result.gas}`);
151
- console.log(`Registers: [${result.registers.join(", ")}]`);
152
- console.log(`Result: [${hexEncode(result.result)}]`);
150
+ const id = pvmStart(program, false);
151
+ let gas = initialGas;
152
+ let pc = initialPc;
153
+ for (;;) {
154
+ const pause = pvmResume(id, gas, pc, logs);
155
+ if (!pause) {
156
+ throw new Error("pvmResume returned null");
157
+ }
158
+ if (pause.status === STATUS.HOST && pause.exitCode === LOG_HOST_CALL_INDEX && logHostCall) {
159
+ printLogHostCall(id, pause.registers);
160
+ // Set r7 = WHAT
161
+ const regs = pause.registers;
162
+ regs[7] = WHAT;
163
+ pvmSetRegisters(id, regs);
164
+ // Deduct gas and advance PC
165
+ gas = pause.gas >= LOG_GAS_COST ? pause.gas - LOG_GAS_COST : 0n;
166
+ pc = pause.nextPc;
167
+ }
168
+ else {
169
+ console.warn(`Unhandled host call: ecalli ${pause.exitCode}. Finishing.`);
170
+ break;
171
+ }
172
+ }
173
+ const result = pvmDestroy(id);
174
+ console.log(`Status: ${result?.status}`);
175
+ console.log(`Exit code: ${result?.exitCode}`);
176
+ console.log(`Program counter: ${result?.pc}`);
177
+ console.log(`Gas remaining: ${result?.gas}`);
178
+ console.log(`Registers: [${result?.registers.join(", ")}]`);
179
+ console.log(`Result: [${hexEncode(result?.result ?? [])}]`);
153
180
  }
154
181
  catch (error) {
155
182
  console.error(`Error running ${programFile}:`, error);
@@ -158,9 +185,9 @@ function handleRun(args) {
158
185
  }
159
186
  function handleReplayTrace(args) {
160
187
  const parsed = minimist(args, {
161
- boolean: ["metadata", "verify", "logs", "help"],
188
+ boolean: ["metadata", "verify", "logs", "help", "log-host-call"],
162
189
  alias: { h: "help" },
163
- default: { metadata: true, logs: true, verify: true },
190
+ default: { metadata: true, logs: true, verify: true, "log-host-call": true },
164
191
  });
165
192
  if (parsed.help) {
166
193
  console.log(HELP_TEXT);
@@ -181,11 +208,13 @@ function handleReplayTrace(args) {
181
208
  const hasMetadata = parsed.metadata ? HasMetadata.Yes : HasMetadata.No;
182
209
  const verify = parsed.verify;
183
210
  const logs = parsed.logs;
211
+ const logHostCall = parsed["log-host-call"];
184
212
  try {
185
213
  const summary = replayTraceFile(file, {
186
214
  logs,
187
215
  hasMetadata,
188
216
  verify,
217
+ logHostCall,
189
218
  });
190
219
  console.log(`✅ Replay complete: ${summary.ecalliCount} ecalli entries`);
191
220
  console.log(`Status: ${summary.termination.type}`);
@@ -58,7 +58,7 @@ export function fuzz(data) {
58
58
  }
59
59
  import { hexEncode } from "./utils.js";
60
60
  function programHex(program) {
61
- return hexEncode(Array.from(program), false);
61
+ return hexEncode(program, false);
62
62
  }
63
63
  function linkTo(programHex) {
64
64
  return `https://pvm.fluffylabs.dev/?program=0x${programHex}#/`;
@@ -0,0 +1,41 @@
1
+ import { pvmReadMemory } from "../../build/release.js";
2
+ export const LOG_HOST_CALL_INDEX = 100;
3
+ export const LOG_GAS_COST = 10n;
4
+ /** The WHAT return value - indicates the host call is not implemented / acknowledged. */
5
+ export const WHAT = 0xfffffffffffffffen;
6
+ const LOG_LEVELS = ["FATAL", "ERROR", "WARN", "INFO", "DEBUG"];
7
+ const MAX_LOG_LEN = 8192;
8
+ /**
9
+ * Print the log message from a JIP-1 log host call (ecalli 100).
10
+ *
11
+ * Reads the level, target, and message from the PVM registers and memory,
12
+ * then prints via console.info.
13
+ */
14
+ export function printLogHostCall(pvmId, registers) {
15
+ const level = Number(registers[7]);
16
+ const targetPtr = Number(registers[8] & 0xffffffffn);
17
+ const targetLen = Math.min(Math.max(0, Number(registers[9] & 0xffffffffn)), MAX_LOG_LEN);
18
+ const messagePtr = Number(registers[10] & 0xffffffffn);
19
+ const messageLen = Math.min(Math.max(0, Number(registers[11] & 0xffffffffn)), MAX_LOG_LEN);
20
+ const levelStr = LOG_LEVELS[level] ?? `LEVEL(${level})`;
21
+ let target = "";
22
+ if (targetPtr !== 0 && targetLen > 0) {
23
+ const targetBytes = pvmReadMemory(pvmId, targetPtr, targetLen);
24
+ if (targetBytes) {
25
+ target = new TextDecoder().decode(targetBytes);
26
+ }
27
+ }
28
+ let message = "";
29
+ if (messagePtr !== 0 && messageLen > 0) {
30
+ const messageBytes = pvmReadMemory(pvmId, messagePtr, messageLen);
31
+ if (messageBytes) {
32
+ message = new TextDecoder().decode(messageBytes);
33
+ }
34
+ }
35
+ if (target) {
36
+ console.info(`[${levelStr}] ${target}: ${message}`);
37
+ }
38
+ else {
39
+ console.info(`[${levelStr}] ${message}`);
40
+ }
41
+ }
@@ -1,5 +1,6 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { InputKind, prepareProgram, pvmDestroy, pvmReadMemory, pvmResume, pvmSetRegisters, pvmStart, pvmWriteMemory, } from "../../build/release.js";
3
+ import { LOG_HOST_CALL_INDEX, printLogHostCall } from "./log-host-call.js";
3
4
  import { ARGS_SEGMENT_START, buildInitialChunks, buildInitialPages, encodeRegistersFromDump, extractSpiArgs, isSpiTrace, parseTrace, STATUS, statusToTermination, } from "./trace-parse.js";
4
5
  import { ConsoleTracer } from "./tracer.js";
5
6
  import { hexEncode } from "./utils.js";
@@ -37,6 +38,10 @@ export function replayTraceFile(filePath, options) {
37
38
  }
38
39
  // Print ecalli line
39
40
  tracer.ecalli(expectedEcalli.index, pause.pc, pause.gas, pause.registers);
41
+ // Print log message for JIP-1 log host call
42
+ if (pause.exitCode === LOG_HOST_CALL_INDEX && options.logHostCall) {
43
+ printLogHostCall(id, pause.registers);
44
+ }
40
45
  if (options.verify) {
41
46
  assertEq(pause.exitCode, expectedEcalli.index, "ecalli index");
42
47
  assertEq(pause.pc, expectedEcalli.pc, "ecalli pc");
@@ -119,7 +124,7 @@ function assertRegisters(actual, expected) {
119
124
  }
120
125
  }
121
126
  function assertMemEq(actual, expected, label) {
122
- const actualString = hexEncode(Array.from(actual));
123
- const expectedString = hexEncode(Array.from(expected));
127
+ const actualString = hexEncode(actual);
128
+ const expectedString = hexEncode(expected);
124
129
  assertEq(actualString, expectedString, label);
125
130
  }
@@ -1,5 +1,5 @@
1
1
  export function hexEncode(result, includePrefix = true) {
2
- const hex = result.map((x) => x.toString(16).padStart(2, "0")).join("");
2
+ const hex = Array.from(result, (x) => x.toString(16).padStart(2, "0")).join("");
3
3
  return includePrefix ? `0x${hex}` : hex;
4
4
  }
5
5
  export function hexDecode(data) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fluffylabs/anan-as",
3
3
  "description": "AssemblyScript PVM interpreter.",
4
- "version": "1.1.3-c185e54",
4
+ "version": "1.1.3-c81d96c",
5
5
  "main": "./dist/bin/index.js",
6
6
  "bin": {
7
7
  "anan-as": "./dist/bin/index.js"