diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..96b624f --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[target.wasm32-unknown-unknown] +rustflags = ["-C", "target-feature=+simd128"] diff --git a/.gitignore b/.gitignore index e345084..5f25664 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ Cargo.lock **/*.rs.bk mod.wasm + +.DS_Store diff --git a/CHANGELOG.md b/CHANGELOG.md index b5ff2cd..769e262 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.3.1] + +### Added +- Add the compact module to make the web assembly API compatiblite on non rust endpoints +- Add support for phoenix_core methods +- Add support for stake contract functions +- Add support for transfer contract functions + +### Changed +- Change the whole api to support `wasm32-unkonwn-unknown` +- Set json as the function argument and return value format with types defined in assets/schema.json + +### Fixed +- Pass in the Fee and the crossover instead of creating it from rng at wallet-core side which caused courrpted proof +- Fix the input selecting algorithm from custom new one to old wallet-core to fix wrong picking of notes +- Fix the execute to work with only one public spend key and one `Transaction` + +## [Old Changelog below] + ## [Unreleased] ### Added diff --git a/Cargo.toml b/Cargo.toml index d310388..8092568 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dusk-wallet-core" -version = "0.21.6" +version = "0.21.8" edition = "2021" description = "The core functionality of the Dusk wallet" license = "MPL-2.0" @@ -55,7 +55,6 @@ hex = { version = "0.4", default-features = false, features = ["alloc"] } hashbrown = "0.14.3" stake-contract-types = "0.1.0-rc" - [features] # rust platforms can use this module without the compact feature default = ["compat"] @@ -73,3 +72,9 @@ wasmtime = "20" [build-dependencies] schemafy_lib = "0.6" + +[profile.release] +lto = true +codegen-units = 1 +opt-level = 'z' +wasm-opt = ['-O3'] diff --git a/assets/dusk_wallet_core.wasm b/assets/dusk_wallet_core.wasm index c0c1655..9658b7c 100755 Binary files a/assets/dusk_wallet_core.wasm and b/assets/dusk_wallet_core.wasm differ diff --git a/assets/schema.json b/assets/schema.json index 3cedb8d..b6ed2d1 100644 --- a/assets/schema.json +++ b/assets/schema.json @@ -448,32 +448,29 @@ } } }, - "RkyvTreeLeafResponse": { - "description": "The response of the public_keys function", + "RkyvTreeLeafArgs": { + "description": "The arguments of the rkyv tree leaf function", "type": "object", "required": [ - "block_height", - "note", - "last_pos" + "bytes", + "seed" ], "properties": { - "block_height": { - "description": "The block height of the note.", - "type": "integer", - "format": "uint64" - }, - "note": { - "description": "Bytes of note at the block_height", + "bytes": { + "description": "Bytes that are rkyv serialized into a phoenix_core::transaction::TreeLeaf", "type": "array", "items": { "type": "integer", "format": "uint8" } }, - "last_pos": { - "description": "Last position of the note", - "type": "integer", - "format": "uint64" + "seed": { + "description": "Seed used to derive the keys of the wallet", + "type": "array", + "items": { + "type": "integer", + "format": "uint8" + } } } }, @@ -576,49 +573,48 @@ } } }, - "CheckNoteOwnershipArgs": { - "description": "Arguments of the check_note_ownership function", + "CheckNoteOwnershipResponse": { + "description": "Response of check_note_ownership function", "type": "object", - "required": ["note", "seed"], + "required": ["notes", "last_pos", "block_heights", "public_spend_keys", "nullifiers"], "properties": { - "note": { - "description": "A singular note we want to check the validity of", + "notes": { + "description": "The raw owned note", "type": "array", "items": { - "type": "integer", - "format": "uint8" + "type": "array", + "items": { + "type": "integer", + "format": "uint8" + } } }, - "seed": { - "description": "The seed to generate the view keys from", + "last_pos": { + "description": "The last position of the note", + "type": "integer", + "format": "uint64" + }, + "block_heights": { + "description": "The block heights of the notes in the same order the notes were returned seperated by comma", + "type": "string" + }, + "public_spend_keys": { + "description": "The public spend keys of the notes in the same order the notes were returned", "type": "array", "items": { - "type": "integer", - "format": "uint8" + "type": "string" } - } - } - }, - "CheckNoteOwnershipResponse": { - "description": "Response of check_note_ownership function", - "type": "object", - "required": ["is_owned", "nullifier"], - "properties": { - "is_owned": { - "description": "Is the note owned by any of the view keys in the provided seed", - "type": "boolean" }, - "nullifier": { - "description": "Nullifier of the note that we were checking the ownership of", + "nullifiers": { + "description": "The nullifiers of the notes in the same order the notes were returned", "type": "array", "items": { - "type": "integer", - "format": "uint8" + "type": "array", + "items": { + "type": "integer", + "format": "uint8" + } } - }, - "public_key": { - "description": "A base 58 encoded public key string", - "type": "string" } } }, diff --git a/build.sh b/build.sh deleted file mode 100644 index 71be775..0000000 --- a/build.sh +++ /dev/null @@ -1,4 +0,0 @@ -make wasm -cp target/wasm32-unknown-unknown/release/dusk_wallet_core.wasm ./assets -wasm-opt -O3 ./assets/dusk_wallet_core.wasm -o "dusk-wallet-core-0.21.0.wasm" -cp ./dusk-wallet-core-0.21.0.wasm ../../Web/dusk-wallet-js/assets diff --git a/dusk-wallet-core-0.21.0.wasm b/dusk-wallet-core-0.21.0.wasm deleted file mode 100644 index 5ac33bc..0000000 Binary files a/dusk-wallet-core-0.21.0.wasm and /dev/null differ diff --git a/src/compat/allow.rs b/src/compat/allow.rs deleted file mode 100644 index b3cc3bf..0000000 --- a/src/compat/allow.rs +++ /dev/null @@ -1,108 +0,0 @@ -// 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 http://mozilla.org/MPL/2.0/. -// -// Copyright (c) DUSK NETWORK. All rights reserved. - -use crate::{key::*, types, utils, MAX_LEN}; - -use alloc::string::String; - -use bls12_381_bls::PublicKey as StakePublicKey; -use dusk_jubjub::JubJubScalar; -use ff::Field; -use phoenix_core::{ - transaction::{allow_signature_message, Allow}, - Crossover, Fee, Note, PublicKey, -}; - -/// Get unstake call data -#[no_mangle] -pub fn get_allow_call_data(args: i32, len: i32) -> i64 { - let types::GetAllowCallDataArgs { - seed, - rng_seed, - sender_index, - refund, - owner_index, - counter, - gas_limit, - gas_price, - } = match utils::take_args(args, len) { - Some(a) => a, - None => return utils::fail(), - }; - - let rng_seed = match utils::sanitize_rng_seed(rng_seed) { - Some(s) => s, - None => return utils::fail(), - }; - - let seed = match utils::sanitize_seed(seed) { - Some(s) => s, - None => return utils::fail(), - }; - - let refund: PublicKey = match utils::bs58_to_pk(&refund) { - Some(a) => a, - None => return utils::fail(), - }; - - let stake_sk = derive_stake_sk(&seed, owner_index); - let staker_pk = StakePublicKey::from(&stake_sk); - - let owner_sk = derive_stake_sk(&seed, sender_index); - let owner_pk = StakePublicKey::from(&owner_sk); - - let rng = &mut utils::rng(rng_seed); - - let msg = allow_signature_message(counter, staker_pk); - let signature = owner_sk.sign(&owner_pk, &msg); - - let blinder = JubJubScalar::random(&mut *rng); - let note = Note::obfuscated(rng, &refund, 0, blinder); - let (mut fee, crossover) = note - .try_into() - .expect("Obfuscated notes should always yield crossovers"); - - fee.gas_limit = gas_limit; - fee.gas_price = gas_price; - - let allow = Allow { - public_key: staker_pk, - owner: owner_pk, - signature, - }; - - let contract = bs58::encode(rusk_abi::STAKE_CONTRACT).into_string(); - let method = String::from("allow"); - let payload = match rkyv::to_bytes::<_, MAX_LEN>(&allow).ok() { - Some(a) => a.to_vec(), - None => return utils::fail(), - }; - - let crossover = match rkyv::to_bytes::(&crossover) { - Ok(a) => a.to_vec(), - Err(_) => return utils::fail(), - }; - - let blinder = match rkyv::to_bytes::(&blinder) { - Ok(a) => a.to_vec(), - Err(_) => return utils::fail(), - }; - - let fee = match rkyv::to_bytes::(&fee) { - Ok(a) => a.to_vec(), - Err(_) => return utils::fail(), - }; - - // reusing this type - utils::into_ptr(types::GetAllowCallDataResponse { - contract, - method, - payload, - blinder, - crossover, - fee, - }) -} diff --git a/src/compat/crypto.rs b/src/compat/crypto.rs index 7c2595e..26f7f32 100644 --- a/src/compat/crypto.rs +++ b/src/compat/crypto.rs @@ -4,12 +4,18 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. +use core::mem::size_of; + use dusk_bls12_381::BlsScalar; use dusk_bytes::Serializable; -use phoenix_core::{Note, PublicKey, ViewKey}; +use phoenix_core::{ + transaction::{ArchivedTreeLeaf, TreeLeaf}, + Note, PublicKey, +}; -use alloc::vec::Vec; +use alloc::{string::ToString, vec::Vec}; +use crate::alloc::borrow::ToOwned; use crate::{ key::{self}, types::{self}, @@ -17,60 +23,85 @@ use crate::{ MAX_KEY, MAX_LEN, }; +const TREE_LEAF_SIZE: usize = size_of::(); + /// Returns true or false if the note is owned by the index /// if its true then nullifier of that note if sent with it #[no_mangle] pub fn check_note_ownership(args: i32, len: i32) -> i64 { - // we just use BalanceArgs again as we don't want to add more cluter types - // when the data you want is the same - let types::CheckNoteOwnershipArgs { note, seed } = - match utils::take_args(args, len) { - Some(a) => a, - None => return utils::fail(), - }; + let args = utils::take_args_raw(args, len); + + let seed = &args[..64]; + let leaves: &[u8] = &args[64..]; - let seed = match utils::sanitize_seed(seed) { + let seed = match seed.try_into().ok() { Some(s) => s, None => return utils::fail(), }; - let note: Note = match rkyv::from_bytes(¬e) { - Ok(n) => n, - Err(_) => return utils::fail(), - }; - - let mut is_owned: bool = false; - let mut nullifier_found = BlsScalar::default(); - let mut pk_found: Option = None; + let mut leaf_chunk = leaves.chunks_exact(TREE_LEAF_SIZE); + let mut last_pos = 0; - for idx in 0..=MAX_KEY { - let idx = idx as u64; - let sk = key::derive_sk(&seed, idx); - let vk = ViewKey::from(&sk); + let mut notes = Vec::new(); + let mut nullifiers = Vec::new(); + let mut block_heights = Vec::new(); + let mut public_spend_keys = Vec::new(); - if vk.owns(¬e) { - let nullifier = note.gen_nullifier(&sk); - - nullifier_found = nullifier; - is_owned = true; - pk_found = Some(PublicKey::from(&sk)); + for leaf_bytes in leaf_chunk.by_ref() { + let TreeLeaf { block_height, note } = + match rkyv::from_bytes(leaf_bytes).ok() { + Some(a) => a, + None => { + return utils::fail(); + } + }; - break; + last_pos = core::cmp::max(last_pos, *note.pos()); + + for idx in 0..=MAX_KEY { + let idx = idx as u64; + let view_key = key::derive_vk(&seed, idx); + + if view_key.owns(¬e) { + let sk = key::derive_sk(&seed, idx); + let nullifier = note.gen_nullifier(&sk); + + let nullifier_found = + match rkyv::to_bytes::(&nullifier).ok() + { + Some(n) => n.to_vec(), + None => return utils::fail(), + }; + + let psk_found = + bs58::encode(PublicKey::from(sk).to_bytes()).into_string(); + + let raw_note: Vec = + match rkyv::to_bytes::(¬e).ok() { + Some(n) => n.to_vec(), + None => return utils::fail(), + }; + + notes.push(raw_note.to_owned()); + block_heights.push(block_height); + public_spend_keys.push(psk_found); + nullifiers.push(nullifier_found); + } } } - let pk_found = pk_found.map(|pk| bs58::encode(pk.to_bytes()).into_string()); - - let nullifier_found = - match rkyv::to_bytes::(&nullifier_found).ok() { - Some(n) => n.to_vec(), - None => return utils::fail(), - }; + let block_heights = block_heights + .iter() + .map(|x| x.to_string()) + .collect::>() + .join(","); utils::into_ptr(types::CheckNoteOwnershipResponse { - is_owned, - nullifier: nullifier_found, - public_key: pk_found, + notes, + block_heights, + public_spend_keys, + nullifiers, + last_pos, }) } diff --git a/src/compat/mod.rs b/src/compat/mod.rs index fea6eb1..a03ff5a 100644 --- a/src/compat/mod.rs +++ b/src/compat/mod.rs @@ -4,8 +4,6 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -/// Includes functions to interact with the stake contract allow tx -pub mod allow; /// Helping us with the crypto primitives pub mod crypto; /// Includes methods to deal with bip39::Mnemonic diff --git a/src/compat/rkyv.rs b/src/compat/rkyv.rs index 4abdb72..aeca6e2 100644 --- a/src/compat/rkyv.rs +++ b/src/compat/rkyv.rs @@ -13,7 +13,7 @@ use crate::{ use bls12_381_bls::PublicKey as StakePublicKey; use dusk_bls12_381::BlsScalar; -use phoenix_core::{transaction::TreeLeaf, Note}; +use phoenix_core::Note; use alloc::vec::Vec; @@ -28,33 +28,6 @@ pub fn rkyv_u64(args: i32, len: i32) -> i64 { utils::rkyv_into_ptr(value) } -/// Get block_heignt a rkyv serialized note from a tree leaf -#[no_mangle] -pub fn rkyv_tree_leaf(args: i32, len: i32) -> i64 { - let types::RkyvTreeLeaf { bytes } = match utils::take_args(args, len) { - Some(a) => a, - None => return utils::fail(), - }; - - let TreeLeaf { block_height, note } = match rkyv::from_bytes(&bytes) { - Ok(n) => n, - Err(_) => return utils::fail(), - }; - - let last_pos = *note.pos(); - - let note = match rkyv::to_bytes::<_, MAX_LEN>(¬e).ok() { - Some(t) => t.into_vec(), - None => return utils::fail(), - }; - - utils::into_ptr(types::RkyvTreeLeafResponse { - block_height, - note, - last_pos, - }) -} - /// Convert a Vec (where note is a U8initArray into a rkyv serialized /// Vec #[no_mangle] @@ -97,12 +70,6 @@ pub fn rkyv_bls_scalar_array(args: i32, len: i32) -> i64 { } } - let bls_scalars = - match rkyv::to_bytes::, MAX_LEN>(&bls_scalars).ok() { - Some(v) => v.to_vec(), - None => return utils::fail(), - }; - utils::rkyv_into_ptr(bls_scalars) } diff --git a/src/lib.rs b/src/lib.rs index 8abcfae..6969931 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,7 @@ pub mod types; pub mod utils; /// The maximum number of keys (inclusive) to derive when attempting to decrypt /// a note. -pub const MAX_KEY: usize = 24; +pub const MAX_KEY: usize = 8; /// The maximum allocated buffer for rkyv serialization. pub const MAX_LEN: usize = rusk_abi::ARGBUF_LEN; diff --git a/src/types.rs b/src/types.rs index efff049..ad61b70 100644 --- a/src/types.rs +++ b/src/types.rs @@ -31,24 +31,19 @@ pub struct BalanceResponse { #[doc = " Total computed balance"] pub value: u64, } -#[doc = " Arguments of the check_note_ownership function"] -#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)] -pub struct CheckNoteOwnershipArgs { - #[doc = " A singular note we want to check the validity of"] - pub note: Vec, - #[doc = " The seed to generate the view keys from"] - pub seed: Vec, -} #[doc = " Response of check_note_ownership function"] #[derive(Clone, PartialEq, Debug, Deserialize, Serialize)] pub struct CheckNoteOwnershipResponse { - #[doc = " Is the note owned by any of the view keys in the provided seed"] - pub is_owned: bool, - #[doc = " Nullifier of the note that we were checking the ownership of"] - pub nullifier: Vec, - #[doc = " A base 58 encoded public key string"] - #[serde(skip_serializing_if = "Option::is_none")] - pub public_key: Option, + #[doc = " The block heights of the notes in the same order the notes were returned seperated by comma"] + pub block_heights: String, + #[doc = " The last position of the note"] + pub last_pos: u64, + #[doc = " The raw owned note"] + pub notes: Vec>, + #[doc = " The nullifiers of the notes in the same order the notes were returned"] + pub nullifiers: Vec>, + #[doc = " The public spend keys of the notes in the same order the notes were returned"] + pub public_spend_keys: Vec, } #[doc = " The value of the Crossover and the blinder"] #[derive(Clone, PartialEq, Debug, Deserialize, Serialize)] @@ -453,15 +448,13 @@ pub struct RkyvTreeLeaf { #[doc = " Bytes that are rkyv serialized into a phoenix_core::transaction::TreeLeaf"] pub bytes: Vec, } -#[doc = " The response of the public_keys function"] +#[doc = " The arguments of the rkyv tree leaf function"] #[derive(Clone, PartialEq, Debug, Deserialize, Serialize)] -pub struct RkyvTreeLeafResponse { - #[doc = " The block height of the note."] - pub block_height: u64, - #[doc = " Last position of the note"] - pub last_pos: u64, - #[doc = " Bytes of note at the block_height"] - pub note: Vec, +pub struct RkyvTreeLeafArgs { + #[doc = " Bytes that are rkyv serialized into a phoenix_core::transaction::TreeLeaf"] + pub bytes: Vec, + #[doc = " Seed used to derive the keys of the wallet"] + pub seed: Vec, } #[doc = " A serialized u64 using rkyv"] #[derive(Clone, PartialEq, Debug, Deserialize, Serialize)] diff --git a/src/utils.rs b/src/utils.rs index bbccded..77405c1 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -63,6 +63,14 @@ where serde_json::from_str(&args).ok() } +/// reads the raw bytes at the pointer for the length and returns what it reason +pub fn take_args_raw<'a>(args: i32, len: i32) -> &'a [u8] { + let args = args as *mut u8; + let len = len as usize; + + unsafe { core::slice::from_raw_parts(args, len) } +} + /// Sanitizes arbitrary bytes into well-formed seed. pub fn sanitize_seed(bytes: Vec) -> Option<[u8; RNG_SEED]> { (bytes.len() == RNG_SEED).then(|| {