From d9d398ccc9831df2b1483a9dab01f08e76729097 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Thu, 16 Apr 2020 13:07:14 -0400 Subject: [PATCH 1/3] Re-support WASM via simple stub headers libsecp256k1 really only barely uses libc at all, and in practice, things like memcpy/memcmp get optimized into something other than a libc call. Thus, if we provide simple stub headers, things seem to work with wasm-pack just fine. --- .travis.yml | 23 +++++++++++++++-------- Cargo.toml | 4 ++++ secp256k1-sys/build.rs | 4 ++++ secp256k1-sys/wasm-sysroot/stdio.h | 0 secp256k1-sys/wasm-sysroot/stdlib.h | 0 secp256k1-sys/wasm-sysroot/string.h | 4 ++++ src/lib.rs | 27 +++++++++++++++++++++++++++ 7 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 secp256k1-sys/wasm-sysroot/stdio.h create mode 100644 secp256k1-sys/wasm-sysroot/stdlib.h create mode 100644 secp256k1-sys/wasm-sysroot/string.h diff --git a/.travis.yml b/.travis.yml index 15302cf..0b57c15 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,19 +1,24 @@ language: rust -# cache: -# directories: -# - cargo_web +cache: + directories: + - wasm rust: - stable - beta - nightly - 1.22.0 +distro: bionic os: - linux - windows addons: chrome: stable + apt: + packages: + - clang-9 + - nodejs matrix: exclude: @@ -46,8 +51,10 @@ script: cd no_std_test && cargo run --release | grep -q "Verified Successfully"; fi - - #if [ ${TRAVIS_RUST_VERSION} == "stable" -a "$TRAVIS_OS_NAME" = "linux" ]; then - #CARGO_TARGET_DIR=cargo_web cargo install --verbose --force cargo-web && - #cargo web build --verbose --target=asmjs-unknown-emscripten && - #cargo web test --verbose --target=asmjs-unknown-emscripten; - #fi + - if [ ${TRAVIS_RUST_VERSION} == "stable" -a "$TRAVIS_OS_NAME" = "linux" ]; then + clang --version && + CARGO_TARGET_DIR=wasm cargo install --verbose --force wasm-pack && + sed -i 's/\[lib\]/[lib]\ncrate-type = ["cdylib", "rlib"]/' Cargo.toml && + CC=clang-9 wasm-pack build && + CC=clang-9 wasm-pack test --node; + fi diff --git a/Cargo.toml b/Cargo.toml index d93d7f0..05ee0d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,10 @@ rand_core = "0.4" serde_test = "1.0" bitcoin_hashes = "0.7" +[target.wasm32-unknown-unknown.dev-dependencies] +wasm-bindgen-test = "0.3" +rand = { version = "0.6", features = ["wasm-bindgen"] } + [dependencies.rand] version = "0.6" optional = true diff --git a/secp256k1-sys/build.rs b/secp256k1-sys/build.rs index af91b5a..8f54961 100644 --- a/secp256k1-sys/build.rs +++ b/secp256k1-sys/build.rs @@ -84,6 +84,10 @@ fn main() { .define("USE_SCALAR_8X32", Some("1")); } + if env::var("TARGET").unwrap() == "wasm32-unknown-unknown" { + base_config.include("wasm-sysroot"); + } + // secp256k1 base_config.file("depend/secp256k1/contrib/lax_der_parsing.c") .file("depend/secp256k1/src/secp256k1.c") diff --git a/secp256k1-sys/wasm-sysroot/stdio.h b/secp256k1-sys/wasm-sysroot/stdio.h new file mode 100644 index 0000000..e69de29 diff --git a/secp256k1-sys/wasm-sysroot/stdlib.h b/secp256k1-sys/wasm-sysroot/stdlib.h new file mode 100644 index 0000000..e69de29 diff --git a/secp256k1-sys/wasm-sysroot/string.h b/secp256k1-sys/wasm-sysroot/string.h new file mode 100644 index 0000000..7580db5 --- /dev/null +++ b/secp256k1-sys/wasm-sysroot/string.h @@ -0,0 +1,4 @@ +#include +void *memset(void *s, int c, size_t n); +void *memcpy(void *dest, const void *src, size_t n); +int memcmp(const void *s1, const void *s2, size_t n); diff --git a/src/lib.rs b/src/lib.rs index 5e66c46..d4f3497 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1060,6 +1060,33 @@ mod tests { assert_tokens(&sig.compact(), &[Token::BorrowedBytes(&SIG_BYTES[..])]); assert_tokens(&sig.readable(), &[Token::BorrowedStr(SIG_STR)]); } + + // For WASM, just run through our general tests in this file all at once. + #[cfg(target_arch = "wasm32")] + extern crate wasm_bindgen_test; + #[cfg(target_arch = "wasm32")] + use self::wasm_bindgen_test::*; + #[cfg(target_arch = "wasm32")] + #[wasm_bindgen_test] + fn stuff() { + test_manual_create_destroy(); + test_raw_ctx(); + // Note that, sadly, WASM doesn't currently properly unwind panics, so use of the library + // via unsafe primitives may cause abort() instead of catch-able panics. + /*assert!(std::panic::catch_unwind(|| { + test_panic_raw_ctx(); + }).is_err());*/ + test_preallocation(); + capabilities(); + signature_serialize_roundtrip(); + signature_display(); + signature_lax_der(); + sign_and_verify(); + sign_and_verify_extreme(); + sign_and_verify_fail(); + test_bad_slice(); + test_low_s(); + } } #[cfg(all(test, feature = "unstable"))] From 931253d41e84405ff3ae3ce025d570dfaf0685e2 Mon Sep 17 00:00:00 2001 From: Elichai Turkel Date: Wed, 29 Apr 2020 22:13:22 +0300 Subject: [PATCH 2/3] Add a size_t type to types --- secp256k1-sys/src/lib.rs | 28 ++++++++++++++-------------- secp256k1-sys/src/types.rs | 1 + 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/secp256k1-sys/src/lib.rs b/secp256k1-sys/src/lib.rs index 060ea33..a779235 100644 --- a/secp256k1-sys/src/lib.rs +++ b/secp256k1-sys/src/lib.rs @@ -152,7 +152,7 @@ extern "C" { // Contexts #[cfg_attr(not(feature = "external-symbols"), link_name = "rustsecp256k1_v0_1_1_context_preallocated_size")] - pub fn secp256k1_context_preallocated_size(flags: c_uint) -> usize; + pub fn secp256k1_context_preallocated_size(flags: c_uint) -> size_t; #[cfg_attr(not(feature = "external-symbols"), link_name = "rustsecp256k1_v0_1_1_context_preallocated_create")] pub fn secp256k1_context_preallocated_create(prealloc: *mut c_void, flags: c_uint) -> *mut Context; @@ -161,7 +161,7 @@ extern "C" { pub fn secp256k1_context_preallocated_destroy(cx: *mut Context); #[cfg_attr(not(feature = "external-symbols"), link_name = "rustsecp256k1_v0_1_1_context_preallocated_clone_size")] - pub fn secp256k1_context_preallocated_clone_size(cx: *const Context) -> usize; + pub fn secp256k1_context_preallocated_clone_size(cx: *const Context) -> size_t; #[cfg_attr(not(feature = "external-symbols"), link_name = "rustsecp256k1_v0_1_1_context_preallocated_clone")] pub fn secp256k1_context_preallocated_clone(cx: *const Context, prealloc: *mut c_void) -> *mut Context; @@ -174,19 +174,19 @@ extern "C" { // Pubkeys #[cfg_attr(not(feature = "external-symbols"), link_name = "rustsecp256k1_v0_1_1_ec_pubkey_parse")] pub fn secp256k1_ec_pubkey_parse(cx: *const Context, pk: *mut PublicKey, - input: *const c_uchar, in_len: usize) + input: *const c_uchar, in_len: size_t) -> c_int; #[cfg_attr(not(feature = "external-symbols"), link_name = "rustsecp256k1_v0_1_1_ec_pubkey_serialize")] pub fn secp256k1_ec_pubkey_serialize(cx: *const Context, output: *mut c_uchar, - out_len: *mut usize, pk: *const PublicKey, + out_len: *mut size_t, pk: *const PublicKey, compressed: c_uint) -> c_int; // Signatures #[cfg_attr(not(feature = "external-symbols"), link_name = "rustsecp256k1_v0_1_1_ecdsa_signature_parse_der")] pub fn secp256k1_ecdsa_signature_parse_der(cx: *const Context, sig: *mut Signature, - input: *const c_uchar, in_len: usize) + input: *const c_uchar, in_len: size_t) -> c_int; #[cfg_attr(not(feature = "external-symbols"), link_name = "rustsecp256k1_v0_1_1_ecdsa_signature_parse_compact")] @@ -196,12 +196,12 @@ extern "C" { #[cfg_attr(not(feature = "external-symbols"), link_name = "rustsecp256k1_v0_1_1_ecdsa_signature_parse_der_lax")] pub fn ecdsa_signature_parse_der_lax(cx: *const Context, sig: *mut Signature, - input: *const c_uchar, in_len: usize) + input: *const c_uchar, in_len: size_t) -> c_int; #[cfg_attr(not(feature = "external-symbols"), link_name = "rustsecp256k1_v0_1_1_ecdsa_signature_serialize_der")] pub fn secp256k1_ecdsa_signature_serialize_der(cx: *const Context, output: *mut c_uchar, - out_len: *mut usize, sig: *const Signature) + out_len: *mut size_t, sig: *const Signature) -> c_int; #[cfg_attr(not(feature = "external-symbols"), link_name = "rustsecp256k1_v0_1_1_ecdsa_signature_serialize_compact")] @@ -462,12 +462,12 @@ mod fuzz_dummy { } /// Return dummy size of context struct. - pub unsafe fn secp256k1_context_preallocated_size(_flags: c_uint) -> usize { + pub unsafe fn secp256k1_context_preallocated_size(_flags: c_uint) -> size_t { mem::size_of::() } /// Return dummy size of context struct. - pub unsafe fn secp256k1_context_preallocated_clone_size(_cx: *mut Context) -> usize { + pub unsafe fn secp256k1_context_preallocated_clone_size(_cx: *mut Context) -> size_t { mem::size_of::() } @@ -494,7 +494,7 @@ mod fuzz_dummy { // Pubkeys /// Parse 33/65 byte pubkey into PublicKey, losing compressed information pub unsafe fn secp256k1_ec_pubkey_parse(cx: *const Context, pk: *mut PublicKey, - input: *const c_uchar, in_len: usize) + input: *const c_uchar, in_len: size_t) -> c_int { assert!(!cx.is_null() && (*cx).0 as u32 & !(SECP256K1_START_NONE | SECP256K1_START_VERIFY | SECP256K1_START_SIGN) == 0); match in_len { @@ -521,7 +521,7 @@ mod fuzz_dummy { /// Serialize PublicKey back to 33/65 byte pubkey pub unsafe fn secp256k1_ec_pubkey_serialize(cx: *const Context, output: *mut c_uchar, - out_len: *mut usize, pk: *const PublicKey, + out_len: *mut size_t, pk: *const PublicKey, compressed: c_uint) -> c_int { assert!(!cx.is_null() && (*cx).0 as u32 & !(SECP256K1_START_NONE | SECP256K1_START_VERIFY | SECP256K1_START_SIGN) == 0); @@ -546,7 +546,7 @@ mod fuzz_dummy { // Signatures pub unsafe fn secp256k1_ecdsa_signature_parse_der(_cx: *const Context, _sig: *mut Signature, - _input: *const c_uchar, _in_len: usize) + _input: *const c_uchar, _in_len: size_t) -> c_int { unimplemented!(); } @@ -562,14 +562,14 @@ mod fuzz_dummy { } pub unsafe fn ecdsa_signature_parse_der_lax(_cx: *const Context, _sig: *mut Signature, - _input: *const c_uchar, _in_len: usize) + _input: *const c_uchar, _in_len: size_t) -> c_int { unimplemented!(); } /// Copies up to 72 bytes into output from sig pub unsafe fn secp256k1_ecdsa_signature_serialize_der(cx: *const Context, output: *mut c_uchar, - out_len: *mut usize, sig: *const Signature) + out_len: *mut size_t, sig: *const Signature) -> c_int { assert!(!cx.is_null() && (*cx).0 as u32 & !(SECP256K1_START_NONE | SECP256K1_START_VERIFY | SECP256K1_START_SIGN) == 0); diff --git a/secp256k1-sys/src/types.rs b/secp256k1-sys/src/types.rs index 8c9f362..ab29a77 100644 --- a/secp256k1-sys/src/types.rs +++ b/secp256k1-sys/src/types.rs @@ -4,6 +4,7 @@ use core::fmt; pub type c_int = i32; pub type c_uchar = u8; pub type c_uint = u32; +pub type size_t = usize; /// This might not match C's `c_char` exactly. /// The way we use it makes it fine either way but this type shouldn't be used outside of the library. From affc6b40272b175d164dcfcfce80a2d8c94602c2 Mon Sep 17 00:00:00 2001 From: Elichai Turkel Date: Wed, 29 Apr 2020 22:15:40 +0300 Subject: [PATCH 3/3] Add sanity checks for wasm32 for size and alignment of types --- secp256k1-sys/src/types.rs | 41 +++++++++++++++++++++++++++++- secp256k1-sys/wasm-sysroot/stdio.h | 17 +++++++++++++ src/context.rs | 6 +++++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/secp256k1-sys/src/types.rs b/secp256k1-sys/src/types.rs index ab29a77..78216af 100644 --- a/secp256k1-sys/src/types.rs +++ b/secp256k1-sys/src/types.rs @@ -12,7 +12,7 @@ pub type c_char = i8; /// This is an exact copy of https://doc.rust-lang.org/core/ffi/enum.c_void.html /// It should be Equivalent to C's void type when used as a pointer. -/// +/// /// We can replace this with `core::ffi::c_void` once we update the rustc version to >=1.30.0. #[repr(u8)] pub enum c_void { @@ -40,3 +40,42 @@ mod tests { assert_eq!(TypeId::of::(), TypeId::of::()); } } + + +#[doc(hidden)] +#[cfg(target_arch = "wasm32")] +pub fn sanity_checks_for_wasm() { + use std::mem::{size_of, align_of}; + extern "C" { + pub static WASM32_INT_SIZE: c_uchar; + pub static WASM32_INT_ALIGN: c_uchar; + + pub static WASM32_UNSIGNED_INT_SIZE: c_uchar; + pub static WASM32_UNSIGNED_INT_ALIGN: c_uchar; + + pub static WASM32_SIZE_T_SIZE: c_uchar; + pub static WASM32_SIZE_T_ALIGN: c_uchar; + + pub static WASM32_UNSIGNED_CHAR_SIZE: c_uchar; + pub static WASM32_UNSIGNED_CHAR_ALIGN: c_uchar; + + pub static WASM32_PTR_SIZE: c_uchar; + pub static WASM32_PTR_ALIGN: c_uchar; + } + unsafe { + assert_eq!(size_of::(), WASM32_INT_SIZE as usize); + assert_eq!(align_of::(), WASM32_INT_ALIGN as usize); + + assert_eq!(size_of::(), WASM32_UNSIGNED_INT_SIZE as usize); + assert_eq!(align_of::(), WASM32_UNSIGNED_INT_ALIGN as usize); + + assert_eq!(size_of::(), WASM32_SIZE_T_SIZE as usize); + assert_eq!(align_of::(), WASM32_SIZE_T_ALIGN as usize); + + assert_eq!(size_of::(), WASM32_UNSIGNED_CHAR_SIZE as usize); + assert_eq!(align_of::(), WASM32_UNSIGNED_CHAR_ALIGN as usize); + + assert_eq!(size_of::<*const ()>(), WASM32_PTR_SIZE as usize); + assert_eq!(align_of::<*const ()>(), WASM32_PTR_ALIGN as usize); + } +} diff --git a/secp256k1-sys/wasm-sysroot/stdio.h b/secp256k1-sys/wasm-sysroot/stdio.h index e69de29..36cc900 100644 --- a/secp256k1-sys/wasm-sysroot/stdio.h +++ b/secp256k1-sys/wasm-sysroot/stdio.h @@ -0,0 +1,17 @@ +#include +#define alignof(type) offsetof (struct { char c; type member; }, member) + +extern const unsigned char WASM32_INT_SIZE = sizeof(int); +extern const unsigned char WASM32_INT_ALIGN = alignof(int); + +extern const unsigned char WASM32_UNSIGNED_INT_SIZE = sizeof(unsigned int); +extern const unsigned char WASM32_UNSIGNED_INT_ALIGN = alignof(unsigned int); + +extern const unsigned char WASM32_SIZE_T_SIZE = sizeof(size_t); +extern const unsigned char WASM32_SIZE_T_ALIGN = alignof(size_t); + +extern const unsigned char WASM32_UNSIGNED_CHAR_SIZE = sizeof(unsigned char); +extern const unsigned char WASM32_UNSIGNED_CHAR_ALIGN = alignof(unsigned char); + +extern const unsigned char WASM32_PTR_SIZE = sizeof(void*); +extern const unsigned char WASM32_PTR_ALIGN = alignof(void*); \ No newline at end of file diff --git a/src/context.rs b/src/context.rs index a3d8fe8..3ddf9a7 100644 --- a/src/context.rs +++ b/src/context.rs @@ -106,6 +106,9 @@ mod std_only { impl Secp256k1 { /// Lets you create a context in a generic manner(sign/verify/all) pub fn gen_new() -> Secp256k1 { + #[cfg(target_arch = "wasm32")] + ffi::types::sanity_checks_for_wasm(); + let buf = vec![0u8; Self::preallocate_size_gen()].into_boxed_slice(); let ptr = Box::into_raw(buf); Secp256k1 { @@ -192,6 +195,9 @@ unsafe impl<'buf> Context for AllPreallocated<'buf> { impl<'buf, C: Context + 'buf> Secp256k1 { /// Lets you create a context with preallocated buffer in a generic manner(sign/verify/all) pub fn preallocated_gen_new(buf: &'buf mut [u8]) -> Result, Error> { + #[cfg(target_arch = "wasm32")] + ffi::types::sanity_checks_for_wasm(); + if buf.len() < Self::preallocate_size_gen() { return Err(Error::NotEnoughMemory); }