diff --git a/.gitignore b/.gitignore index 1db9278e16..d653eb3628 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,12 @@ deno.lock **/*.rs.bk .rusk .vscode + +# Ignore IDE specific files +.idea + +# Ignore macOS specific files +.DS_Store + +# Ignore C headers +*.h \ No newline at end of file diff --git a/wallet-core/Cargo.toml b/wallet-core/Cargo.toml index d1503b38d1..6d8fb23c44 100644 --- a/wallet-core/Cargo.toml +++ b/wallet-core/Cargo.toml @@ -34,3 +34,4 @@ tiny-bip39 = { workspace = true } [features] debug = [] +ffi = [] diff --git a/wallet-core/Makefile b/wallet-core/Makefile index 8cdc89385d..c99f27ac2a 100644 --- a/wallet-core/Makefile +++ b/wallet-core/Makefile @@ -7,7 +7,6 @@ help: ## Display this help screen test: cargo test --release - wasm: ## Build the WASM files @cargo wasm @@ -18,4 +17,7 @@ clippy: ## Run clippy doc: ## Run doc gen @cargo doc --release +headers: ## Generate C headers + @cbindgen --crate dusk-wallet-core --output wallet-core.h --config cbindgen.toml + .PHONY: all help test diff --git a/wallet-core/cbindgen.toml b/wallet-core/cbindgen.toml new file mode 100644 index 0000000000..73a7cd9f0e --- /dev/null +++ b/wallet-core/cbindgen.toml @@ -0,0 +1,5 @@ +language = "C" +autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */" + +[defines] +"feature = ffi" = "ffi" \ No newline at end of file diff --git a/wallet-core/src/ffi.rs b/wallet-core/src/ffi.rs index 064e7d2f33..85498ecec2 100644 --- a/wallet-core/src/ffi.rs +++ b/wallet-core/src/ffi.rs @@ -95,12 +95,15 @@ where slice.iter().map(|&byte| callback(seed, byte)).collect() } +#[no_mangle] +static PROFILE_SIZE: usize = PhoenixPublicKey::SIZE + BlsPublicKey::SIZE; + /// Generate a profile (account / address pair) for the given seed and index. #[no_mangle] pub unsafe extern "C" fn generate_profile( seed: &Seed, index: u8, - profile: *mut [u8; PhoenixPublicKey::SIZE + BlsPublicKey::SIZE], + profile: *mut [u8; PROFILE_SIZE], ) -> ErrorCode { let ppk = derive_phoenix_pk(seed, index).to_bytes(); let bpk = derive_bls_pk(seed, index).to_bytes(); @@ -123,7 +126,7 @@ pub unsafe extern "C" fn generate_profile( /// Filter all notes and their block height that are owned by the given keys, /// mapped to their nullifiers. #[no_mangle] -pub unsafe fn map_owned( +pub unsafe extern "C" fn map_owned( seed: &Seed, indexes: *const u8, notes_ptr: *const u8, @@ -154,10 +157,14 @@ pub unsafe fn map_owned( let len = bytes.len().to_le_bytes(); - let ptr = mem::malloc(4 + bytes.len() as u32); - + #[cfg(target_family = "wasm")] + let ptr = mem::malloc((4 + bytes.len()) as u32); + #[cfg(target_family = "wasm")] let ptr = ptr as *mut u8; + #[cfg(not(target_family = "wasm"))] + let ptr = mem::allocate(4 + bytes.len()); + *owned_ptr = ptr; ptr::copy_nonoverlapping(len.as_ptr(), ptr, 4); @@ -192,7 +199,7 @@ pub unsafe fn display_scalar( } #[no_mangle] -pub unsafe fn accounts_into_raw( +pub unsafe extern "C" fn accounts_into_raw( accounts_ptr: *const u8, raws_ptr: *mut *mut u8, ) -> ErrorCode { @@ -212,9 +219,15 @@ pub unsafe fn accounts_into_raw( }); let len = bytes.len().to_le_bytes(); - let ptr = mem::malloc(4 + bytes.len() as u32); + + #[cfg(target_family = "wasm")] + let ptr = mem::malloc((4 + bytes.len()) as u32); + #[cfg(target_family = "wasm")] let ptr = ptr as *mut u8; + #[cfg(not(target_family = "wasm"))] + let ptr = mem::allocate(4 + bytes.len()); + *raws_ptr = ptr; ptr::copy_nonoverlapping(len.as_ptr(), ptr, 4); @@ -226,7 +239,7 @@ pub unsafe fn accounts_into_raw( /// Calculate the balance info for the phoenix address at the given index for /// the given seed. #[no_mangle] -pub unsafe fn balance( +pub unsafe extern "C" fn balance( seed: &Seed, index: u8, notes_ptr: *const u8, @@ -249,7 +262,7 @@ pub unsafe fn balance( /// Pick the notes to be used in a transaction from an owned notes list. #[no_mangle] -pub unsafe fn pick_notes( +pub unsafe extern "C" fn pick_notes( seed: &Seed, index: u8, value: *const u64, @@ -274,7 +287,7 @@ pub unsafe fn pick_notes( /// Gets the bookmark from the given note. #[no_mangle] -pub unsafe fn bookmarks( +pub unsafe extern "C" fn bookmarks( notes_ptr: *const u8, bookmarks_ptr: *mut *mut u8, ) -> ErrorCode { @@ -288,9 +301,14 @@ pub unsafe fn bookmarks( .flat_map(|&num| num.to_le_bytes()) .collect(); + #[cfg(target_family = "wasm")] let ptr = mem::malloc(bytes.len() as u32); + #[cfg(target_family = "wasm")] let ptr = ptr as *mut u8; + #[cfg(not(target_family = "wasm"))] + let ptr = mem::allocate(bytes.len()); + *bookmarks_ptr = ptr; ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, bytes.len()); @@ -312,7 +330,7 @@ impl Prove for NoOpProver { } #[no_mangle] -pub unsafe fn into_proven( +pub unsafe extern "C" fn into_proven( tx_ptr: *const u8, proof_ptr: *const u8, proven_ptr: *mut *mut u8, @@ -328,9 +346,14 @@ pub unsafe fn into_proven( let len = bytes.len().to_le_bytes(); - let ptr = mem::malloc(4 + bytes.len() as u32); + #[cfg(target_family = "wasm")] + let ptr = mem::malloc((4 + bytes.len()) as u32); + #[cfg(target_family = "wasm")] let ptr = ptr as *mut u8; + #[cfg(not(target_family = "wasm"))] + let ptr = mem::allocate(4 + bytes.len()); + *proven_ptr = ptr; ptr::copy_nonoverlapping(len.as_ptr(), ptr, 4); @@ -345,7 +368,7 @@ pub unsafe fn into_proven( } #[no_mangle] -pub unsafe fn phoenix( +pub unsafe extern "C" fn phoenix( rng: &[u8; 32], seed: &Seed, sender_index: u8, @@ -417,9 +440,14 @@ pub unsafe fn phoenix( let bytes = to_bytes::<_, 4096>(&tx).or(Err(ErrorCode::ArchivingError))?; let len = bytes.len().to_le_bytes(); - let ptr = mem::malloc(4 + bytes.len() as u32); + #[cfg(target_family = "wasm")] + let ptr = mem::malloc((4 + bytes.len()) as u32); + #[cfg(target_family = "wasm")] let ptr = ptr as *mut u8; + #[cfg(not(target_family = "wasm"))] + let ptr = mem::allocate(4 + bytes.len()); + *tx_ptr = ptr; ptr::copy_nonoverlapping(len.as_ptr(), ptr, 4); @@ -428,9 +456,14 @@ pub unsafe fn phoenix( let bytes = prover.circuits.into_inner(); let len = bytes.len().to_le_bytes(); - let ptr = mem::malloc(4 + bytes.len() as u32); + #[cfg(target_family = "wasm")] + let ptr = mem::malloc((4 + bytes.len()) as u32); + #[cfg(target_family = "wasm")] let ptr = ptr as *mut u8; + #[cfg(not(target_family = "wasm"))] + let ptr = mem::allocate(4 + bytes.len()); + *proof_ptr = ptr; ptr::copy_nonoverlapping(len.as_ptr(), ptr, 4); @@ -440,7 +473,7 @@ pub unsafe fn phoenix( } #[no_mangle] -pub unsafe fn moonlight( +pub unsafe extern "C" fn moonlight( seed: &Seed, sender_index: u8, receiver: *const [u8; BlsPublicKey::SIZE], @@ -491,9 +524,14 @@ pub unsafe fn moonlight( let bytes = Transaction::Moonlight(tx.clone()).to_var_bytes(); let len = bytes.len().to_le_bytes(); - let ptr = mem::malloc(4 + bytes.len() as u32); + #[cfg(target_family = "wasm")] + let ptr = mem::malloc((4 + bytes.len()) as u32); + #[cfg(target_family = "wasm")] let ptr = ptr as *mut u8; + #[cfg(not(target_family = "wasm"))] + let ptr = mem::allocate(4 + bytes.len()); + *tx_ptr = ptr; ptr::copy_nonoverlapping(len.as_ptr(), ptr, 4); @@ -508,7 +546,7 @@ pub unsafe fn moonlight( } #[no_mangle] -pub unsafe fn phoenix_to_moonlight( +pub unsafe extern "C" fn phoenix_to_moonlight( rng: &[u8; 32], seed: &Seed, profile_index: u8, @@ -566,9 +604,14 @@ pub unsafe fn phoenix_to_moonlight( let bytes = to_bytes::<_, 4096>(&tx).or(Err(ErrorCode::ArchivingError))?; let len = bytes.len().to_le_bytes(); - let ptr = mem::malloc(4 + bytes.len() as u32); + #[cfg(target_family = "wasm")] + let ptr = mem::malloc((4 + bytes.len()) as u32); + #[cfg(target_family = "wasm")] let ptr = ptr as *mut u8; + #[cfg(not(target_family = "wasm"))] + let ptr = mem::allocate(4 + bytes.len()); + *tx_ptr = ptr; ptr::copy_nonoverlapping(len.as_ptr(), ptr, 4); @@ -577,9 +620,14 @@ pub unsafe fn phoenix_to_moonlight( let bytes = prover.circuits.into_inner(); let len = bytes.len().to_le_bytes(); - let ptr = mem::malloc(4 + bytes.len() as u32); + #[cfg(target_family = "wasm")] + let ptr = mem::malloc((4 + bytes.len()) as u32); + #[cfg(target_family = "wasm")] let ptr = ptr as *mut u8; + #[cfg(not(target_family = "wasm"))] + let ptr = mem::allocate(4 + bytes.len()); + *proof_ptr = ptr; ptr::copy_nonoverlapping(len.as_ptr(), ptr, 4); @@ -589,7 +637,7 @@ pub unsafe fn phoenix_to_moonlight( } #[no_mangle] -pub unsafe fn moonlight_to_phoenix( +pub unsafe extern "C" fn moonlight_to_phoenix( rng: &[u8; 32], seed: &Seed, profile_index: u8, @@ -621,9 +669,14 @@ pub unsafe fn moonlight_to_phoenix( let bytes = tx.to_var_bytes(); let len = bytes.len().to_le_bytes(); - let ptr = mem::malloc(4 + bytes.len() as u32); + #[cfg(target_family = "wasm")] + let ptr = mem::malloc((4 + bytes.len()) as u32); + #[cfg(target_family = "wasm")] let ptr = ptr as *mut u8; + #[cfg(not(target_family = "wasm"))] + let ptr = mem::allocate(4 + bytes.len()); + *tx_ptr = ptr; ptr::copy_nonoverlapping(len.as_ptr(), ptr, 4); @@ -638,7 +691,7 @@ pub unsafe fn moonlight_to_phoenix( } #[no_mangle] -pub unsafe fn moonlight_stake( +pub unsafe extern "C" fn moonlight_stake( seed: &Seed, sender_index: u8, stake_value: *const u64, @@ -677,9 +730,14 @@ pub unsafe fn moonlight_stake( let bytes = tx.to_var_bytes(); let len = bytes.len().to_le_bytes(); - let ptr = mem::malloc(4 + bytes.len() as u32); + #[cfg(target_family = "wasm")] + let ptr = mem::malloc((4 + bytes.len()) as u32); + #[cfg(target_family = "wasm")] let ptr = ptr as *mut u8; + #[cfg(not(target_family = "wasm"))] + let ptr = mem::allocate(4 + bytes.len()); + *tx_ptr = ptr; ptr::copy_nonoverlapping(len.as_ptr(), ptr, 4); @@ -694,7 +752,7 @@ pub unsafe fn moonlight_stake( } #[no_mangle] -pub unsafe fn moonlight_unstake( +pub unsafe extern "C" fn moonlight_unstake( rng: &[u8; 32], seed: &Seed, sender_index: u8, @@ -742,9 +800,14 @@ pub unsafe fn moonlight_unstake( let bytes = tx.to_var_bytes(); let len = bytes.len().to_le_bytes(); - let ptr = mem::malloc(4 + bytes.len() as u32); + #[cfg(target_family = "wasm")] + let ptr = mem::malloc((4 + bytes.len()) as u32); + #[cfg(target_family = "wasm")] let ptr = ptr as *mut u8; + #[cfg(not(target_family = "wasm"))] + let ptr = mem::allocate(4 + bytes.len()); + *tx_ptr = ptr; ptr::copy_nonoverlapping(len.as_ptr(), ptr, 4); @@ -759,7 +822,7 @@ pub unsafe fn moonlight_unstake( } #[no_mangle] -pub unsafe fn moonlight_stake_reward( +pub unsafe extern "C" fn moonlight_stake_reward( rng: &[u8; 32], seed: &Seed, sender_index: u8, @@ -807,9 +870,14 @@ pub unsafe fn moonlight_stake_reward( let bytes = tx.to_var_bytes(); let len = bytes.len().to_le_bytes(); - let ptr = mem::malloc(4 + bytes.len() as u32); + #[cfg(target_family = "wasm")] + let ptr = mem::malloc((4 + bytes.len()) as u32); + #[cfg(target_family = "wasm")] let ptr = ptr as *mut u8; + #[cfg(not(target_family = "wasm"))] + let ptr = mem::allocate(4 + bytes.len()); + *tx_ptr = ptr; ptr::copy_nonoverlapping(len.as_ptr(), ptr, 4); @@ -824,12 +892,12 @@ pub unsafe fn moonlight_stake_reward( } #[no_mangle] -pub unsafe fn create_tx_data( +pub unsafe extern "C" fn create_tx_data( fn_name_len: *const u32, fn_name_buf: *mut u8, fn_args_len: *const u32, fn_args_buf: *mut u8, - contract_id: [u8; 32], + contract_id_buf: *mut u8, memo_len: *const u32, memo_buf: *mut u8, rkyv_ptr: *mut *mut u8, @@ -847,6 +915,9 @@ pub unsafe fn create_tx_data( *fn_args_len as usize, *fn_args_len as usize, ); + + let mut contract_id = [0u8; 32]; + ptr::copy_nonoverlapping(contract_id_buf, contract_id.as_mut_ptr(), 32); let contract = ContractId::from_bytes(contract_id); let contract_call = ContractCall { @@ -854,6 +925,8 @@ pub unsafe fn create_tx_data( fn_args, contract, }; + println!("call"); + TransactionData::Call(contract_call) } else { let memo = alloc::vec::Vec::from_raw_parts( @@ -861,6 +934,8 @@ pub unsafe fn create_tx_data( *memo_len as usize, *memo_len as usize, ); + println!("memo"); + TransactionData::Memo(memo) }; let bytes = match rkyv::to_bytes::<_, 4096>(&tx_data) { @@ -869,9 +944,50 @@ pub unsafe fn create_tx_data( }; let len = bytes.len().to_le_bytes(); + #[cfg(target_family = "wasm")] + let ptr = mem::malloc(4 + bytes.len() as u32); + #[cfg(target_family = "wasm")] + let ptr = ptr as *mut u8; + + #[cfg(not(target_family = "wasm"))] + let ptr = mem::allocate(4 + bytes.len()); + + *rkyv_ptr = ptr; + + ptr::copy_nonoverlapping(len.as_ptr(), ptr, 4); + ptr::copy_nonoverlapping(bytes.as_ptr(), ptr.add(4), bytes.len()); + + ErrorCode::Ok +} + +#[no_mangle] +pub unsafe extern "C" fn create_blob_tx_data( + input_ptr: *const u8, + rkyv_ptr: *mut *mut u8, +) -> ErrorCode { + let buffer = mem::read_buffer(input_ptr); + + let tx_data = match TransactionData::from_slice(buffer) { + Ok(td) => td.unwrap(), + Err(e) => { + return Err(ErrorCode::DeserializationError)?; + } + }; + + let bytes = match rkyv::to_bytes::<_, 131203>(&tx_data) { + Ok(v) => v.to_vec(), + Err(_) => return ErrorCode::ArchivingError, + }; + let len = bytes.len().to_le_bytes(); + + #[cfg(target_family = "wasm")] let ptr = mem::malloc(4 + bytes.len() as u32); + #[cfg(target_family = "wasm")] let ptr = ptr as *mut u8; + #[cfg(not(target_family = "wasm"))] + let ptr = mem::allocate(4 + bytes.len()); + *rkyv_ptr = ptr; ptr::copy_nonoverlapping(len.as_ptr(), ptr, 4); diff --git a/wallet-core/src/ffi/mem.rs b/wallet-core/src/ffi/mem.rs index c2e1dd2d29..f7df79fca9 100644 --- a/wallet-core/src/ffi/mem.rs +++ b/wallet-core/src/ffi/mem.rs @@ -14,12 +14,14 @@ use rkyv::{check_archived_root, Archive, Deserialize}; use crate::ffi::error::ErrorCode; -/// The alignment of the memory allocated by the FFI. +#[cfg(target_family = "wasm")] +/// The alignment of the memory allocated by the FFI for WASM. /// /// This is 1 because we're not allocating any complex data structures, and /// just interacting with the memory directly. const ALIGNMENT: usize = 1; +#[cfg(target_family = "wasm")] /// Allocates a buffer of `len` bytes on the WASM memory. #[no_mangle] pub fn malloc(len: u32) -> u32 { @@ -30,6 +32,7 @@ pub fn malloc(len: u32) -> u32 { } } +#[cfg(target_family = "wasm")] /// Frees a previously allocated buffer on the WASM memory. #[no_mangle] pub fn free(ptr: u32, len: u32) { @@ -39,6 +42,39 @@ pub fn free(ptr: u32, len: u32) { } } +#[cfg(not(target_family = "wasm"))] +// Standard system alignment for non-WASM environment. +const ALIGNMENT: usize = std::mem::size_of::(); + +#[cfg(not(target_family = "wasm"))] +/// Allocates a buffer of `len` bytes on the system memory. +pub extern "C" fn allocate(len: usize) -> *mut u8 { + unsafe { + let layout = Layout::from_size_align_unchecked(len as usize, ALIGNMENT); + let ptr = alloc(layout); + ptr + } +} + +#[cfg(not(target_family = "wasm"))] +#[no_mangle] +pub extern "C" fn deallocate(ptr: *mut u8, len: u32) { + if ptr.is_null() { + return; + } + + let size = len as usize; + + let layout = match Layout::from_size_align(size, 8) { + Ok(layout) => layout, + Err(_) => return, + }; + + unsafe { + dealloc(ptr, layout); + } +} + /// Read a buffer from the given pointer. pub unsafe fn read_buffer<'a>(ptr: *const u8) -> &'a [u8] { let len = slice::from_raw_parts(ptr, 4); diff --git a/wallet-core/src/ffi/panic.rs b/wallet-core/src/ffi/panic.rs index aa5dc08a5e..aec8e14023 100644 --- a/wallet-core/src/ffi/panic.rs +++ b/wallet-core/src/ffi/panic.rs @@ -9,6 +9,7 @@ mod panic_handling { #[panic_handler] #[allow(unused)] + #[cfg(target_family = "wasm")] fn panic(info: &PanicInfo) -> ! { #[cfg(debug_assertions)] eprintln!("{}", info); diff --git a/wallet-core/src/lib.rs b/wallet-core/src/lib.rs index da77793f23..4b15cc9f31 100644 --- a/wallet-core/src/lib.rs +++ b/wallet-core/src/lib.rs @@ -18,9 +18,13 @@ #[global_allocator] static ALLOC: dlmalloc::GlobalDlmalloc = dlmalloc::GlobalDlmalloc; +#[cfg(all(not(target_family = "wasm"), feature = "ffi"))] +#[global_allocator] +static GLOBAL: std::alloc::System = std::alloc::System; + extern crate alloc; -#[cfg(target_family = "wasm")] +#[cfg(any(target_family = "wasm", feature = "ffi"))] #[macro_use] mod ffi;