From dd1c45780bc6e81f5e240048328f24ff362efa27 Mon Sep 17 00:00:00 2001 From: andrea Date: Thu, 14 Nov 2024 15:05:59 -0800 Subject: [PATCH] WASM: expose the `Transaction` type --- Cargo.lock | 24 +++- ironfish-rust-wasm/Cargo.toml | 1 + ironfish-rust-wasm/src/lib.rs | 4 +- ironfish-rust-wasm/src/transaction/mod.rs | 166 ++++++++++++++++++++++ supply-chain/audits.toml | 5 + supply-chain/imports.lock | 43 ++++-- 6 files changed, 223 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a8fdff21ee..ee028eb588 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1652,6 +1652,7 @@ dependencies = [ "ironfish-jubjub", "ironfish_zkp", "rand", + "rayon", "wasm-bindgen", "wasm-bindgen-test", ] @@ -2311,24 +2312,24 @@ dependencies = [ [[package]] name = "rayon" -version = "1.6.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", + "wasm_sync", ] [[package]] name = "rayon-core" -version = "1.10.1" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "num_cpus", + "wasm_sync", ] [[package]] @@ -3188,6 +3189,17 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "wasm_sync" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff360cade7fec41ff0e9d2cda57fe58258c5f16def0e21302394659e6bbb0ea" +dependencies = [ + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "web-sys" version = "0.3.61" diff --git a/ironfish-rust-wasm/Cargo.toml b/ironfish-rust-wasm/Cargo.toml index 32d317331c..e6e51b7de5 100644 --- a/ironfish-rust-wasm/Cargo.toml +++ b/ironfish-rust-wasm/Cargo.toml @@ -21,6 +21,7 @@ ironfish = { version = "0.3.0", path = "../ironfish-rust" } ironfish-jubjub = "0.1.0" ironfish_zkp = { version = "0.2.0", path = "../ironfish-zkp" } rand = "0.8.5" +rayon = { version = "1.8.1", features = ["web_spin_lock"] } # need to explicitly enable the `web_spin_lock` in order to run in a browser wasm-bindgen = "0.2.95" [dev-dependencies] diff --git a/ironfish-rust-wasm/src/lib.rs b/ironfish-rust-wasm/src/lib.rs index 3048349c14..0e4865b20c 100644 --- a/ironfish-rust-wasm/src/lib.rs +++ b/ironfish-rust-wasm/src/lib.rs @@ -10,8 +10,10 @@ #![warn(unused_macro_rules)] #![warn(unused_qualifications)] -// The getrandom dependency exists only to ensure that the `js` feature is enabled +// These dependencies exist only to ensure that some browser-specific features are enabled, and are +// not actually used in our code use getrandom as _; +use rayon as _; pub mod assets; pub mod errors; diff --git a/ironfish-rust-wasm/src/transaction/mod.rs b/ironfish-rust-wasm/src/transaction/mod.rs index 3d0fd2a72d..18d2d0b65a 100644 --- a/ironfish-rust-wasm/src/transaction/mod.rs +++ b/ironfish-rust-wasm/src/transaction/mod.rs @@ -7,7 +7,173 @@ mod mints; mod outputs; mod spends; +use crate::{errors::IronfishError, primitives::PublicKey}; +use wasm_bindgen::prelude::*; + pub use burns::BurnDescription; pub use mints::MintDescription; pub use outputs::OutputDescription; pub use spends::SpendDescription; + +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct Transaction(ironfish::Transaction); + +#[wasm_bindgen] +impl Transaction { + #[wasm_bindgen(constructor)] + pub fn deserialize(bytes: &[u8]) -> Result { + Ok(Self(ironfish::Transaction::read(bytes)?)) + } + + #[wasm_bindgen] + pub fn serialize(&self) -> Vec { + let mut buf = Vec::new(); + self.0 + .write(&mut buf) + .expect("failed to serialize transaction"); + buf + } + + #[wasm_bindgen(getter)] + pub fn fee(&self) -> i64 { + self.0.fee() + } + + #[wasm_bindgen(getter)] + pub fn expiration(&self) -> u32 { + self.0.expiration() + } + + #[wasm_bindgen(getter, js_name = randomizedPublicKey)] + pub fn randomized_public_key(&self) -> PublicKey { + self.0.randomized_public_key().clone().into() + } + + #[wasm_bindgen(getter)] + pub fn spends(&self) -> Vec { + self.0 + .spends() + .iter() + .cloned() + .map(SpendDescription::from) + .collect() + } + + #[wasm_bindgen(getter)] + pub fn outputs(&self) -> Vec { + self.0 + .outputs() + .iter() + .cloned() + .map(OutputDescription::from) + .collect() + } + + #[wasm_bindgen(getter)] + pub fn mints(&self) -> Vec { + self.0 + .mints() + .iter() + .cloned() + .map(MintDescription::from) + .collect() + } + + #[wasm_bindgen(getter)] + pub fn burns(&self) -> Vec { + self.0 + .burns() + .iter() + .cloned() + .map(BurnDescription::from) + .collect() + } + + #[wasm_bindgen(js_name = transactionSignatureHash)] + pub fn transaction_signature_hash(&self) -> Result, IronfishError> { + self.0 + .transaction_signature_hash() + .map(|hash| hash.to_vec()) + .map_err(|err| err.into()) + } +} + +impl From for Transaction { + fn from(t: ironfish::Transaction) -> Self { + Self(t) + } +} + +impl AsRef for Transaction { + fn as_ref(&self) -> &ironfish::Transaction { + &self.0 + } +} + +#[cfg(test)] +mod tests { + use super::Transaction; + use hex_literal::hex; + use wasm_bindgen_test::wasm_bindgen_test; + + // Transaction copied from one of the fixtures in the `ironfish` NodeJS package + const TEST_TRANSACTION_BYTES: [u8; 661] = hex!( + "010000000000000000010000000000000000000000000000000000000000000000006cca88ffffffff00000000\ + 5e0c3088ca0767097b456c190416cc9ec82a296d5500876ce394218cde263c3e987762affaec55596ab06f7d7f4\ + 6dd949762f7705fc4978e64842242c59ff99e4dab95eaa46384f3e2e2705732db4d458bb3146e28620273558cc6\ + e31d2c4f5127d0e787468e5a56ca0d0a30b0434e22b2438e9d026f63be9dac46500671cb67197dd654f3e8fe68a\ + e3abca0fcc50009a89751a2f179c7470888f8a107492606cd30103a72870af2f87adf8210a2cb3d8d73f1150d99\ + e0dfbbb9daaba03e7daf24e26dd468b572b3dded502311ab83c17b87eb3db1a1bb8f7a3c5af0d40035d11b15a3c\ + e6f235138b2ef5f9853a01d61b9a9e549290618fbd697330380b9f0712e1d926b454b7a4cb7ddad47220bbaae68\ + 34ab67e0b42d6dd13b70d5ffb49c7067da8db3832b9f444990950bc25d7741a7ccb236b6a2eb346cfe8e02a34e6\ + b2f2993889cd256f9eb4cd2eebdc2bfdb9805e60730c92581fa4fea090f7baafcb8bf18a233ab150764bb76285b\ + 22b0f16831b8a3f47b4d41e96ab00a30e86994b4fb7b5a49d3ef8d37cce7035e741d1eacf649356f61169b06490\ + d702e34033d35f446864085f51315048de2e827746928492ef8cdec5c4faadf5bc82877462291118b643f44da99\ + e82335717cf1da9f149cc556100c4bd76c49726f6e2046697368206e6f746520656e6372797074696f6e206d696\ + e6572206b6579303030303030303030303030303030303030303030303030303030303030303030303030303030\ + 303030303030be04297828e5177a3ac901e89a7224a7e8f760a4377fc46b46384f3ef90a0c38e95fa386d2306f9\ + 8aeddeb6532ef022fb13e3b695d6df812587cd5eda684e502" + ); + + #[test] + #[wasm_bindgen_test] + fn deserialize() { + let tx = Transaction::deserialize(TEST_TRANSACTION_BYTES.as_slice()) + .expect("reading transaction should have succeeded"); + + assert_eq!(tx.fee(), -2_000_000_000); + assert_eq!(tx.expiration(), 0); + assert_eq!( + tx.randomized_public_key().serialize(), + hex!("5e0c3088ca0767097b456c190416cc9ec82a296d5500876ce394218cde263c3e") + ); + + assert_eq!(tx.spends().len(), 0); + assert_eq!(tx.outputs().len(), 1); + assert_eq!(tx.mints().len(), 0); + assert_eq!(tx.burns().len(), 0); + + let [output] = &tx.outputs()[..] else { + panic!("expected exactly one output") + }; + output + .partial_verify() + .expect("output verification should have succeeded"); + assert_eq!( + output.merkle_note().merkle_hash().serialize(), + hex!("2e1d926b454b7a4cb7ddad47220bbaae6834ab67e0b42d6dd13b70d5ffb49c70") + ); + + assert_eq!( + tx.transaction_signature_hash().unwrap(), + hex!("2ab1daec6bbb764e4247d3d82f1aa6da9eb71b98ac9e0dfc61e1d8aec487c9d2") + ); + } + + #[test] + #[wasm_bindgen_test] + fn deserialize_failure() { + Transaction::deserialize(b"abc").expect_err("reading transaction should have failed"); + } +} diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml index 99df836d8c..e6a5c2b03c 100644 --- a/supply-chain/audits.toml +++ b/supply-chain/audits.toml @@ -168,6 +168,11 @@ who = "andrea " criteria = "safe-to-run" version = "0.3.45" +[[audits.wasm_sync]] +who = "andrea " +criteria = "safe-to-deploy" +version = "0.1.2" + [[trusted.reddsa]] criteria = "safe-to-deploy" user-id = 6289 # Jack Grigg (str4d) diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index 9bb1cb54b6..7cee3625ab 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -718,6 +718,36 @@ who = "David Cook " criteria = "safe-to-deploy" version = "0.6.3" +[[audits.isrg.audits.rayon]] +who = "Brandon Pitman " +criteria = "safe-to-deploy" +delta = "1.6.1 -> 1.7.0" + +[[audits.isrg.audits.rayon]] +who = "David Cook " +criteria = "safe-to-deploy" +delta = "1.7.0 -> 1.8.0" + +[[audits.isrg.audits.rayon]] +who = "Ameer Ghani " +criteria = "safe-to-deploy" +delta = "1.8.0 -> 1.8.1" + +[[audits.isrg.audits.rayon]] +who = "Brandon Pitman " +criteria = "safe-to-deploy" +delta = "1.8.1 -> 1.9.0" + +[[audits.isrg.audits.rayon]] +who = "Brandon Pitman " +criteria = "safe-to-deploy" +delta = "1.9.0 -> 1.10.0" + +[[audits.isrg.audits.rayon-core]] +who = "Ameer Ghani " +criteria = "safe-to-deploy" +version = "1.12.1" + [[audits.isrg.audits.sha2]] who = "David Cook " criteria = "safe-to-deploy" @@ -1009,19 +1039,6 @@ criteria = "safe-to-deploy" delta = "1.5.3 -> 1.6.1" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.mozilla.audits.rayon-core]] -who = "Josh Stone " -criteria = "safe-to-deploy" -version = "1.9.3" -notes = "All code written or reviewed by Josh Stone or Niko Matsakis." -aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" - -[[audits.mozilla.audits.rayon-core]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "1.9.3 -> 1.10.1" -aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" - [[audits.mozilla.audits.redox_syscall]] who = "Jan-Erik Rediger " criteria = "safe-to-deploy"