diff --git a/assets/dusk_wallet_core.wasm b/assets/dusk_wallet_core.wasm index 187253c..a205f8b 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 7c418f1..1f04706 100644 --- a/assets/schema.json +++ b/assets/schema.json @@ -139,10 +139,45 @@ } } }, + "CrossoverType": { + "description": "The value of the Crossover and the blinder", + "type": "object", + "required": [ + "crossover", + "blinder", + "value" + ], + "properties": { + "crossover": { + "description": "The rkyv serialized bytes of the crossover struct", + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0 + } + }, + "blinder": { + "description": "The rkyv serialized blinder of the crossover", + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0 + } + }, + "value": { + "description": "The value of the crossover", + "type": "integer", + "format": "uint64" + } + } + }, "ExecuteArgs": { "description": "The arguments of the execute function", "type": "object", "required": [ + "fee", "gas_limit", "gas_price", "inputs", @@ -157,22 +192,27 @@ "$ref": "#/definitions/ExecuteCall" }, "crossover": { - "description": "The [phoenix_core::Crossover] value", - "type": "integer", - "format": "uint64", - "minimum": 0 + "description": "The crossover value", + "$ref": "#/definitions/CrossoverType" + }, + "fee": { + "description": "A rkyv serialized Fee", + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0 + } }, "gas_limit": { "description": "The gas limit of the transaction", "type": "integer", - "format": "uint64", - "minimum": 0 + "format": "uint64" }, "gas_price": { "description": "The gas price per unit for the transaction", "type": "integer", - "format": "uint64", - "minimum": 0 + "format": "uint64" }, "inputs": { "description": "A rkyv serialized [Vec] to be used as inputs", @@ -208,8 +248,8 @@ "format": "uint8", "minimum": 0 }, - "maxItems": 64, - "minItems": 64 + "maxItems": 32, + "minItems": 32 }, "seed": { "description": "Seed used to derive the keys of the wallet", diff --git a/src/ffi.rs b/src/ffi.rs index 2f8821a..82a5084 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -10,7 +10,7 @@ use alloc::{vec, vec::Vec}; use core::mem; use dusk_bytes::Serializable; -use phoenix_core::Note; +use phoenix_core::{Fee, Note}; use sha2::{Digest, Sha512}; use crate::{key, tx, types, utils, MAX_KEY, MAX_LEN}; @@ -132,11 +132,12 @@ pub fn execute(args: i32, len: i32) -> i64 { let types::ExecuteArgs { call, crossover, - gas_limit, - gas_price, + fee, inputs, openings, output, + gas_limit, + gas_price, refund, rng_seed, seed, @@ -150,6 +151,11 @@ pub fn execute(args: i32, len: i32) -> i64 { Err(_) => return utils::fail(), }; + let fee: Fee = match rkyv::from_bytes(&fee) { + Ok(n) => n, + Err(_) => return utils::fail(), + }; + let openings: Vec = match rkyv::from_bytes(&openings) { Ok(n) => n, Err(_) => return utils::fail(), @@ -160,7 +166,7 @@ pub fn execute(args: i32, len: i32) -> i64 { None => return utils::fail(), }; - let rng_seed = match utils::sanitize_seed(rng_seed) { + let rng_seed: [u8; 32] = match rng_seed.try_into().ok() { Some(s) => s, None => return utils::fail(), }; @@ -169,7 +175,7 @@ pub fn execute(args: i32, len: i32) -> i64 { let total_output = gas_limit .saturating_mul(gas_price) .saturating_add(value) - .saturating_add(crossover.unwrap_or_default()); + .saturating_add(crossover.clone().map(|c| c.value).unwrap_or_default()); let mut keys = unsafe { [mem::zeroed(); MAX_KEY + 1] }; let mut keys_ssk = unsafe { [mem::zeroed(); MAX_KEY + 1] }; @@ -221,9 +227,6 @@ pub fn execute(args: i32, len: i32) -> i64 { let total_refund = total_input.saturating_sub(total_output); let mut outputs = Vec::with_capacity(2); - if let Some(o) = output { - outputs.push(o); - } if total_refund > 0 { outputs.push(types::ExecuteOutput { note_type: types::OutputType::Obfuscated, @@ -232,10 +235,13 @@ pub fn execute(args: i32, len: i32) -> i64 { value: total_refund, }); } + if let Some(o) = output { + outputs.push(o); + } - let rng = &mut utils::rng(&rng_seed); + let rng = &mut utils::rng(rng_seed); let tx = tx::UnprovenTransaction::new( - rng, inputs, outputs, refund, gas_limit, gas_price, crossover, call, + rng, inputs, outputs, fee, crossover, call, ); let tx = match tx { Some(t) => t, @@ -307,7 +313,7 @@ pub fn filter_notes(args: i32, len: i32) -> i64 { let notes: Vec<_> = notes .into_iter() - .zip(flags.into_iter()) + .zip(flags) .filter_map(|(n, f)| (!f).then_some(n)) .collect(); diff --git a/src/tx.rs b/src/tx.rs index 008982f..4079adb 100644 --- a/src/tx.rs +++ b/src/tx.rs @@ -24,7 +24,7 @@ use rkyv::{Archive, Deserialize, Serialize}; use rusk_abi::hash::Hasher; use rusk_abi::{ContractId, POSEIDON_TREE_DEPTH}; -use crate::{types, utils}; +use crate::{types, types::CrossoverType, utils}; /// Chosen arity for the Notes tree implementation. pub const POSEIDON_TREE_ARITY: usize = 4; @@ -94,7 +94,7 @@ pub struct Output { /// A crossover to a transaction that is yet to be proven. #[derive(Debug, Clone, Archive, Serialize, Deserialize)] #[archive_attr(derive(CheckBytes))] -pub struct Crossover { +pub struct WasmCrossover { /// Crossover value to be used in inter-contract calls. pub crossover: PhoenixCrossover, /// Value of the crossover. @@ -128,7 +128,7 @@ pub struct UnprovenTransaction { /// Fee setup for the transaction. pub fee: Fee, /// Crossover value for inter-contract calls. - pub crossover: Option, + pub crossover: Option, /// Call data payload for contract calls. pub call: Option, } @@ -143,10 +143,8 @@ impl UnprovenTransaction { rng: &mut Rng, inputs: I, outputs: O, - refund: String, - gas_limit: u64, - gas_price: u64, - crossover: Option, + fee: Fee, + crossover: Option, call: Option, ) -> Option where @@ -163,7 +161,6 @@ impl UnprovenTransaction { .unzip(); let anchor = inputs.first().map(|i| i.opening.root().hash)?; - let refund = utils::bs58_to_psk(&refund)?; let mut output_notes = Vec::with_capacity(4); let mut outputs_values = Vec::with_capacity(4); @@ -222,20 +219,25 @@ impl UnprovenTransaction { (c.contract.to_bytes(), c.method.clone(), c.payload.clone()) }); - let fee = Fee::new(rng, gas_limit, gas_price, &refund); - - let crossover = crossover.map(|crossover| { - let blinder = JubJubScalar::random(rng); - let (_, crossover_note) = - Note::obfuscated(rng, &refund, crossover, blinder) - .try_into() - .expect("Obfuscated notes should always yield crossovers"); - Crossover { - crossover: crossover_note, - value: crossover, - blinder, - } - }); + let crossover = crossover.and_then( + |CrossoverType { + blinder, + crossover, + value, + }| { + Some({ + WasmCrossover { + crossover: rkyv::from_bytes::( + &crossover, + ) + .ok()?, + value, + blinder: rkyv::from_bytes::(&blinder) + .ok()?, + } + }) + }, + ); let tx_hash = Transaction::hash_input_bytes_from_components( &nullifiers, diff --git a/src/types.rs b/src/types.rs index f58ac56..62409ee 100644 --- a/src/types.rs +++ b/src/types.rs @@ -31,15 +31,27 @@ pub struct BalanceResponse { #[doc = " Total computed balance"] pub value: u64, } +#[doc = " The value of the Crossover and the blinder"] +#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)] +pub struct CrossoverType { + #[doc = " The rkyv serialized blinder of the crossover"] + pub blinder: Vec, + #[doc = " The rkyv serialized bytes of the crossover struct"] + pub crossover: Vec, + #[doc = " The value of the crossover"] + pub value: u64, +} #[doc = " The arguments of the execute function"] #[derive(Clone, PartialEq, Debug, Deserialize, Serialize)] pub struct ExecuteArgs { #[doc = " A call to a contract method"] #[serde(skip_serializing_if = "Option::is_none")] pub call: Option, - #[doc = " The [phoenix_core::Crossover] value"] + #[doc = " The crossover value"] #[serde(skip_serializing_if = "Option::is_none")] - pub crossover: Option, + pub crossover: Option, + #[doc = " A rkyv serialized Fee"] + pub fee: Vec, #[doc = " The gas limit of the transaction"] pub gas_limit: u64, #[doc = " The gas price per unit for the transaction"] diff --git a/src/utils.rs b/src/utils.rs index 8614b81..e83227c 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -101,15 +101,9 @@ where compose(true, ptr, len) } -/// Creates a secure RNG from a seed. -pub fn rng(seed: &[u8; RNG_SEED]) -> ChaCha12Rng { - let mut hash = Sha256::new(); - - hash.update(seed); - hash.update(b"RNG"); - - let hash = hash.finalize().into(); - ChaCha12Rng::from_seed(hash) +/// Creates a secure RNG directly a seed. +pub fn rng(seed: [u8; 32]) -> ChaCha12Rng { + ChaCha12Rng::from_seed(seed) } /// Creates a secure RNG from a seed with embedded index. diff --git a/tests/wallet.rs b/tests/wallet.rs index fa1611a..8f5d23d 100644 --- a/tests/wallet.rs +++ b/tests/wallet.rs @@ -7,8 +7,16 @@ //! Wallet library tests. use dusk_bytes::Serializable; +use dusk_jubjub::JubJubScalar; use dusk_pki::PublicSpendKey; -use dusk_wallet_core::{tx, types, utils, MAX_KEY, MAX_LEN, RNG_SEED}; +use dusk_wallet_core::{ + tx, + types::{self, CrossoverType as WasmCrossover}, + utils, MAX_KEY, MAX_LEN, RNG_SEED, +}; +use phoenix_core::{Crossover, Fee}; +use rand::rngs::StdRng; +use rand_core::SeedableRng; use rusk_abi::ContractId; use serde::{Deserialize, Serialize}; use serde_json::json; @@ -48,7 +56,7 @@ fn balance_works() { #[test] fn execute_works() { let seed = [0xfa; RNG_SEED]; - let rng_seed = [0xfb; RNG_SEED]; + let rng_seed = [0xfb; 32]; let values = [10, 250, 15, 7500]; let mut wallet = Wallet::default(); @@ -68,15 +76,38 @@ fn execute_works() { let contract = bs58::encode(contract.as_bytes()).into_string(); let (inputs, openings) = node::notes_and_openings(&seed, values); + let crossover = Crossover::default(); + let blinder = JubJubScalar::default(); + + let crossover = WasmCrossover { + blinder: rkyv::to_bytes::(&blinder) + .unwrap() + .to_vec(), + crossover: rkyv::to_bytes::(&crossover) + .unwrap() + .to_vec(), + value: 0, + }; + + let fee = Fee::new( + &mut StdRng::from_entropy(), + 240000, + 0, + &utils::bs58_to_psk(psk).unwrap(), + ); + + let fee = rkyv::to_bytes::(&fee).unwrap().to_vec(); + let args = json!({ "call": { "contract": contract, "method": "commit", "payload": b"We lost because we told ourselves we lost.".to_vec(), }, - "crossover": 25, + "crossover": crossover, "gas_limit": 100, "gas_price": 2, + "fee": fee, "inputs": inputs, "openings": openings, "output": { @@ -101,10 +132,10 @@ fn merge_notes_works() { let seed = [0xfa; RNG_SEED]; let notes1 = node::raw_notes(&seed, [10, 250, 15, 39, 55]); - let notes2 = vec![notes1[1].clone(), notes1[3].clone()]; + let notes2 = vec![notes1[1], notes1[3]]; let notes3: Vec<_> = node::raw_notes(&seed, [10, 250, 15, 39, 55]) .into_iter() - .chain([notes1[4].clone()]) + .chain([notes1[4]]) .collect(); let notes_unmerged: Vec<_> = notes1 @@ -143,7 +174,7 @@ fn filter_notes_works() { let notes = node::raw_notes(&seed, [10, 250, 15, 39, 55]); let flags = vec![true, true, false, true, false]; - let filtered = vec![notes[2].clone(), notes[4].clone()]; + let filtered = vec![notes[2], notes[4]]; let filtered = utils::sanitize_notes(filtered); let notes = rkyv::to_bytes::<_, MAX_LEN>(¬es).unwrap().into_vec(); @@ -236,15 +267,16 @@ mod node { use core::mem; use dusk_jubjub::{BlsScalar, JubJubScalar}; - use dusk_wallet_core::{key, tx, utils, MAX_KEY, MAX_LEN, RNG_SEED}; + use dusk_wallet_core::{key, tx, MAX_KEY, MAX_LEN, RNG_SEED}; use phoenix_core::Note; - use rand::RngCore; + use rand::{rngs::StdRng, RngCore}; + use rand_core::SeedableRng; pub fn raw_notes(seed: &[u8; RNG_SEED], values: Values) -> Vec where Values: IntoIterator, { - let rng = &mut utils::rng(seed); + let rng = &mut StdRng::from_entropy(); values .into_iter() .map(|value| { @@ -299,7 +331,7 @@ mod node { where Values: IntoIterator, { - let rng = &mut utils::rng(seed); + let rng = &mut StdRng::from_entropy(); values .into_iter() .map(|value| {