Skip to content

Commit

Permalink
integrate with wallet-cli tests
Browse files Browse the repository at this point in the history
  • Loading branch information
artifex11 committed Aug 26, 2023
1 parent 8a647da commit ce2703b
Show file tree
Hide file tree
Showing 9 changed files with 92 additions and 91 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "dusk-wallet-core"
version = "0.20.0-piecrust.0.6"
version = "0.21.0"
edition = "2021"
description = "The core functionality of the Dusk wallet"
license = "MPL-2.0"
Expand All @@ -20,7 +20,7 @@ phoenix-core = { version = "0.20.0-rc.0", default-features = false, features = [
poseidon-merkle = { version = "0.2.1-rc.0", features = ["rkyv-impl"] }
rand_chacha = { version = "^0.3", default-features = false }
rand_core = "^0.6"
rkyv = { version = "^0.7", default-features = false }
rkyv = { version = "^0.7", default-features = false, features = ["size_32"] }
serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] }
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
sha2 = { version = "^0.10", default-features = false }
Expand Down
41 changes: 32 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,46 @@
[![codecov](https://codecov.io/gh/dusk-network/wallet-core/branch/main/graph/badge.svg?token=9W3J09AWZG)](https://codecov.io/gh/dusk-network/wallet-core)
[![documentation](https://img.shields.io/badge/docs-wallet-blue?logo=rust)](https://docs.rs/dusk-wallet-core/)

A library for generating and dealing with transactions.
A WASM library to provide business logic for Dusk wallet implementations.

Check the available methods under the [FFI](src/ffi.rs) module.

Every function expects a fat pointer to its arguments already allocated to the WASM memory. For the arguments definition, check the [JSON Schema](assets/schema.json). It will consume this pointer region and free it after execution. The return of the function will also be in accordance to the schema, and the user will have to free the memory himself after fetching the data.

For maximum compatibility, every WASM function returns a `i64` with the status of the operation and an embedded pointer. The structure of the bytes in big-endian is as follows:

[(pointer) x 4bytes (length) x 3bytes (status) x 1bit]

The pointer will be a maximum `u32` number, and the length a `u24` number. The status of the operation is the least significant bit of the number, and will be `0` if the operation is successful.

Here is an algorithm to split the result into meaningful parts:

```rust
let ptr = (result >> 32) as u64;
let len = ((result << 32) >> 48) as u64;
let success = ((result << 63) >> 63) == 0;
```

For an example usage, check the [wallet-cli](https://github.com/dusk-network/wallet-cli) implementation that consumes this library.

## Requirements

To generate packages, we use [binaryen](https://github.com/WebAssembly/binaryen).
- [Rust 1.71.0](https://www.rust-lang.org/)
- [target.wasm32-unknown-unknown](https://github.com/rustwasm/)
- [binaryen](https://github.com/WebAssembly/binaryen) to generate packages

## Build

To build and test the crate:
To build a distributable package:

```shell
cargo b
cargo t --all-features
```sh
make package
```

To build the WASM module:
## Test

To run the tests, there is an automated Makefile script

```shell
cargo b --release --target wasm32-unknown-unknown
```sh
make test
```
28 changes: 14 additions & 14 deletions assets/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@
"format": "uint8",
"minimum": 0
},
"maxItems": 32,
"minItems": 32
"maxItems": 64,
"minItems": 64
}
}
},
Expand Down Expand Up @@ -161,8 +161,8 @@
"format": "uint8",
"minimum": 0
},
"maxItems": 32,
"minItems": 32
"maxItems": 64,
"minItems": 64
}
}
},
Expand Down Expand Up @@ -235,8 +235,8 @@
"format": "uint8",
"minimum": 0
},
"maxItems": 32,
"minItems": 32
"maxItems": 64,
"minItems": 64
},
"seed": {
"description": "Seed used to derive the keys of the wallet",
Expand All @@ -246,8 +246,8 @@
"format": "uint8",
"minimum": 0
},
"maxItems": 32,
"minItems": 32
"maxItems": 64,
"minItems": 64
}
}
},
Expand Down Expand Up @@ -316,7 +316,7 @@
}
},
"notes": {
"description": "Rkyv serialized notes to be filtered",
"description": "A rkyv serialized [Vec<phoenix_core::Note>] to be filtered",
"type": "array",
"items": {
"type": "integer",
Expand All @@ -341,8 +341,8 @@
"format": "uint8",
"minimum": 0
},
"maxItems": 32,
"minItems": 32
"maxItems": 64,
"minItems": 64
}
}
},
Expand All @@ -355,7 +355,7 @@
],
"properties": {
"notes": {
"description": "Rkyv serialized notes to have nullifiers generated",
"description": "A rkyv serialized [Vec<phoenix_core::Note>] to have nullifiers generated",
"type": "array",
"items": {
"type": "integer",
Expand All @@ -371,8 +371,8 @@
"format": "uint8",
"minimum": 0
},
"maxItems": 32,
"minItems": 32
"maxItems": 64,
"minItems": 64
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion rust-toolchain
Original file line number Diff line number Diff line change
@@ -1 +1 @@
nightly-2023-01-05
nightly-2023-05-22
80 changes: 20 additions & 60 deletions src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ use alloc::{vec, vec::Vec};
use core::mem;

use dusk_bytes::Serializable;
use phoenix_core::note::{ArchivedNote, Note};
use rkyv::validation::validators::FromBytesError;
use phoenix_core::Note;
use sha2::{Digest, Sha512};

use crate::{key, tx, types, utils, MAX_KEY, MAX_LEN};
Expand Down Expand Up @@ -156,7 +155,7 @@ pub fn balance(args: i32, len: i32) -> i64 {
/// [types::ExecuteArgs].
///
/// Will return a triplet (status, ptr, len) pointing to JSON string
/// representing [types::ExecuteResult].
/// representing [types::ExecuteResponse].
#[no_mangle]
pub fn execute(args: i32, len: i32) -> i64 {
let types::ExecuteArgs {
Expand Down Expand Up @@ -196,8 +195,10 @@ pub fn execute(args: i32, len: i32) -> i64 {
};

let value = output.as_ref().map(|o| o.value).unwrap_or(0);
let total_output =
gas_limit.saturating_mul(gas_price).saturating_add(value);
let total_output = gas_limit
.saturating_mul(gas_price)
.saturating_add(value)
.saturating_add(crossover.unwrap_or_default());

let mut keys = unsafe { [mem::zeroed(); MAX_KEY + 1] };
let mut keys_ssk = unsafe { [mem::zeroed(); MAX_KEY + 1] };
Expand Down Expand Up @@ -297,33 +298,19 @@ pub fn merge_notes(args: i32, len: i32) -> i64 {
None => return utils::fail(),
};

let len =
3usize.saturating_mul(notes.len()) / mem::size_of::<ArchivedNote>() / 2;

let notes = match notes
.into_iter()
.map(|n| rkyv::from_bytes::<Vec<Note>>(&n))
.try_fold::<_, _, Result<_, FromBytesError<Vec<Note>>>>(
Vec::with_capacity(len),
|mut set, notes| {
set.extend(notes?);
Ok(utils::sanitize_notes(set))
},
) {
Ok(n) => n,
Err(_) => return utils::fail(),
};

let notes = match rkyv::to_bytes::<_, MAX_LEN>(&notes) {
Ok(n) => n.into_vec(),
Err(_) => return utils::fail(),
};
let mut list = Vec::with_capacity(10);
for notes in notes {
if !notes.is_empty() {
match rkyv::from_bytes::<Vec<Note>>(&notes) {
Ok(n) => list.extend(n),
Err(_) => return utils::fail(),
};
}
}

let ptr = notes.as_ptr() as u32;
let len = notes.len() as u32;
let notes = utils::sanitize_notes(list);

mem::forget(notes);
utils::compose(true, ptr, len)
utils::rkyv_into_ptr(notes)
}

/// Filters a list of notes from a list of negative flags. The flags that are
Expand Down Expand Up @@ -354,16 +341,7 @@ pub fn filter_notes(args: i32, len: i32) -> i64 {
.collect();

let notes = utils::sanitize_notes(notes);
let notes = match rkyv::to_bytes::<_, MAX_LEN>(&notes) {
Ok(n) => n.into_vec(),
Err(_) => return utils::fail(),
};

let ptr = notes.as_ptr() as u32;
let len = notes.len() as u32;

mem::forget(notes);
utils::compose(true, ptr, len)
utils::rkyv_into_ptr(notes)
}

/// Returns a list of [ViewKey] that belongs to this wallet.
Expand All @@ -389,16 +367,7 @@ pub fn view_keys(args: i32, len: i32) -> i64 {
.map(|idx| key::derive_vk(&seed, idx as u64))
.collect();

let vks = match rkyv::to_bytes::<_, MAX_LEN>(&vks) {
Ok(k) => k.into_vec(),
Err(_) => return utils::fail(),
};

let ptr = vks.as_ptr() as u32;
let len = vks.len() as u32;

mem::forget(vks);
utils::compose(true, ptr, len)
utils::rkyv_into_ptr(vks)
}

/// Returns a list of [BlsScalar] nullifiers for the given [Vec<Note>] combined
Expand Down Expand Up @@ -451,14 +420,5 @@ pub fn nullifiers(args: i32, len: i32) -> i64 {
return utils::fail();
}

let nullifiers = match rkyv::to_bytes::<_, MAX_LEN>(&nullifiers) {
Ok(n) => n.into_vec(),
Err(_) => return utils::fail(),
};

let ptr = nullifiers.as_ptr() as u32;
let len = nullifiers.len() as u32;

mem::forget(nullifiers);
utils::compose(true, ptr, len)
utils::rkyv_into_ptr(nullifiers)
}
2 changes: 1 addition & 1 deletion src/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ impl UnprovenTransaction {
r#type, &r, nonce, &receiver, value, blinder,
);

output_notes.push(note.clone());
output_notes.push(note);
outputs_values.push(Output {
note,
value,
Expand Down
4 changes: 2 additions & 2 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ pub struct ExecuteResponse {
pub struct FilterNotesArgs {
#[doc = " Boolean flags to be negative filtered"]
pub flags: Vec<bool>,
#[doc = " Rkyv serialized notes to be filtered"]
#[doc = " A rkyv serialized [Vec<phoenix_core::Note>] to be filtered"]
pub notes: Vec<u8>,
}
#[doc = " The arguments of the merge_notes function"]
Expand All @@ -105,7 +105,7 @@ pub struct MergeNotesArgs {
#[doc = " The arguments of the nullifiers function"]
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
pub struct NullifiersArgs {
#[doc = " Rkyv serialized notes to have nullifiers generated"]
#[doc = " A rkyv serialized [Vec<phoenix_core::Note>] to have nullifiers generated"]
pub notes: Vec<u8>,
#[doc = " Seed used to derive the keys of the wallet"]
pub seed: Vec<u8>,
Expand Down
19 changes: 18 additions & 1 deletion src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

//! Misc utilities required by the library implementation.

use crate::{tx, RNG_SEED};
use crate::{tx, MAX_LEN, RNG_SEED};

use alloc::vec::Vec;
use core::mem;
Expand Down Expand Up @@ -84,6 +84,23 @@ where
result
}

/// Returns the provided bytes as a pointer
pub fn rkyv_into_ptr<T>(value: T) -> i64
where
T: rkyv::Serialize<rkyv::ser::serializers::AllocSerializer<MAX_LEN>>,
{
let bytes = match rkyv::to_bytes(&value) {
Ok(t) => t.into_vec(),
Err(_) => return fail(),
};

let ptr = bytes.as_ptr() as u32;
let len = bytes.len() as u32;

mem::forget(bytes);
compose(true, ptr, len)
}

/// Creates a secure RNG from a seed.
pub fn rng(seed: &[u8; RNG_SEED]) -> ChaCha12Rng {
let mut hash = Sha256::new();
Expand Down
3 changes: 2 additions & 1 deletion tests/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ fn merge_notes_works() {
let notes1 = rkyv::to_bytes::<_, MAX_LEN>(&notes1).unwrap().into_vec();
let notes2 = rkyv::to_bytes::<_, MAX_LEN>(&notes2).unwrap().into_vec();
let notes3 = rkyv::to_bytes::<_, MAX_LEN>(&notes3).unwrap().into_vec();
let notes = vec![notes1, notes2, notes3];
let notes4 = vec![];
let notes = vec![notes1, notes2, notes3, notes4];

let mut wallet = Wallet::default();

Expand Down

0 comments on commit ce2703b

Please sign in to comment.