Skip to content

Commit

Permalink
Take the latest PVM and adjust code accordingly
Browse files Browse the repository at this point in the history
  • Loading branch information
wkwiatek committed Jul 23, 2024
1 parent cbc4579 commit 859ca92
Show file tree
Hide file tree
Showing 51 changed files with 1,542 additions and 394 deletions.
4 changes: 2 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import "./App.css";
import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea.tsx";
import { InitialState, Pvm } from "@/pvm/pvm.ts";
import { InitialState, Pvm } from "@/pvm-packages/pvm/pvm.ts";
import { useState } from "react";

function App() {
Expand All @@ -22,7 +22,7 @@ function App() {
// const program = [0, 0, 3, 8, 135, 9, 249]

const handleClick = () => {
const pvm = new Pvm(program, initialState);
const pvm = new Pvm(new Uint8Array(program), initialState);
// console.log(pvm.printProgram())
pvm.runProgram();
// console.log(pvm.getState())
Expand Down
42 changes: 42 additions & 0 deletions src/pvm-packages/bytes.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import assert from "node:assert";
import { test } from "node:test";
import { Bytes, BytesBlob } from "./bytes";

test("BytesBlob", async (t) => {
await t.test("should fail if 0x is missing", () => {
try {
BytesBlob.parseBlob("ff2f");
assert.fail("Should throw an exception");
} catch (e) {
assert.strictEqual(`${e}`, "Error: Invalid hex string: ff2f.");
}
});

await t.test("parse 0x-prefixed hex string into blob of bytes", () => {
const input = "0x2fa3f686df876995167e7c2e5d74c4c7b6e48f8068fe0e44208344d480f7904c";
const result = BytesBlob.parseBlob(input);

assert.deepStrictEqual(new Uint8Array(result.buffer), new Uint8Array([47, 163, 246, 134, 223, 135, 105, 149, 22, 126, 124, 46, 93, 116, 196, 199, 182, 228, 143, 128, 104, 254, 14, 68, 32, 131, 68, 212, 128, 247, 144, 76]));
});
});

test("Bytes", async (t) => {
await t.test("should fail in case of length mismatch", () => {
const input = "0x9c2d3bce7aa0a5857c67a85247365d2035f7d9daec2b515e86086584ad5e8644";

try {
Bytes.parseBytes(input, 16);
assert.fail("Should throw an exception");
} catch (e) {
assert.strictEqual(`${e}`, "Error: Input string too long. Expected 16, got 32");
}
});

await t.test("parse 0x-prefixed, fixed length bytes vector", () => {
const input = "0x9c2d3bce7aa0a5857c67a85247365d2035f7d9daec2b515e86086584ad5e8644";

const bytes = Bytes.parseBytes(input, 32);

assert.deepStrictEqual(new Uint8Array(bytes.view.buffer), new Uint8Array([156, 45, 59, 206, 122, 160, 165, 133, 124, 103, 168, 82, 71, 54, 93, 32, 53, 247, 217, 218, 236, 43, 81, 94, 134, 8, 101, 132, 173, 94, 134, 68]));
});
});
67 changes: 67 additions & 0 deletions src/pvm-packages/bytes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
function assert(value: boolean, description: string): asserts value is true {
if (!value) throw new Error(description);
}

function bufferToHexString(buffer: ArrayBuffer): string {
// TODO [ToDr] consider using TextDecoder API?
let s = "0x";
const asUint = new Uint8Array(buffer);
for (const v of asUint) {
s += v.toString(16).padStart(2, "0");
}
return s;
}

export class BytesBlob {
readonly buffer: ArrayBuffer = new ArrayBuffer(0);
readonly length: number = 0;

constructor(buffer: ArrayBuffer) {
this.buffer = buffer;
this.length = buffer.byteLength;
}

toString() {
return bufferToHexString(this.buffer);
}

static parseBlob(v: string): BytesBlob {
const len = v.length;
if (len % 2 === 1 || !v.startsWith("0x")) {
throw new Error(`Invalid hex string: ${v}.`);
}
// NOTE [ToDr] alloc
const buffer = new ArrayBuffer(len / 2 - 1);
const bytes = new Uint8Array(buffer);
for (let i = 2; i < len - 1; i += 2) {
const c = v.substring(i, i + 2);
bytes[i / 2 - 1] = Number.parseInt(c, 16);
}

return new BytesBlob(buffer);
}
}

export class Bytes<T extends number> {
readonly view: DataView = new DataView(new ArrayBuffer(0));
readonly length: T;

constructor(view: DataView, len: T) {
assert(view.byteLength === len, `Given buffer has incorrect size ${view.byteLength} vs expected ${len}`);
this.view = view;
this.length = len;
}

toString() {
return bufferToHexString(this.view.buffer);
}

static parseBytes<X extends number>(v: string, len: X): Bytes<X> {
if (v.length > 2 * len + 2) {
throw new Error(`Input string too long. Expected ${len}, got ${v.length / 2 - 1}`);
}

const blob = BytesBlob.parseBlob(v);
return new Bytes(new DataView(blob.buffer), len);
}
}
6 changes: 6 additions & 0 deletions src/pvm-packages/crypto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { Bytes } from "./bytes";
import type { Opaque } from "./opaque";

export type Ed25519Key = Opaque<Bytes<32>, "ed25519">;
export type BandersnatchKey = Opaque<Bytes<32>, "BandersnatchKey">;
export type BlsKey = Opaque<Bytes<144>, "bls">;
5 changes: 5 additions & 0 deletions src/pvm-packages/hash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { Bytes } from "./bytes";
import type { Opaque } from "./opaque";

export type Hash = Bytes<32>;
export type EntropyHash = Opaque<Hash, "entropy">;
226 changes: 226 additions & 0 deletions src/pvm-packages/jam-codec/decode-natural-number.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
import assert from "node:assert";
import { test } from "node:test";

import { decodeNaturalNumber } from "./decode-natural-number";

test("decodeNaturalNumber", async (t) => {
await t.test("decode 0", () => {
const encodedBytes = new Uint8Array([0]);
const expectedValue = 0n;

const result = decodeNaturalNumber(encodedBytes);

assert.strictEqual(result.value, expectedValue);
assert.strictEqual(result.bytesToSkip, encodedBytes.length);
});

await t.test("decode single byte min value", () => {
const encodedBytes = new Uint8Array([1]);
const expectedValue = 1n;

const result = decodeNaturalNumber(encodedBytes);

assert.strictEqual(result.value, expectedValue);
assert.strictEqual(result.bytesToSkip, encodedBytes.length);
});

await t.test("decode single byte max value", () => {
const encodedBytes = new Uint8Array([127]);
const expectedValue = 127n;

const result = decodeNaturalNumber(encodedBytes);

assert.strictEqual(result.value, expectedValue);
assert.strictEqual(result.bytesToSkip, encodedBytes.length);
});

await t.test("decode 2 bytes min value", () => {
const encodedBytes = new Uint8Array([128, 128]);
const expectedValue = 128n;

const result = decodeNaturalNumber(encodedBytes);

assert.strictEqual(result.value, expectedValue);
assert.strictEqual(result.bytesToSkip, encodedBytes.length);
});

await t.test("decode 2 bytes max value", () => {
const encodedBytes = new Uint8Array([191, 255]);
const expectedValue = 2n ** 14n - 1n;

const result = decodeNaturalNumber(encodedBytes);

assert.strictEqual(result.value, expectedValue);
assert.strictEqual(result.bytesToSkip, encodedBytes.length);
});

await t.test("decode 3 bytes min value", () => {
const encodedBytes = new Uint8Array([192, 0, 0x40]);
const expectedValue = 2n ** 14n;

const result = decodeNaturalNumber(encodedBytes);

assert.strictEqual(result.value, expectedValue);
assert.strictEqual(result.bytesToSkip, encodedBytes.length);
});

await t.test("decode 3 bytes max value", () => {
const encodedBytes = new Uint8Array([192 + 31, 0xff, 0xff]);
const expectedValue = 2n ** 21n - 1n;

const result = decodeNaturalNumber(encodedBytes);

assert.strictEqual(result.value, expectedValue);
assert.strictEqual(result.bytesToSkip, encodedBytes.length);
});

await t.test("decode 4 bytes min value", () => {
const encodedBytes = new Uint8Array([0xe0, 0, 0, 0x20]);
const expectedValue = 2n ** 21n;

const result = decodeNaturalNumber(encodedBytes);

assert.strictEqual(result.value, expectedValue);
assert.strictEqual(result.bytesToSkip, encodedBytes.length);
});

await t.test("decode 4 bytes max value", () => {
const encodedBytes = new Uint8Array([0xe0 + 15, 0xff, 0xff, 0xff]);
const expectedValue = 2n ** 28n - 1n;

const result = decodeNaturalNumber(encodedBytes);

assert.strictEqual(result.value, expectedValue);
assert.strictEqual(result.bytesToSkip, encodedBytes.length);
});

await t.test("decode 5 bytes min value", () => {
const encodedBytes = new Uint8Array([256 - 16, 0, 0, 0, 0x10]);
const expectedValue = 2n ** 28n;

const result = decodeNaturalNumber(encodedBytes);

assert.strictEqual(result.value, expectedValue);
assert.strictEqual(result.bytesToSkip, encodedBytes.length);
});

await t.test("decode 5 bytes max value", () => {
const encodedBytes = new Uint8Array([256 - 16 + 7, 0xff, 0xff, 0xff, 0xff]);
const expectedValue = 2n ** 35n - 1n;

const result = decodeNaturalNumber(encodedBytes);

assert.strictEqual(result.value, expectedValue);
assert.strictEqual(result.bytesToSkip, encodedBytes.length);
});

await t.test("decode 6 bytes min value", () => {
const encodedBytes = new Uint8Array([256 - 8, 0, 0, 0, 0, 0x08]);
const expectedValue = 2n ** 35n;

const result = decodeNaturalNumber(encodedBytes);

assert.strictEqual(result.value, expectedValue);
assert.strictEqual(result.bytesToSkip, encodedBytes.length);
});

await t.test("decode 6 bytes max value", () => {
const encodedBytes = new Uint8Array([256 - 8 + 3, 0xff, 0xff, 0xff, 0xff, 0xff]);
const expectedValue = 2n ** 42n - 1n;

const result = decodeNaturalNumber(encodedBytes);

assert.strictEqual(result.value, expectedValue);
assert.strictEqual(result.bytesToSkip, encodedBytes.length);
});

await t.test("decode 7 bytes min value", () => {
const encodedBytes = new Uint8Array([256 - 4, 0, 0, 0, 0, 0, 0x04]);
const expectedValue = 2n ** 42n;

const result = decodeNaturalNumber(encodedBytes);

assert.strictEqual(result.value, expectedValue);
assert.strictEqual(result.bytesToSkip, encodedBytes.length);
});

await t.test("decode 7 bytes max value", () => {
const encodedBytes = new Uint8Array([256 - 4 + 1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]);
const expectedValue = 2n ** 49n - 1n;

const result = decodeNaturalNumber(encodedBytes);

assert.strictEqual(result.value, expectedValue);
assert.strictEqual(result.bytesToSkip, encodedBytes.length);
});

await t.test("decode 8 bytes min value", () => {
const encodedBytes = new Uint8Array([256 - 2, 0, 0, 0, 0, 0, 0, 0x02]);
const expectedValue = 2n ** 49n;

const result = decodeNaturalNumber(encodedBytes);

assert.strictEqual(result.value, expectedValue);
assert.strictEqual(result.bytesToSkip, encodedBytes.length);
});

await t.test("decode 8 bytes max value", () => {
const encodedBytes = new Uint8Array([256 - 2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]);
const expectedValue = 2n ** 56n - 1n;

const result = decodeNaturalNumber(encodedBytes);

assert.strictEqual(result.value, expectedValue);
assert.strictEqual(result.bytesToSkip, encodedBytes.length);
});

await t.test("decode 9 bytes min value", () => {
const encodedBytes = new Uint8Array([255, 0, 0, 0, 0, 0, 0, 0, 0x01]);
const expectedValue = 2n ** 56n;

const result = decodeNaturalNumber(encodedBytes);

assert.strictEqual(result.value, expectedValue);
assert.strictEqual(result.bytesToSkip, encodedBytes.length);
});

await t.test("decode 9 bytes max value", () => {
const encodedBytes = new Uint8Array([255, 255, 255, 255, 255, 255, 255, 255, 255]);
const expectedValue = 2n ** 64n - 1n;

const result = decodeNaturalNumber(encodedBytes);

assert.strictEqual(result.value, expectedValue);
assert.strictEqual(result.bytesToSkip, encodedBytes.length);
});

await t.test("decode 0 with extra bytes", () => {
const encodedBytes = new Uint8Array([0, 1, 2, 3]);
const expectedValue = 0n;

const result = decodeNaturalNumber(encodedBytes);

assert.strictEqual(result.value, expectedValue);
assert.strictEqual(result.bytesToSkip, 1);
});

await t.test("decode 7 bytes number with extra bytes ", () => {
const encodedBytes = new Uint8Array([256 - 4 + 1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1, 0x2]);
const expectedValue = 2n ** 49n - 1n;

const result = decodeNaturalNumber(encodedBytes);

assert.strictEqual(result.value, expectedValue);
assert.strictEqual(result.bytesToSkip, 7);
});

await t.test("decode 9 bytes number with extra bytes", () => {
const encodedBytes = new Uint8Array([255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 2, 3]);
const expectedValue = 2n ** 64n - 1n;

const result = decodeNaturalNumber(encodedBytes);

assert.strictEqual(result.value, expectedValue);
assert.strictEqual(result.bytesToSkip, 9);
});
});
Loading

0 comments on commit 859ca92

Please sign in to comment.