From 13a7675829fa36fdc2f0d5dfdf7735b8af655bbd Mon Sep 17 00:00:00 2001 From: andrea Date: Thu, 14 Nov 2024 14:22:07 -0800 Subject: [PATCH] WASM: expose the `Asset` and `AssetIdentifier` types --- ironfish-rust-wasm/src/assets.rs | 233 +++++++++++++++++++++++++++++ ironfish-rust-wasm/src/keys/mod.rs | 4 +- ironfish-rust-wasm/src/lib.rs | 1 + ironfish-rust/src/assets/asset.rs | 2 +- 4 files changed, 238 insertions(+), 2 deletions(-) create mode 100644 ironfish-rust-wasm/src/assets.rs diff --git a/ironfish-rust-wasm/src/assets.rs b/ironfish-rust-wasm/src/assets.rs new file mode 100644 index 0000000000..b0606caeeb --- /dev/null +++ b/ironfish-rust-wasm/src/assets.rs @@ -0,0 +1,233 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::{ + errors::IronfishError, + keys::PublicAddress, + primitives::{ExtendedPoint, SubgroupPoint}, +}; +use ironfish::errors::IronfishErrorKind; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Asset(ironfish::assets::asset::Asset); + +#[wasm_bindgen] +impl Asset { + #[wasm_bindgen(constructor)] + pub fn deserialize(bytes: &[u8]) -> Result { + Ok(Self(ironfish::assets::asset::Asset::read(bytes)?)) + } + + #[wasm_bindgen] + pub fn serialize(&self) -> Vec { + let mut buf = Vec::new(); + self.0.write(&mut buf).expect("failed to serialize asset"); + buf + } + + #[wasm_bindgen(js_name = fromParts)] + pub fn from_parts( + creator: PublicAddress, + name: &str, + metadata: &str, + ) -> Result { + Ok(Self(ironfish::assets::asset::Asset::new( + creator.as_ref().to_owned(), + name, + metadata, + )?)) + } + + #[wasm_bindgen(js_name = fromPartsWithNonce)] + pub fn from_parts_with_nonce( + creator: PublicAddress, + name: &[u8], + metadata: &[u8], + nonce: u8, + ) -> Result { + let name = name + .try_into() + .map_err(|_| IronfishErrorKind::InvalidData)?; + let metadata = metadata + .try_into() + .map_err(|_| IronfishErrorKind::InvalidData)?; + Ok(Self(ironfish::assets::asset::Asset::new_with_nonce( + creator.as_ref().to_owned(), + name, + metadata, + nonce, + )?)) + } + + #[wasm_bindgen(getter)] + pub fn metadata(&self) -> Vec { + self.0.metadata().to_vec() + } + + #[wasm_bindgen(getter)] + pub fn name(&self) -> Vec { + self.0.name().to_vec() + } + + #[wasm_bindgen(getter)] + pub fn nonce(&self) -> u8 { + self.0.nonce() + } + + #[wasm_bindgen(getter)] + pub fn creator(&self) -> PublicAddress { + PublicAddress::deserialize(self.0.creator().as_slice()) + .expect("failed to deserialize public address") + } + + #[wasm_bindgen(getter)] + pub fn id(&self) -> AssetIdentifier { + self.0.id().to_owned().into() + } + + #[wasm_bindgen(getter, js_name = assetGenerator)] + pub fn asset_generator(&self) -> ExtendedPoint { + self.0.asset_generator().into() + } + + #[wasm_bindgen(getter, js_name = valueCommitmentGenerator)] + pub fn value_commitment_generator(&self) -> SubgroupPoint { + self.0.value_commitment_generator().into() + } +} + +impl From for Asset { + fn from(d: ironfish::assets::asset::Asset) -> Self { + Self(d) + } +} + +impl AsRef for Asset { + fn as_ref(&self) -> &ironfish::assets::asset::Asset { + &self.0 + } +} + +#[wasm_bindgen] +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct AssetIdentifier(ironfish::assets::asset_identifier::AssetIdentifier); + +#[wasm_bindgen] +impl AssetIdentifier { + #[wasm_bindgen(constructor)] + pub fn deserialize(bytes: &[u8]) -> Result { + Ok(Self( + ironfish::assets::asset_identifier::AssetIdentifier::read(bytes)?, + )) + } + + #[wasm_bindgen] + pub fn serialize(&self) -> Vec { + self.0.as_bytes().to_vec() + } + + #[wasm_bindgen(getter, js_name = assetGenerator)] + pub fn asset_generator(&self) -> ExtendedPoint { + self.0.asset_generator().into() + } + + #[wasm_bindgen(getter, js_name = valueCommitmentGenerator)] + pub fn value_commitment_generator(&self) -> SubgroupPoint { + self.0.value_commitment_generator().into() + } +} + +impl From for AssetIdentifier { + fn from(d: ironfish::assets::asset_identifier::AssetIdentifier) -> Self { + Self(d) + } +} + +impl AsRef for AssetIdentifier { + fn as_ref(&self) -> &ironfish::assets::asset_identifier::AssetIdentifier { + &self.0 + } +} + +#[cfg(test)] +mod tests { + mod asset { + use crate::{assets::Asset, keys::PublicAddress}; + use hex_literal::hex; + use wasm_bindgen_test::wasm_bindgen_test; + + fn test_address() -> PublicAddress { + PublicAddress::deserialize( + hex!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0").as_slice(), + ) + .unwrap() + } + + fn test_asset() -> Asset { + let asset = Asset::from_parts(test_address(), "name", "meta").unwrap(); + + assert_eq!(asset.creator(), test_address()); + assert_eq!( + asset.name(), + b"name\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" + ); + assert_eq!( + asset.metadata(), + b"meta\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\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\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" + ); + assert_eq!( + asset.id().serialize(), + hex!("2b845f8f97b90d2279bf502eb3ebdf71bf47460b083ca926421b0c7ee68ec816") + ); + + asset + } + + #[test] + #[wasm_bindgen_test] + fn serialize_deserialize_roundtrip() { + let asset = test_asset(); + + let serialization = asset.serialize(); + let deserialized = Asset::deserialize(&serialization[..]).unwrap(); + + assert_eq!(asset, deserialized); + assert_eq!(serialization, deserialized.serialize()); + } + + #[test] + #[wasm_bindgen_test] + fn from_parts_with_nonce() { + let asset = Asset::from_parts_with_nonce( + test_address(), + b"name\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", + b"meta\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\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\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, + ) + .unwrap(); + assert_eq!(asset, test_asset()); + } + } + + mod asset_identifier { + use crate::assets::AssetIdentifier; + use hex_literal::hex; + use wasm_bindgen_test::wasm_bindgen_test; + + #[test] + #[wasm_bindgen_test] + fn serialize_deserialize_roundtrip() { + let serialization = + hex!("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb1"); + let id = AssetIdentifier::deserialize(&serialization[..]).unwrap(); + assert_eq!(id.serialize(), serialization); + } + } +} diff --git a/ironfish-rust-wasm/src/keys/mod.rs b/ironfish-rust-wasm/src/keys/mod.rs index 7fda23142a..54b4364b7b 100644 --- a/ironfish-rust-wasm/src/keys/mod.rs +++ b/ironfish-rust-wasm/src/keys/mod.rs @@ -2,4 +2,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -pub mod public_address; +mod public_address; + +pub use public_address::PublicAddress; diff --git a/ironfish-rust-wasm/src/lib.rs b/ironfish-rust-wasm/src/lib.rs index 81ab379e5e..5c6ad85a3d 100644 --- a/ironfish-rust-wasm/src/lib.rs +++ b/ironfish-rust-wasm/src/lib.rs @@ -13,6 +13,7 @@ // The getrandom dependency exists only to ensure that the `js` feature is enabled use getrandom as _; +pub mod assets; pub mod errors; pub mod keys; pub mod primitives; diff --git a/ironfish-rust/src/assets/asset.rs b/ironfish-rust/src/assets/asset.rs index 81405df2fa..6a3ddf33bd 100644 --- a/ironfish-rust/src/assets/asset.rs +++ b/ironfish-rust/src/assets/asset.rs @@ -21,7 +21,7 @@ pub const ID_LENGTH: usize = ASSET_ID_LENGTH; /// Describes all the fields necessary for creating and transacting with an /// asset on the Iron Fish network -#[derive(Clone, Copy, PartialEq, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct Asset { /// Name of the asset pub(crate) name: [u8; NAME_LENGTH],