Skip to content

Commit

Permalink
More tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Rigidity committed Sep 25, 2024
1 parent 89e4510 commit 10e1cbe
Show file tree
Hide file tree
Showing 8 changed files with 223 additions and 27 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions napi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ chia-wallet-sdk = { workspace = true }
chia = { workspace = true }
clvmr = { workspace = true }
num-bigint = { workspace = true }
hex = { workspace = true }

[build-dependencies]
napi-build = "2.0.1"
165 changes: 154 additions & 11 deletions napi/__test__/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,169 @@
import test from "ava";

import { toCoinId } from "../index.js";
import {
ClvmAllocator,
ClvmPtr,
compareBytes,
fromHex,
toCoinId,
toHex,
} from "../index.js";

test("calculate coin id", (t) => {
const coinId = toCoinId({
parentCoinInfo: Buffer.from(
"4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a",
"hex"
parentCoinInfo: fromHex(
"4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"
),
puzzleHash: Buffer.from(
"dbc1b4c900ffe48d575b5da5c638040125f65db0fe3e24494b76ea986457d986",
"hex"
puzzleHash: fromHex(
"dbc1b4c900ffe48d575b5da5c638040125f65db0fe3e24494b76ea986457d986"
),
amount: 100n,
});

t.true(
Buffer.from(coinId).equals(
Buffer.from(
"fd3e669c27be9d634fe79f1f7d7d8aaacc3597b855cffea1d708f4642f1d542a",
"hex"
compareBytes(
coinId,
fromHex(
"fd3e669c27be9d634fe79f1f7d7d8aaacc3597b855cffea1d708f4642f1d542a"
)
)
);
});

test("byte equality", (t) => {
const a = Uint8Array.from([1, 2, 3]);
const b = Uint8Array.from([1, 2, 3]);

t.true(compareBytes(a, b));
t.true(Buffer.from(a).equals(b));
});

test("byte inequality", (t) => {
const a = Uint8Array.from([1, 2, 3]);
const b = Uint8Array.from([1, 2, 4]);

t.true(!compareBytes(a, b));
t.true(!Buffer.from(a).equals(b));
});

test("atom roundtrip", (t) => {
const clvm = new ClvmAllocator();

const expected = Uint8Array.from([1, 2, 3]);
const atom = clvm.newAtom(expected);

t.true(compareBytes(clvm.atom(atom), expected));
});

test("small number roundtrip", (t) => {
const clvm = new ClvmAllocator();

for (const expected of [0, 1, 420, 67108863]) {
const num = clvm.newSmallNumber(expected);
t.is(clvm.smallNumber(num), expected);
}
});

test("small number overflow", (t) => {
const clvm = new ClvmAllocator();

for (const expected of [67108864, 2 ** 32 - 1, Number.MAX_SAFE_INTEGER]) {
t.throws(() => clvm.newSmallNumber(expected));
}
});

test("bigint roundtrip", (t) => {
const clvm = new ClvmAllocator();

for (const expected of [
0n,
1n,
420n,
67108863n,
-1n,
-100n,
-421489719874198729487129847n,
4384723984791283749823764732649187498237483927482n,
]) {
const num = clvm.newBigInt(expected);
t.is(clvm.bigInt(num), expected);
}
});

test("pair roundtrip", (t) => {
const clvm = new ClvmAllocator();
const a = clvm.newSmallNumber(1);
const b = clvm.newBigInt(100n);

const ptr = clvm.newPair(a, b);
const { first, rest } = clvm.pair(ptr);

t.is(clvm.smallNumber(first), 1);
t.is(clvm.bigInt(rest), 100n);
});

test("list roundtrip", (t) => {
const clvm = new ClvmAllocator();

const items = Array.from({ length: 10 }, (_, i) => i);
const ptr = clvm.newList(items.map((i) => clvm.newSmallNumber(i)));
const list = clvm.list(ptr).map((ptr) => clvm.smallNumber(ptr));

t.deepEqual(list, items);
});

test("curry add function", (t) => {
const clvm = new ClvmAllocator();

const addMod = clvm.deserialize(fromHex("ff10ff02ff0580"));
const addToTen = clvm.curry(addMod, [clvm.newSmallNumber(10)]);
const result = clvm.run(
addToTen,
clvm.newList([clvm.newSmallNumber(5)]),
10000000n,
true
);

t.is(clvm.smallNumber(result.value), 15);
t.is(result.cost, 1082n);
});

test("curry roundtrip", (t) => {
const clvm = new ClvmAllocator();

const items = Array.from({ length: 10 }, (_, i) => i);
const ptr = clvm.curry(
ClvmPtr.nil(),
items.map((i) => clvm.newSmallNumber(i))
);
const uncurry = clvm.uncurry(ptr);
const args = uncurry.args.map((ptr) => clvm.smallNumber(ptr));

t.true(
compareBytes(clvm.treeHash(ClvmPtr.nil()), clvm.treeHash(uncurry.program))
);
t.deepEqual(args, items);
});

test("clvm serialization", (t) => {
const clvm = new ClvmAllocator();

for (const [ptr, hex] of [
[clvm.newAtom(Uint8Array.from([1, 2, 3])), "83010203"],
[clvm.newSmallNumber(420), "8201a4"],
[clvm.newBigInt(100n), "64"],
[
clvm.newPair(
clvm.newAtom(Uint8Array.from([1, 2, 3])),
clvm.newBigInt(100n)
),
"ff8301020364",
],
] as const) {
const serialized = clvm.serialize(ptr);
const deserialized = clvm.deserialize(serialized);

t.true(compareBytes(clvm.treeHash(ptr), clvm.treeHash(deserialized)));
t.is(hex as string, toHex(serialized));
}
});
10 changes: 7 additions & 3 deletions napi/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,11 @@ export interface Spend {
}
export declare function spendP2Standard(clvm: ClvmAllocator, syntheticKey: Uint8Array, conditions: Array<ClvmPtr>): Spend
export declare function spendP2Singleton(clvm: ClvmAllocator, launcherId: Uint8Array, coinId: Uint8Array, singletonInnerPuzzleHash: Uint8Array): Spend
export declare function testRoundtrip(value: bigint): bigint
export declare function compareBytes(a: Uint8Array, b: Uint8Array): boolean
export declare function sha256(bytes: Uint8Array): Uint8Array
export declare function fromHexRaw(hex: string): Uint8Array
export declare function fromHex(hex: string): Uint8Array
export declare function toHex(bytes: Uint8Array): string
export declare class ClvmAllocator {
constructor()
deserialize(value: Uint8Array): ClvmPtr
Expand All @@ -94,13 +98,13 @@ export declare class ClvmAllocator {
newList(values: Array<ClvmPtr>): ClvmPtr
newPair(first: ClvmPtr, rest: ClvmPtr): ClvmPtr
newAtom(value: Uint8Array): ClvmPtr
newU32(value: number): ClvmPtr
newSmallNumber(value: number): ClvmPtr
newBigInt(value: bigint): ClvmPtr
list(ptr: ClvmPtr): Array<ClvmPtr>
pair(ptr: ClvmPtr): Pair | null
atom(ptr: ClvmPtr): Uint8Array | null
atomLength(ptr: ClvmPtr): number | null
u32(ptr: ClvmPtr): number | null
smallNumber(ptr: ClvmPtr): number | null
bigInt(ptr: ClvmPtr): bigint | null
}
export declare class ClvmPtr {
Expand Down
8 changes: 6 additions & 2 deletions napi/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}

const { ClvmAllocator, ClvmPtr, toCoinId, parseNftInfo, parseUnspentNft, spendNft, mintNfts, spendP2Standard, spendP2Singleton, testRoundtrip } = nativeBinding
const { ClvmAllocator, ClvmPtr, toCoinId, parseNftInfo, parseUnspentNft, spendNft, mintNfts, spendP2Standard, spendP2Singleton, compareBytes, sha256, fromHexRaw, fromHex, toHex } = nativeBinding

module.exports.ClvmAllocator = ClvmAllocator
module.exports.ClvmPtr = ClvmPtr
Expand All @@ -321,4 +321,8 @@ module.exports.spendNft = spendNft
module.exports.mintNfts = mintNfts
module.exports.spendP2Standard = spendP2Standard
module.exports.spendP2Singleton = spendP2Singleton
module.exports.testRoundtrip = testRoundtrip
module.exports.compareBytes = compareBytes
module.exports.sha256 = sha256
module.exports.fromHexRaw = fromHexRaw
module.exports.fromHex = fromHex
module.exports.toHex = toHex
16 changes: 13 additions & 3 deletions napi/src/clvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,14 @@ impl ClvmAllocator {
}

#[napi]
pub fn new_u32(&mut self, value: u32) -> Result<ClvmPtr> {
pub fn new_small_number(&mut self, value: u32) -> Result<ClvmPtr> {
// TODO: Upstream a better check to clvmr?
if value > 67_108_863 {
return Err(Error::from_reason(
"Value is too large to fit in a small number".to_string(),
));
}

let ptr = self
.0
.new_small_number(value)
Expand Down Expand Up @@ -221,8 +228,11 @@ impl ClvmAllocator {
}

#[napi]
pub fn u32(&self, ptr: &ClvmPtr) -> Option<u32> {
self.0.small_number(ptr.0)
pub fn small_number(&self, ptr: &ClvmPtr) -> Option<u32> {
match self.0.sexp(ptr.0) {
SExp::Atom => self.0.small_number(ptr.0),
SExp::Pair(..) => None,
}
}

#[napi]
Expand Down
10 changes: 2 additions & 8 deletions napi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod nft;
mod nft_mint;
mod spend;
mod traits;
mod utils;

pub use clvm::*;
pub use coin::*;
Expand All @@ -21,11 +22,4 @@ pub use lineage_proof::*;
pub use nft::*;
pub use nft_mint::*;
pub use spend::*;

use traits::{IntoJs, IntoRust};

#[napi]
pub fn test_roundtrip(value: napi::bindgen_prelude::BigInt) -> napi::bindgen_prelude::BigInt {
let num: num_bigint::BigInt = value.into_rust().unwrap();
num.into_js().unwrap()
}
pub use utils::*;
39 changes: 39 additions & 0 deletions napi/src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use clvmr::sha2::Sha256;
use napi::bindgen_prelude::*;

use crate::traits::IntoJs;

#[napi]
pub fn compare_bytes(a: Uint8Array, b: Uint8Array) -> bool {
a.as_ref() == b.as_ref()
}

#[napi]
pub fn sha256(bytes: Uint8Array) -> Result<Uint8Array> {
let mut hasher = Sha256::new();
hasher.update(bytes.as_ref());
hasher.finalize().into_js()
}

#[napi]
pub fn from_hex_raw(hex: String) -> Result<Uint8Array> {
let bytes = hex::decode(hex).map_err(|error| Error::from_reason(error.to_string()))?;
bytes.into_js()
}

#[napi]
pub fn from_hex(hex: String) -> Result<Uint8Array> {
let mut hex = hex.as_str();

if let Some(stripped) = hex.strip_prefix("0x") {
hex = stripped;
}

let bytes = hex::decode(hex).map_err(|error| Error::from_reason(error.to_string()))?;
bytes.into_js()
}

#[napi]
pub fn to_hex(bytes: Uint8Array) -> String {
hex::encode(bytes.as_ref())
}

0 comments on commit 10e1cbe

Please sign in to comment.