From c1423fca521334387deee99a449faad3b23fefcd Mon Sep 17 00:00:00 2001 From: Dimitri Date: Wed, 11 Dec 2024 12:20:27 +0700 Subject: [PATCH] feat: add public_key_hash wasm bindings --- package.json | 2 +- src/wasm.rs | 47 +++++++++++++++++++++++++++++++++++++ ts_tests/public_key_hash.ts | 23 ++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 ts_tests/public_key_hash.ts diff --git a/package.json b/package.json index 578e411..fb7f124 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "wasm:postbuild": "node build.js", "build": "npm run wasm:build && npm run wasm:postbuild" }, - "version": "0.4.52", + "version": "0.4.55", "devDependencies": { "@types/bun": "latest", "prettier": "^3.3.3" diff --git a/src/wasm.rs b/src/wasm.rs index c829836..9612212 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -13,6 +13,8 @@ use crate::{ AccountSalt, CircuitInputWithDecomposedRegexesAndExternalInputsParams, DecomposedRegex, ExternalInput, PaddedEmailAddr, ParsedEmail, }; +#[cfg(target_arch = "wasm32")] +use wasm_bindgen_futures::future_to_promise; #[wasm_bindgen] #[allow(non_snake_case)] @@ -264,3 +266,48 @@ pub async fn sha256Pad(data: JsValue, max_sha_bytes: usize) -> Promise { } } } + +#[wasm_bindgen] +#[allow(non_snake_case)] +#[cfg(target_arch = "wasm32")] +/// Computes the Poseidon hash of a public key. +/// +/// # Arguments +/// +/// * `public_key_n` - A `Uint8Array` containing the public key in little endian format. +/// +/// # Returns +/// +/// A `Promise` that resolves with the hexadecimal string representation of the hash, +/// or rejects with an error message. +pub async fn publicKeyHash(public_key_n: JsValue) -> Promise { + use crate::{field_to_hex, public_key_hash}; + console_error_panic_hook::set_once(); + + // We'll wrap the logic in a future so we can use `Promise` and `await`. + let future = async move { + // Convert JsValue (Uint8Array) to Vec + let mut key_bytes: Vec = from_value(public_key_n) + .map_err(|e| JsValue::from_str(&format!("Failed to convert input: {}", e)))?; + + // Reverse the bytes for little-endian format + key_bytes.reverse(); + + // Compute the hash + let hash = public_key_hash(&key_bytes) + .map_err(|e| JsValue::from_str(&format!("Failed to compute hash: {}", e)))?; + + // Convert hash field to hex string + let hex_hash = field_to_hex(&hash); + to_value(&hex_hash) + .map_err(|e| JsValue::from_str(&format!("Failed to serialize result: {}", e))) + }; + + // Convert the future into a JS Promise + future_to_promise(async move { + match future.await { + Ok(js_value) => Ok(js_value), + Err(e) => Err(e), + } + }) +} diff --git a/ts_tests/public_key_hash.ts b/ts_tests/public_key_hash.ts new file mode 100644 index 0000000..d137461 --- /dev/null +++ b/ts_tests/public_key_hash.ts @@ -0,0 +1,23 @@ +import { expect, test, describe, it } from "bun:test"; +import { publicKeyHash, init } from "../pkg"; + +const publicKeyHex = + "cfb0520e4ad78c4adb0deb5e605162b6469349fc1fde9269b88d596ed9f3735c00c592317c982320874b987bcc38e8556ac544bdee169b66ae8fe639828ff5afb4f199017e3d8e675a077f21cd9e5c526c1866476e7ba74cd7bb16a1c3d93bc7bb1d576aedb4307c6b948d5b8c29f79307788d7a8ebf84585bf53994827c23a5"; + +// Convert hex string to Uint8Array (no reversal needed) +function hexToBytes(hex) { + return new Uint8Array(hex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))); +} + +const publicKeyBytes = hexToBytes(publicKeyHex); + +// The expected hash result from the Rust test +const expectedHash = "0x181ab950d973ee53838532ecb1b8b11528f6ea7ab08e2868fb3218464052f953"; + +describe("publicKeyHash", async () => { + await init(); + it("should correctly hash the public key", async () => { + const result = await publicKeyHash(publicKeyBytes); + expect(result).toBe(expectedHash); + }); +});