Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fuzzer & fixes #14

Merged
merged 16 commits into from
Dec 29, 2024
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules
tests/*
25 changes: 20 additions & 5 deletions assembly/api-generic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { INSTRUCTIONS, MISSING_INSTRUCTION } from "./instructions";
import { Interpreter, Status } from "./interpreter";
import { Memory, MemoryBuilder } from "./memory";
import { Access, PAGE_SIZE } from "./memory-page";
import { Program, decodeArguments, decodeProgram, liftBytes } from "./program";
import { Program, decodeArguments, decodeProgram, liftBytes, resolveArguments } from "./program";
import { NO_OF_REGISTERS, Registers } from "./registers";

export class InitialPage {
Expand Down Expand Up @@ -62,7 +62,7 @@ export function getAssembly(p: Program): string {
}

const args = decodeArguments(iData.kind, p.code.subarray(i + 1, end));
const argsArray = [args.a, args.b, args.c, args.d];
const argsArray = args === null ? [0, 0, 0, 0] : [args.a, args.b, args.c, args.d];
const relevantArgs = RELEVANT_ARGS[iData.kind];
for (let i = 0; i < relevantArgs; i++) {
v += ` ${argsArray[i]}, `;
Expand Down Expand Up @@ -96,9 +96,19 @@ export function runVm(input: VmInput, logs: boolean = false): VmOutput {
if (logs) console.log(`PC = ${int.pc}`);
if (logs) console.log(`STATUS = ${int.status}`);
if (logs) console.log(`REGISTERS = ${registers.join(", ")}`);
if (logs && int.pc < u32(int.program.code.length)) {
const name = changetype<string>(INSTRUCTIONS[int.program.code[int.pc]].namePtr);
console.log(`INSTRUCTION = ${name} (${int.program.code[int.pc]})`);
if (logs) {
const instruction = int.pc < u32(int.program.code.length) ? int.program.code[int.pc] : 0;
const iData = instruction >= <u8>INSTRUCTIONS.length ? MISSING_INSTRUCTION : INSTRUCTIONS[instruction];
const name = changetype<string>(iData.namePtr);
console.log(`INSTRUCTION = ${name} (${instruction})`);
const args = resolveArguments(iData.kind, int.program.code.subarray(int.pc + 1), int.registers);
if (args !== null) {
console.log(`ARGUMENTS:
${args.a} (${args.decoded.a}) = 0x${u64(args.a).toString(16)},
${args.b} (${args.decoded.b}) = 0x${u64(args.b).toString(16)},
${args.c} (${args.decoded.c}) = 0x${u64(args.c).toString(16)},
${args.d} (${args.decoded.d}) = 0x${u64(args.d).toString(16)}`);
}
}

isOk = int.nextStep();
Expand All @@ -120,6 +130,11 @@ export function getOutputChunks(memory: Memory): InitialChunk[] {
const pageIdx = pages[i];
const page = memory.pages.get(pageIdx);

// skip empty pages
if (page.raw.page === null) {
continue;
}

for (let n = 0; n < page.raw.data.length; n++) {
const v = page.raw.data[n];
if (v !== 0) {
Expand Down
29 changes: 18 additions & 11 deletions assembly/arguments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export enum Arguments {
/** How many numbers in `Args` is relevant for given `Arguments`. */
export const RELEVANT_ARGS = [<i32>0, 1, 2, 1, 2, 3, 3, 3, 2, 3, 3, 4, 3];
/** How many bytes is required by given `Arguments`. */
export const REQUIRED_BYTES = [<i32>0, 0, 0, 0, 1, 9, 1, 1, 1, 1, 1, 1, 2];
export const REQUIRED_BYTES = [<i32>0, 0, 1, 0, 1, 9, 1, 1, 1, 1, 1, 2, 2];

// @unmanaged
export class Args {
Expand All @@ -39,11 +39,8 @@ function asArgs(a: u32, b: u32, c: u32, d: u32): Args {
type ArgsDecoder = (data: Uint8Array) => Args;

function twoImm(data: Uint8Array): Args {
if (data.length === 0) {
return asArgs(0, 0, 0, 0);
}
const n = nibbles(data[0]);
const split = n.low + 1;
const split = <i32>Math.min(4, n.low) + 1;
const first = decodeI32(data.subarray(1, split));
const second = decodeI32(data.subarray(split));
return asArgs(first, second, 0, 0);
Expand Down Expand Up @@ -77,16 +74,16 @@ export const DECODERS: ArgsDecoder[] = [
},
//DECODERS[Arguments.OneRegTwoImm] =
(data: Uint8Array) => {
const first = nibbles(data[0]);
const split = first.hig + 1;
const n = nibbles(data[0]);
const split = <i32>Math.min(4, n.hig) + 1;
const immA = decodeI32(data.subarray(1, split));
const immB = decodeI32(data.subarray(split));
return asArgs(first.low, immA, immB, 0);
return asArgs(n.low, immA, immB, 0);
},
// DECODERS[Arguments.OneRegOneImmOneOff] =
(data: Uint8Array) => {
const n = nibbles(data[0]);
const split = n.hig + 1;
const split = <i32>Math.min(4, n.hig) + 1;
const immA = decodeI32(data.subarray(1, split));
const offs = decodeI32(data.subarray(split));
return asArgs(n.low, immA, offs, 0);
Expand Down Expand Up @@ -127,7 +124,7 @@ class Nibbles {
}

// @inline
function nibbles(byte: u8): Nibbles {
export function nibbles(byte: u8): Nibbles {
const low = byte & 0xf;
const hig = byte >> 4;
const n = new Nibbles();
Expand All @@ -138,7 +135,7 @@ function nibbles(byte: u8): Nibbles {

//@inline
function decodeI32(data: Uint8Array): u32 {
const len = <u32>data.length;
const len = <u32>Math.min(4, data.length);
let num = 0;
for (let i: u32 = 0; i < len; i++) {
num |= u32(data[i]) << (i * 8);
Expand All @@ -152,6 +149,16 @@ function decodeI32(data: Uint8Array): u32 {
return num;
}

export function encodeI32(input: i32): u8[] {
const data: u8[] = [];
let num = u32(input);
while (num > 0) {
data.push(u8(num));
num >>= 8;
}
return data;
}

function decodeU32(data: Uint8Array): u32 {
let num = u32(data[0]);
num |= u32(data[1]) << 8;
Expand Down
46 changes: 44 additions & 2 deletions assembly/codec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class Decoder {

varU32(): u32 {
this.ensureBytes(1);
const v = readVarU32(this.source.subarray(this.offset));
const v = decodeVarU32(this.source.subarray(this.offset));
this.offset += v.offset;
return v.value;
}
Expand Down Expand Up @@ -90,7 +90,7 @@ export class ValOffset<T> {
}

/** Read variable-length u32 and return number of bytes read. */
export function readVarU32(data: Uint8Array): ValOffset<u32> {
export function decodeVarU32(data: Uint8Array): ValOffset<u32> {
const length = i32(variableLength(data[0]));
const first = u32(data[0]);
if (length === 0) {
Expand All @@ -110,3 +110,45 @@ export function readVarU32(data: Uint8Array): ValOffset<u32> {

return new ValOffset(number, 1 + length);
}

export function encodeVarU32(v: u64): Uint8Array {
if (v === 0) {
return new Uint8Array(1);
}

// handle the biggest case
let maxEncoded = u64(2 ** (7 * 8));
if (v >= maxEncoded) {
const dest = new Uint8Array(9);
dest[0] = 0xff;
const dataView = new DataView(dest.buffer);
dataView.setUint64(1, v, true);
return dest;
}

// let's look for the correct range
let minEncoded = maxEncoded >> 7;
for (let l = 7; l >= 0; l -= 1) {
if (v >= minEncoded) {
const dest = new Uint8Array(l + 1);

// encode the first byte
const maxVal = 2 ** (8 * l);
const byte = 2 ** 8 - 2 ** (8 - l) + v / maxVal;
dest[0] = u8(byte);

// now encode the rest of bytes of len `l`
let rest = v % maxVal;
for (let i = 1; i < 1 + l; i += 1) {
dest[i] = u8(rest);
rest >>= 8;
}
return dest;
}
// move one power down
maxEncoded = minEncoded;
minEncoded >>= 7;
}

throw new Error(`Unhandled number encoding: ${v}`);
}
6 changes: 3 additions & 3 deletions assembly/gas.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/** Gas type. */
export type Gas = u64;
export type Gas = i64;

/** Create a new gas counter instance depending on the gas value. */
export function gasCounter(gas: u64): GasCounter {
export function gasCounter(gas: i64): GasCounter {
return new GasCounterU64(gas);
}

Expand All @@ -25,7 +25,7 @@ class GasCounterU64 implements GasCounter {
constructor(private gas: Gas) {}

set(g: Gas): void {
this.gas = <u64>g;
this.gas = <i64>g;
}

get(): Gas {
Expand Down
9 changes: 5 additions & 4 deletions assembly/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { VmInput, getAssembly, runVm } from "./api-generic";
import { VmInput, VmOutput, getAssembly, runVm } from "./api-generic";
import { decodeProgram, decodeSpi, liftBytes } from "./program";

export * from "./api";
export { runVm, getAssembly } from "./api-generic";
export { wrapAsProgram } from "./program-build";

export enum InputKind {
Generic = 0,
Expand All @@ -24,16 +25,16 @@ export function disassemble(input: u8[], kind: InputKind): string {
return `Unknown kind: ${kind}`;
}

export function runProgram(input: u8[], kind: InputKind): void {
export function runProgram(input: u8[], registers: u64[], kind: InputKind): VmOutput {
if (kind === InputKind.Generic) {
const vmInput = new VmInput();
vmInput.registers[7] = 9;
vmInput.registers = registers;
vmInput.gas = 10_000;
vmInput.program = input;

const output = runVm(vmInput, true);
console.log(`Finished with status: ${output.status}`);
return;
return output;
}

if (kind === InputKind.SPI) {
Expand Down
Loading
Loading