diff --git a/Cargo.lock b/Cargo.lock index 318959030..68f645aef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -321,6 +321,8 @@ dependencies = [ "sha2 0.10.8", "thiserror", "tiny-bip39", + "wasm-bindgen", + "wasm-patch", ] [[package]] @@ -371,9 +373,12 @@ dependencies = [ "clvm-traits", "clvmr", "hex", + "paste", "pyo3", "rstest 0.17.0", "sha2 0.10.8", + "wasm-bindgen", + "wasm-patch", ] [[package]] @@ -466,7 +471,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.39", ] [[package]] @@ -476,7 +481,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.39", ] [[package]] @@ -549,7 +554,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.39", ] [[package]] @@ -564,7 +569,7 @@ version = "0.2.12" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.39", ] [[package]] @@ -798,7 +803,7 @@ checksum = "53e0efad4403bfc52dc201159c4b842a246a14b98c64b55dfd0f2d89729dfeb8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.39", ] [[package]] @@ -830,7 +835,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.39", ] [[package]] @@ -988,7 +993,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.39", ] [[package]] @@ -1508,6 +1513,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "pbkdf2" version = "0.11.0" @@ -1642,9 +1653,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ "unicode-ident", ] @@ -1712,9 +1723,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.32" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -2078,7 +2089,7 @@ checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.39", ] [[package]] @@ -2239,9 +2250,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.28" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -2309,7 +2320,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.39", ] [[package]] @@ -2561,9 +2572,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2571,16 +2582,16 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.39", "wasm-bindgen-shared", ] @@ -2598,9 +2609,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2608,22 +2619,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.39", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" [[package]] name = "wasm-bindgen-test" @@ -2649,6 +2660,15 @@ dependencies = [ "quote", ] +[[package]] +name = "wasm-patch" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "web-sys" version = "0.3.64" @@ -2830,7 +2850,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.39", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index ff5cc3458..45d7e1c24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ members = [ "clvm-utils/fuzz", "fuzz", "wasm", + "wasm-patch", ] exclude = ["wheel"] diff --git a/chia-bls/Cargo.toml b/chia-bls/Cargo.toml index fb70d8d41..d48a82572 100644 --- a/chia-bls/Cargo.toml +++ b/chia-bls/Cargo.toml @@ -26,6 +26,10 @@ thiserror = "1.0.44" pyo3 = { version = "0.19.0", features = ["multiple-pymethods"], optional = true } arbitrary = { version = "1.3.0" } +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen = "0.2.90" +wasm-patch = { version = "0.1.0", path = "../wasm-patch" } + [dev-dependencies] rand = "0.8.5" criterion = "0.5.1" diff --git a/chia-bls/src/gtelement.rs b/chia-bls/src/gtelement.rs index 818085f1c..68bd1d84f 100644 --- a/chia-bls/src/gtelement.rs +++ b/chia-bls/src/gtelement.rs @@ -19,6 +19,7 @@ use pyo3::exceptions::PyValueError; #[cfg(feature = "py-bindings")] use pyo3::{pyclass, pymethods, IntoPy, PyAny, PyObject, PyResult, Python}; +#[cfg_attr(target_arch = "wasm32", wasm_patch::with_wasm)] #[cfg_attr(feature = "py-bindings", pyclass, derive(PyStreamable, Clone))] pub struct GTElement(pub(crate) blst_fp12); diff --git a/chia-bls/src/public_key.rs b/chia-bls/src/public_key.rs index 33a376ad1..afad363c3 100644 --- a/chia-bls/src/public_key.rs +++ b/chia-bls/src/public_key.rs @@ -22,6 +22,7 @@ use chia_traits::to_json_dict::ToJsonDict; #[cfg(feature = "py-bindings")] use pyo3::{pyclass, pymethods, IntoPy, PyAny, PyObject, PyResult, Python}; +#[cfg_attr(target_arch = "wasm32", wasm_patch::with_wasm)] #[cfg_attr( feature = "py-bindings", pyclass(name = "G1Element"), diff --git a/chia-bls/src/secret_key.rs b/chia-bls/src/secret_key.rs index ceb0d38a6..342f8ea01 100644 --- a/chia-bls/src/secret_key.rs +++ b/chia-bls/src/secret_key.rs @@ -22,6 +22,7 @@ use chia_traits::to_json_dict::ToJsonDict; #[cfg(feature = "py-bindings")] use pyo3::{pyclass, pymethods, IntoPy, PyAny, PyObject, PyResult, Python}; +#[cfg_attr(target_arch = "wasm32", wasm_patch::with_wasm)] #[cfg_attr( feature = "py-bindings", pyclass(frozen, name = "PrivateKey"), diff --git a/chia-bls/src/signature.rs b/chia-bls/src/signature.rs index 4e3c77574..554f73698 100644 --- a/chia-bls/src/signature.rs +++ b/chia-bls/src/signature.rs @@ -26,6 +26,7 @@ use pyo3::{pyclass, pymethods, IntoPy, PyAny, PyObject, PyResult, Python}; // we use the augmented scheme pub const DST: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_AUG_"; +#[cfg_attr(target_arch = "wasm32", wasm_patch::with_wasm)] #[cfg_attr( feature = "py-bindings", pyclass(name = "G2Element"), diff --git a/chia-protocol/Cargo.toml b/chia-protocol/Cargo.toml index 1c4af1172..26bef7e3d 100644 --- a/chia-protocol/Cargo.toml +++ b/chia-protocol/Cargo.toml @@ -23,8 +23,13 @@ clvm-traits = { version = "=0.2.12", path = "../clvm-traits", features = ["deriv chia-bls = { version = "0.2.13", path = "../chia-bls" } arbitrary = { version = "1.3.0", features = ["derive"] } +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen = "0.2.90" +paste = "1.0.14" +wasm-patch = { version = "0.1.0", path = "../wasm-patch" } + [dev-dependencies] rstest = "0.17.0" [lib] -crate-type = ["rlib"] +crate-type = ["rlib", "cdylib"] diff --git a/chia-protocol/src/bytes.rs b/chia-protocol/src/bytes.rs index 01f6d57bb..1f19d9102 100644 --- a/chia-protocol/src/bytes.rs +++ b/chia-protocol/src/bytes.rs @@ -12,6 +12,11 @@ use std::fmt::Debug; use std::io::Cursor; use std::ops::Deref; +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::prelude::*; +#[cfg(target_arch = "wasm32")] +use paste::paste; + #[cfg(feature = "py-bindings")] use chia_traits::{FromJsonDict, ToJsonDict}; #[cfg(feature = "py-bindings")] @@ -23,6 +28,7 @@ use pyo3::prelude::*; #[cfg(feature = "py-bindings")] use pyo3::types::PyBytes; +#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] #[derive(Hash, Clone, Eq, PartialEq, PartialOrd, Ord)] #[cfg_attr(fuzzing, derive(arbitrary::Arbitrary))] pub struct Bytes(Vec); @@ -187,6 +193,22 @@ impl fmt::Display for Bytes { #[cfg_attr(fuzzing, derive(arbitrary::Arbitrary))] pub struct BytesImpl([u8; N]); +#[cfg(target_arch = "wasm32")] +macro_rules! generate_bytes_n { + ($($N:expr),+) => { + paste! { + $( + #[wasm_bindgen] + #[derive(Hash, PartialEq, Eq, Copy, Clone, PartialOrd, Ord)] + #[cfg_attr(fuzzing, derive(arbitrary::Arbitrary))] + pub struct []([u8; $N]); + )+ + } + }; +} +#[cfg(target_arch = "wasm32")] +generate_bytes_n!(32, 48, 96, 100); + impl Streamable for BytesImpl { fn update_digest(&self, digest: &mut Sha256) { digest.update(self.0); @@ -201,12 +223,54 @@ impl Streamable for BytesImpl { } } +#[cfg(target_arch = "wasm32")] +macro_rules! impl_streamable_for_bytes_n { + ($($N:expr),+) => { + paste! { + $( + impl Streamable for [] { + fn update_digest(&self, digest: &mut Sha256) { + digest.update(self.0); + } + fn stream(&self, out: &mut Vec) -> chia_error::Result<()> { + out.extend_from_slice(&self.0); + Ok(()) + } + + fn parse(input: &mut Cursor<&[u8]>) -> chia_error::Result { + Ok([](read_bytes(input, $N)?.try_into().unwrap())) + } + } + )+ + } + }; +} +#[cfg(target_arch = "wasm32")] +impl_streamable_for_bytes_n!(32, 48, 96, 100); + impl ToClvm for BytesImpl { fn to_clvm(&self, a: &mut Allocator) -> clvm_traits::Result { Ok(a.new_atom(self.0.as_slice())?) } } +#[cfg(target_arch = "wasm32")] +macro_rules! impl_to_clvm_for_bytes_n { + ($($N:expr),+) => { + paste! { + $( + impl ToClvm for [] { + fn to_clvm(&self, a: &mut Allocator) -> clvm_traits::Result { + Ok(a.new_atom(self.0.as_slice())?) + } + } + )+ + } + }; +} +#[cfg(target_arch = "wasm32")] +impl_to_clvm_for_bytes_n!(32, 48, 96, 100); + impl FromClvm for BytesImpl { fn from_clvm(a: &Allocator, ptr: NodePtr) -> clvm_traits::Result { let blob = match a.sexp(ptr) { @@ -224,16 +288,80 @@ impl FromClvm for BytesImpl { } } +#[cfg(target_arch = "wasm32")] +macro_rules! impl_from_clvm_for_bytes_n { + ($($N:expr),+) => { + paste! { + $( + impl FromClvm for [] { + fn from_clvm(a: &Allocator, ptr: NodePtr) -> clvm_traits::Result { + let blob = match a.sexp(ptr) { + SExp::Atom => { + if a.atom_len(ptr) != $N { + return Err(clvm_traits::Error::Custom("invalid size".to_string())); + } + a.atom(ptr) + } + _ => { + return Err(clvm_traits::Error::ExpectedAtom(ptr)); + } + }; + Ok(Self::from(blob)) + } + } + )+ + } + }; +} +#[cfg(target_arch = "wasm32")] +impl_from_clvm_for_bytes_n!(32, 48, 96, 100); + impl From<[u8; N]> for BytesImpl { fn from(v: [u8; N]) -> BytesImpl { BytesImpl::(v) } } + +#[cfg(target_arch = "wasm32")] +macro_rules! impl_from_u8n_for_bytes_n { + ($($N:expr),+) => { + paste! { + $( + impl From<[u8; $N]> for [] { + fn from(v: [u8; $N]) -> [] { + [](v) + } + } + )+ + } + }; +} +#[cfg(target_arch = "wasm32")] +impl_from_u8n_for_bytes_n!(32, 48, 96, 100); + impl From<&[u8; N]> for BytesImpl { fn from(v: &[u8; N]) -> BytesImpl { BytesImpl::(*v) } } + +#[cfg(target_arch = "wasm32")] +macro_rules! impl_from_ref_u8n_for_bytes_n { + ($($N:expr),+) => { + paste! { + $( + impl From<&[u8; $N]> for [] { + fn from(v: &[u8; $N]) -> [] { + [](*v) + } + } + )+ + } + }; +} +#[cfg(target_arch = "wasm32")] +impl_from_ref_u8n_for_bytes_n!(32, 48, 96, 100); + impl From<&[u8]> for BytesImpl { fn from(v: &[u8]) -> BytesImpl { if v.len() != N { @@ -244,6 +372,29 @@ impl From<&[u8]> for BytesImpl { ret } } + +#[cfg(target_arch = "wasm32")] +macro_rules! impl_from_ref_u8_for_bytes_n { + ($($N:expr),+) => { + paste! { + $( + impl From<&[u8]> for [] { + fn from(v: &[u8]) -> [] { + if v.len() != $N { + panic!("invalid atom, expected {} bytes (got {})", $N, v.len()); + } + let mut ret = []([0; $N]); + ret.0.copy_from_slice(v); + ret + } + } + )+ + } + }; +} +#[cfg(target_arch = "wasm32")] +impl_from_ref_u8_for_bytes_n!(32, 48, 96, 100); + impl From<&Vec> for BytesImpl { fn from(v: &Vec) -> BytesImpl { if v.len() != N { @@ -254,88 +405,338 @@ impl From<&Vec> for BytesImpl { ret } } + +#[cfg(target_arch = "wasm32")] +macro_rules! impl_from_ref_vec_u8_for_bytes_n { + ($($N:expr),+) => { + paste! { + $( + impl From<&Vec> for [] { + fn from(v: &Vec) -> [] { + if v.len() != $N { + panic!("invalid atom, expected {} bytes (got {})", $N, v.len()); + } + let mut ret = []([0; $N]); + ret.0.copy_from_slice(v); + ret + } + } + )+ + } + }; +} +#[cfg(target_arch = "wasm32")] +impl_from_ref_vec_u8_for_bytes_n!(32, 48, 96, 100); + impl<'a, const N: usize> From<&'a BytesImpl> for &'a [u8; N] { fn from(v: &'a BytesImpl) -> &'a [u8; N] { &v.0 } } +#[cfg(target_arch = "wasm32")] +macro_rules! impl_from_a_bytes_u8_for_a_u8n { + ($($N:expr),+) => { + paste! { + $( + impl<'a> From<&'a []> for &'a [u8; $N] { + fn from(v: &'a []) -> &'a [u8; $N] { + &v.0 + } + } + )+ + } + }; +} +#[cfg(target_arch = "wasm32")] +impl_from_a_bytes_u8_for_a_u8n!(32, 48, 96, 100); + impl From<&BytesImpl> for [u8; N] { fn from(v: &BytesImpl) -> [u8; N] { v.0 } } +#[cfg(target_arch = "wasm32")] +macro_rules! impl_from_ref_bytes_n_for_u8n { + ($($N:expr),+) => { + paste! { + $( + impl From<&[]> for [u8; $N] { + fn from(v: &[]) -> [u8; $N] { + v.0 + } + } + )+ + } + }; +} +#[cfg(target_arch = "wasm32")] +impl_from_ref_bytes_n_for_u8n!(32, 48, 96, 100); + impl<'a, const N: usize> From<&'a BytesImpl> for &'a [u8] { fn from(v: &'a BytesImpl) -> &'a [u8] { &v.0 } } +#[cfg(target_arch = "wasm32")] +macro_rules! impl_from_a_bytes_n_for_a_u8 { + ($($N:expr),+) => { + paste! { + $( + impl<'a> From<&'a []> for &'a [u8] { + fn from(v: &'a []) -> &'a [u8] { + &v.0 + } + } + )+ + } + }; +} +#[cfg(target_arch = "wasm32")] +impl_from_a_bytes_n_for_a_u8!(32, 48, 96, 100); impl BytesImpl { pub fn to_vec(&self) -> Vec { self.0.to_vec() } } +#[cfg(target_arch = "wasm32")] +macro_rules! impl_bytes_n { + ($($N:expr),+) => { + paste! { + $( + impl [] { + pub fn to_vec(&self) -> Vec { + self.0.to_vec() + } + } + )+ + } + }; +} +#[cfg(target_arch = "wasm32")] +impl_bytes_n!(32, 48, 96, 100); impl AsRef<[u8]> for BytesImpl { fn as_ref(&self) -> &[u8] { &self.0 } } + +#[cfg(target_arch = "wasm32")] +macro_rules! impl_as_ref_for_bytes_n { + ($($N:expr),+) => { + paste! { + $( + impl AsRef<[u8]> for [] { + fn as_ref(&self) -> &[u8] { &self.0 } + } + )+ + } + }; +} +#[cfg(target_arch = "wasm32")] +impl_as_ref_for_bytes_n!(32, 48, 96, 100); + impl Deref for BytesImpl { type Target = [u8]; fn deref(&self) -> &[u8] { &self.0 } } + +#[cfg(target_arch = "wasm32")] +macro_rules! impl_deref_for_bytes_n { + ($($N:expr),+) => { + paste! { + $( + impl Deref for [] { + type Target = [u8]; + fn deref(&self) -> &[u8] { &self.0 } + } + )+ + } + }; +} +#[cfg(target_arch = "wasm32")] +impl_deref_for_bytes_n!(32, 48, 96, 100); + impl Debug for BytesImpl { fn fmt(&self, formatter: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { formatter.write_str(&hex::encode(self.0)) } } + +#[cfg(target_arch = "wasm32")] +macro_rules! impl_debug_for_bytes_n { + ($($N:expr),+) => { + paste! { + $( + impl Debug for [] { + fn fmt(&self, formatter: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + formatter.write_str(&hex::encode(self.0)) + } + } + )+ + } + }; +} +#[cfg(target_arch = "wasm32")] +impl_debug_for_bytes_n!(32, 48, 96, 100); + impl fmt::Display for BytesImpl { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str(&hex::encode(self.0)) } } +#[cfg(target_arch = "wasm32")] +macro_rules! impl_display_for_bytes_n { + ($($N:expr),+) => { + paste! { + $( + impl fmt::Display for [] { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(&hex::encode(self.0)) + } + } + )+ + } + }; +} +#[cfg(target_arch = "wasm32")] +impl_display_for_bytes_n!(32, 48, 96, 100); + impl PartialEq<&[u8]> for BytesImpl { fn eq(&self, lhs: &&[u8]) -> bool { self.0 == *lhs } } +#[cfg(target_arch = "wasm32")] +macro_rules! impl_partial_eq_for_ref_bytes_n { + ($($N:expr),+) => { + paste! { + $( + impl PartialEq<&[u8]> for [] { + fn eq(&self, lhs: &&[u8]) -> bool { self.0 == *lhs } + } + )+ + } + }; +} +#[cfg(target_arch = "wasm32")] +impl_partial_eq_for_ref_bytes_n!(32, 48, 96, 100); + impl PartialEq> for &[u8] { fn eq(&self, lhs: &BytesImpl) -> bool { self == &lhs.0 } } +#[cfg(target_arch = "wasm32")] +macro_rules! impl_partial_eq_for_ref_u8 { + ($($N:expr),+) => { + paste! { + $( + impl PartialEq<[]> for &[u8] { + fn eq(&self, lhs: &[]) -> bool { self == &lhs.0 } + } + )+ + } + }; +} +#[cfg(target_arch = "wasm32")] +impl_partial_eq_for_ref_u8!(32, 48, 96, 100); + impl PartialEq<&[u8; N]> for BytesImpl { fn eq(&self, lhs: &&[u8; N]) -> bool { &self.0 == *lhs } } +#[cfg(target_arch = "wasm32")] +macro_rules! impl_partial_eq_ref_u8n_for_bytes_n { + ($($N:expr),+) => { + paste! { + $( + impl PartialEq<&[u8; $N]> for [] { + fn eq(&self, lhs: &&[u8; $N]) -> bool { &self.0 == *lhs } + } + )+ + } + }; +} +#[cfg(target_arch = "wasm32")] +impl_partial_eq_ref_u8n_for_bytes_n!(32, 48, 96, 100); + impl PartialEq> for &[u8; N] { fn eq(&self, lhs: &BytesImpl) -> bool { *self == &lhs.0 } } +#[cfg(target_arch = "wasm32")] +macro_rules! impl_partial_eq_bytes_n_for_ref_u8n { + ($($N:expr),+) => { + paste! { + $( + impl PartialEq<[]> for &[u8; $N] { + fn eq(&self, lhs: &[]) -> bool { *self == &lhs.0 } + } + )+ + } + }; +} +#[cfg(target_arch = "wasm32")] +impl_partial_eq_bytes_n_for_ref_u8n!(32, 48, 96, 100); + impl PartialEq<[u8; N]> for BytesImpl { fn eq(&self, lhs: &[u8; N]) -> bool { &self.0 == lhs } } +#[cfg(target_arch = "wasm32")] +macro_rules! impl_partial_eq_u8n_for_bytes_n { + ($($N:expr),+) => { + paste! { + $( + impl PartialEq<[u8; $N]> for [] { + fn eq(&self, lhs: &[u8; $N]) -> bool { + &self.0 == lhs + } + } + )+ + } + }; +} +#[cfg(target_arch = "wasm32")] +impl_partial_eq_u8n_for_bytes_n!(32, 48, 96, 100); + + impl PartialEq> for [u8; N] { fn eq(&self, lhs: &BytesImpl) -> bool { self == &lhs.0 } } +#[cfg(target_arch = "wasm32")] +macro_rules! impl_partial_eq_bytes_n_for_u8n { + ($($N:expr),+) => { + paste! { + $( + impl PartialEq<[]> for [u8; $N] { + fn eq(&self, lhs: &[]) -> bool { self == &lhs.0 } + } + )+ + } + }; +} +#[cfg(target_arch = "wasm32")] +impl_partial_eq_bytes_n_for_u8n!(32, 48, 96, 100); + #[cfg(feature = "py-bindings")] impl ToJsonDict for BytesImpl { fn to_json_dict(&self, py: Python) -> PyResult { @@ -370,9 +771,13 @@ impl FromJsonDict for BytesImpl { } } +#[cfg(not(target_arch = "wasm32"))] pub type Bytes32 = BytesImpl<32>; +#[cfg(not(target_arch = "wasm32"))] pub type Bytes48 = BytesImpl<48>; +#[cfg(not(target_arch = "wasm32"))] pub type Bytes96 = BytesImpl<96>; +#[cfg(not(target_arch = "wasm32"))] pub type Bytes100 = BytesImpl<100>; #[cfg(feature = "py-bindings")] diff --git a/chia-protocol/src/chia_protocol.rs b/chia-protocol/src/chia_protocol.rs index b2fd2a653..c894b1e67 100644 --- a/chia-protocol/src/chia_protocol.rs +++ b/chia-protocol/src/chia_protocol.rs @@ -7,6 +7,7 @@ use crate::Bytes; #[cfg(feature = "py-bindings")] use chia_py_streamable_macro::{PyJsonDict, PyStreamable}; +#[cfg_attr(target_arch = "wasm32", wasm_patch::with_wasm)] #[repr(u8)] #[cfg_attr(feature = "py-bindings", derive(PyJsonDict, PyStreamable))] #[cfg_attr(fuzzing, derive(arbitrary::Arbitrary))] @@ -127,6 +128,7 @@ pub trait ChiaProtocolMessage { fn msg_type() -> ProtocolMessageTypes; } +#[cfg_attr(target_arch = "wasm32", wasm_patch::with_wasm)] #[repr(u8)] #[cfg_attr(feature = "py-bindings", derive(PyJsonDict, PyStreamable))] #[cfg_attr(fuzzing, derive(arbitrary::Arbitrary))] diff --git a/chia-protocol/src/coin.rs b/chia-protocol/src/coin.rs index e66551172..8f64efc84 100644 --- a/chia-protocol/src/coin.rs +++ b/chia-protocol/src/coin.rs @@ -1,5 +1,5 @@ use crate::streamable_struct; -use crate::{bytes::Bytes32, BytesImpl}; +use crate::bytes::Bytes32; use chia_streamable_macro::Streamable; use clvm_traits::{clvm_list, destructure_list, match_list, FromClvm, ToClvm}; use clvmr::allocator::NodePtr; @@ -62,7 +62,7 @@ impl ToClvm for Coin { impl FromClvm for Coin { fn from_clvm(a: &Allocator, ptr: NodePtr) -> clvm_traits::Result { let destructure_list!(parent_coin_info, puzzle_hash, amount) = - , BytesImpl<32>, u64)>::from_clvm(a, ptr)?; + ::from_clvm(a, ptr)?; Ok(Coin { parent_coin_info, puzzle_hash, diff --git a/chia-protocol/src/fullblock.rs b/chia-protocol/src/fullblock.rs index 5003f3ba8..302ce0d52 100644 --- a/chia-protocol/src/fullblock.rs +++ b/chia-protocol/src/fullblock.rs @@ -43,6 +43,7 @@ impl FullBlock { self.foliage.foliage_transaction_block_hash.is_some() } + #[cfg_attr(target_arch = "wasm32", wasm_patch::conv_u128_to_u64_for_wasm)] pub fn total_iters(&self) -> u128 { self.reward_chain_block.total_iters } @@ -51,6 +52,7 @@ impl FullBlock { self.reward_chain_block.height } + #[cfg_attr(target_arch = "wasm32", wasm_patch::conv_u128_to_u64_for_wasm)] pub fn weight(&self) -> u128 { self.reward_chain_block.weight } diff --git a/chia-protocol/src/header_block.rs b/chia-protocol/src/header_block.rs index e5af72402..c4aa92044 100644 --- a/chia-protocol/src/header_block.rs +++ b/chia-protocol/src/header_block.rs @@ -45,6 +45,7 @@ impl HeaderBlock { self.reward_chain_block.height } + #[cfg_attr(target_arch = "wasm32", wasm_patch::conv_u128_to_u64_for_wasm)] pub fn weight(&self) -> u128 { self.reward_chain_block.weight } @@ -53,6 +54,7 @@ impl HeaderBlock { self.foliage.hash().into() } + #[cfg_attr(target_arch = "wasm32", wasm_patch::conv_u128_to_u64_for_wasm)] pub fn total_iters(&self) -> u128 { self.reward_chain_block.total_iters } diff --git a/chia-protocol/src/message_struct.rs b/chia-protocol/src/message_struct.rs index 111163667..aa4154370 100644 --- a/chia-protocol/src/message_struct.rs +++ b/chia-protocol/src/message_struct.rs @@ -27,6 +27,7 @@ macro_rules! message_struct { #[macro_export] macro_rules! streamable_struct { ($name:ident {$($field:ident: $t:ty $(,)? )*}) => { + #[cfg_attr(target_arch = "wasm32", wasm_patch::with_wasm)] #[cfg_attr(feature = "py-bindings", pyo3::pyclass(get_all, frozen), derive(chia_py_streamable_macro::PyJsonDict, chia_py_streamable_macro::PyStreamable))] #[derive(Streamable, Hash, Debug, Clone, Eq, PartialEq)] #[cfg_attr(fuzzing, derive(arbitrary::Arbitrary))] @@ -34,6 +35,7 @@ macro_rules! streamable_struct { $(pub $field: $t),* } + #[cfg_attr(target_arch = "wasm32", wasm_patch::with_wasm)] #[cfg(not(feature = "py-bindings"))] #[allow(clippy::too_many_arguments)] impl $name { diff --git a/chia-protocol/src/program.rs b/chia-protocol/src/program.rs index 10bff1f1b..b752c0a7b 100644 --- a/chia-protocol/src/program.rs +++ b/chia-protocol/src/program.rs @@ -17,6 +17,7 @@ use chia_py_streamable_macro::PyStreamable; #[cfg(feature = "py-bindings")] use pyo3::prelude::*; +#[cfg_attr(target_arch = "wasm32", wasm_patch::with_wasm)] #[cfg_attr(feature = "py-bindings", pyclass, derive(PyStreamable))] #[derive(Hash, Debug, Clone, Eq, PartialEq)] pub struct Program(Bytes); diff --git a/chia-protocol/src/unfinished_block.rs b/chia-protocol/src/unfinished_block.rs index 16e2560a4..fd2d428e5 100644 --- a/chia-protocol/src/unfinished_block.rs +++ b/chia-protocol/src/unfinished_block.rs @@ -38,6 +38,7 @@ impl UnfinishedBlock { self.foliage.foliage_transaction_block_hash.is_some() } + #[cfg_attr(target_arch = "wasm32", wasm_patch::conv_u128_to_u64_for_wasm)] pub fn total_iters(&self) -> u128 { self.reward_chain_block.total_iters } diff --git a/wasm-patch/Cargo.toml b/wasm-patch/Cargo.toml new file mode 100644 index 000000000..2903cdf55 --- /dev/null +++ b/wasm-patch/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "wasm-patch" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +proc-macro2 = "1.0.70" +quote = "1.0.33" +syn = { version = "2.0.39", features = ["full", "visit-mut"]} diff --git a/wasm-patch/src/lib.rs b/wasm-patch/src/lib.rs new file mode 100644 index 000000000..92b88a1b4 --- /dev/null +++ b/wasm-patch/src/lib.rs @@ -0,0 +1,48 @@ +extern crate proc_macro; + +use syn::{visit_mut::VisitMut, parse_quote, Type}; +use proc_macro::{TokenStream}; + +// See https://github.com/rustwasm/wasm-bindgen/issues/3707 +fn _patch_issue_3707(input: &TokenStream) -> TokenStream { + let mut s = input.to_string(); + s = format!("#[wasm_bindgen::prelude::wasm_bindgen(getter_with_clone)] {}", s); + s.parse().unwrap() +} + +struct TypeConverter; + +// As of Dec 2023, wasm-bindgen cannot handle u128 at all. +// We convert u128 into u64 here. +// u64 is then converted into bigint in JS which has no size limitation. +// So this conversion does no harm unless a value greater than u64_max is +// communicated between JS and Wasm Runtime. +impl VisitMut for TypeConverter { + fn visit_type_mut(&mut self, i: &mut Type) { + if let Type::Path(type_path) = i { + if type_path.path.is_ident("u128") { + *i = parse_quote! { u64 }; + } + } + syn::visit_mut::visit_type_mut(self, i); + } +} + +#[proc_macro_attribute] +pub fn with_wasm(_args: TokenStream, input: TokenStream) -> TokenStream { + let mut ast: syn::File = syn::parse(input.into()).unwrap(); + let mut converter = TypeConverter; + converter.visit_file_mut(&mut ast); + + let input_maybe_modified = quote::quote!(#ast); + _patch_issue_3707(&input_maybe_modified.into()) +} + +#[proc_macro_attribute] +pub fn conv_u128_to_u64_for_wasm(_args: TokenStream, input: TokenStream) -> TokenStream { + let mut ast: syn::ImplItemFn = syn::parse(input.into()).unwrap(); + let mut converter = TypeConverter; + converter.visit_impl_item_fn_mut(&mut ast); + + quote::quote!(#ast).into() +} diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index dc1bafcc7..7185c1e9e 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -16,6 +16,6 @@ path = "src/lib.rs" [dependencies] chia = { path = ".." } -wasm-bindgen = "=0.2.87" +wasm-bindgen = "=0.2.90" wasm-bindgen-test = "=0.3.37" js-sys = "=0.3.64"