Skip to content

Commit

Permalink
Add wasm bindings for Hash
Browse files Browse the repository at this point in the history
  • Loading branch information
mvines committed Dec 9, 2021
1 parent 488dc37 commit 03a956e
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 3 deletions.
4 changes: 3 additions & 1 deletion sdk/program/src/hash.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! The `hash` module provides functions for creating SHA-256 hashes.
use {
crate::sanitize::Sanitize,
crate::{sanitize::Sanitize, wasm_bindgen},
borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
sha2::{Digest, Sha256},
std::{convert::TryFrom, fmt, mem, str::FromStr},
Expand All @@ -11,6 +11,8 @@ use {
pub const HASH_BYTES: usize = 32;
/// Maximum string length of a base58 encoded hash
const MAX_BASE58_LEN: usize = 44;

#[wasm_bindgen]
#[derive(
Serialize,
Deserialize,
Expand Down
57 changes: 57 additions & 0 deletions sdk/program/src/wasm/hash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//! `Hash` Javascript interface
#![cfg(target_arch = "wasm32")]
#![allow(non_snake_case)]
use {
crate::{hash::*, wasm::display_to_jsvalue},
js_sys::{Array, Uint8Array},
wasm_bindgen::{prelude::*, JsCast},
};

#[wasm_bindgen]
impl Hash {
/// Create a new Hash object
///
/// * `value` - optional hash as a base58 encoded string, `Uint8Array`, `[number]`
#[wasm_bindgen(constructor)]
pub fn constructor(value: JsValue) -> Result<Hash, JsValue> {
if let Some(base58_str) = value.as_string() {
base58_str.parse::<Hash>().map_err(display_to_jsvalue)
} else if let Some(uint8_array) = value.dyn_ref::<Uint8Array>() {
Ok(Hash::new(&uint8_array.to_vec()))
} else if let Some(array) = value.dyn_ref::<Array>() {
let mut bytes = vec![];
let iterator = js_sys::try_iter(&array.values())?.expect("array to be iterable");
for x in iterator {
let x = x?;

if let Some(n) = x.as_f64() {
if n >= 0. && n <= 255. {
bytes.push(n as u8);
continue;
}
}
return Err(format!("Invalid array argument: {:?}", x).into());
}
Ok(Hash::new(&bytes))
} else if value.is_undefined() {
Ok(Hash::default())
} else {
Err("Unsupported argument".into())
}
}

/// Return the base58 string representation of the hash
pub fn toString(&self) -> String {
self.to_string()
}

/// Checks if two `Hash`s are equal
pub fn equals(&self, other: &Hash) -> bool {
self == other
}

/// Return the `Uint8Array` representation of the hash
pub fn toBytes(&self) -> Box<[u8]> {
self.0.clone().into()
}
}
10 changes: 8 additions & 2 deletions sdk/program/src/wasm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@
#![cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;

pub mod hash;
pub mod pubkey;

/// Initialize Javascript logging and panic handler
#[wasm_bindgen]
pub fn init() {
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
console_log::init_with_level(log::Level::Info).unwrap();
use std::sync::Once;
static INIT: Once = Once::new();

INIT.call_once(|| {
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
console_log::init_with_level(log::Level::Info).unwrap();
});
}

pub fn display_to_jsvalue<T: std::fmt::Display>(display: T) -> JsValue {
Expand Down
81 changes: 81 additions & 0 deletions sdk/program/tests/hash.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { expect } from "chai";
import { init, Hash } from "crate";
init();

// TODO: wasm_bindgen doesn't currently support exporting constants
const HASH_BYTES = 32;

describe("Hash", function () {
it("invalid", () => {
expect(() => {
new Hash([
3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
]);
}).to.throw();

expect(() => {
new Hash([
'invalid', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
]);
}).to.throw();

expect(() => {
new Hash(
"0x300000000000000000000000000000000000000000000000000000000000000000000"
);
}).to.throw();

expect(() => {
new Hash(
"0x300000000000000000000000000000000000000000000000000000000000000"
);
}).to.throw();

expect(() => {
new Hash(
"135693854574979916511997248057056142015550763280047535983739356259273198796800000"
);
}).to.throw();

expect(() => {
new Hash("12345");
}).to.throw();
});

it("toString", () => {
const key = new Hash("CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3");
expect(key.toString()).to.eq("CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3");

const key2 = new Hash("1111111111111111111111111111BukQL");
expect(key2.toString()).to.eq("1111111111111111111111111111BukQL");

const key3 = new Hash("11111111111111111111111111111111");
expect(key3.toString()).to.eq("11111111111111111111111111111111");

const key4 = new Hash([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
]);
expect(key4.toString()).to.eq("11111111111111111111111111111111");
});

it("toBytes", () => {
const key = new Hash("CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3");
expect(key.toBytes()).to.deep.equal(
new Uint8Array([
3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
])
);

const key2 = new Hash();
expect(key2.toBytes()).to.deep.equal(
new Uint8Array([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
])
);
});
});

0 comments on commit 03a956e

Please sign in to comment.