Skip to content

Commit

Permalink
Merge pull request #30 from LedgerHQ/client-sdk
Browse files Browse the repository at this point in the history
Created initial code for the `vanadium_client_sdk`; deleted the `host` crate.
  • Loading branch information
bigspider authored Oct 11, 2024
2 parents d3d8b8b + 3ea158e commit f9d702b
Show file tree
Hide file tree
Showing 27 changed files with 1,448 additions and 726 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ jobs:
target: x86_64-unknown-linux-gnu
components: rustfmt, clippy
profile: minimal
- name: Install libudev-dev and pkg-config
run: |
sudo apt-get update && sudo apt-get install -y libudev-dev pkg-config
- name: Clone
uses: actions/checkout@v4
- name: Unit tests
Expand Down Expand Up @@ -97,6 +100,9 @@ jobs:
target: x86_64-unknown-linux-gnu
components: rustfmt, clippy
profile: minimal
- name: Install libudev-dev and pkg-config
run: |
sudo apt-get update && sudo apt-get install -y libudev-dev pkg-config
- name: Clone
uses: actions/checkout@v4
- name: Unit tests
Expand Down
61 changes: 59 additions & 2 deletions apps/test/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,61 @@
This will be a test V-app.
This is a test V-App, with a few simple computations and functionalities to test various aspects of the Vanadium VM.

- [app](app) contains the Risc-V app, based on the V-app Sdk.
- [client](client) folder contains the client of the app, based on the V-app Client Sdk.
- [client](client) folder contains the client of the app, based on the V-app Client Sdk.

The `client` is a library crate (see [lib.rs](client/src/lib.rs)), but it also has a test executable ([main.rs](client/src/main.rs)) to interact with the app from the command line.

## Build the V-App

### Risc-V

In order to build the app for the Risc-V target, enter the `app` folder and run:

```sh
cargo build --release --target=riscv32i-unknown-none-elf
```

### Native

In order to build the app for the native target, enter the `app` folder and run:

```sh
cargo build --release --target=x86_64-unknown-linux-gnu
```

## Run the V-App

Make sure you built the V-App for the Risc-V target.

Launch Vanadium on speculos. Then execute:

From the `client` folder

```sh
cargo run
```

If you want to run the V-app on a real device, execute instead:

```sh
cargo run -- --hid
```

If you want to run the V-app natively, after building it for the native target, use:

```sh
cargo run -- --native
```


### Client commands

Once the client is running, these are the available commands:

- `reverse <hex_buffer>` - Reverses the given buffer.
- `sha256 <hex_buffer>` - Computes the sha256 hash of the given buffer.
- `b58enc <hex_buffer>` - Computes the base58 encoding of the given buffer (the output is in hex as well).
- `addnumbers <n>` - Computes the sum of the numbers between `1` and `n`.
- `nprimes <n>` - Counts the number of primes up to `n` using the Sieve of Eratosthenes.
- `panic <panic message>` - Cause the V-App to panic. Everything written after 'panic' is the panic message.
- An empty command will exit the V-App.
2 changes: 2 additions & 0 deletions apps/test/app/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ version = "0.1.0"
edition = "2021"

[dependencies]
bs58 = { version = "0.5.1", default-features = false, features = ["alloc"] }
sdk = { package = "vanadium-app-sdk", path = "../../../app-sdk"}
sha2 = { version = "0.10.8", default-features = false }

[workspace]
28 changes: 28 additions & 0 deletions apps/test/app/src/commands.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Duplicated for simplicity with the corresponding module in the client crate.
// Make sure to keep them in sync.

#[derive(Debug)]
pub enum Command {
Reverse,
AddNumbers,
Base58Encode,
Sha256,
CountPrimes,
Panic = 0xff,
}

impl TryFrom<u8> for Command {
type Error = ();

fn try_from(byte: u8) -> Result<Self, Self::Error> {
match byte {
0x00 => Ok(Command::Reverse),
0x01 => Ok(Command::AddNumbers),
0x02 => Ok(Command::Base58Encode),
0x03 => Ok(Command::Sha256),
0x04 => Ok(Command::CountPrimes),
0xff => Ok(Command::Panic),
_ => Err(()),
}
}
}
5 changes: 5 additions & 0 deletions apps/test/app/src/handlers/base58.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use alloc::vec::Vec;

pub fn handle_base58_encode(data: &[u8]) -> Vec<u8> {
bs58::encode(data).into_vec()
}
54 changes: 54 additions & 0 deletions apps/test/app/src/handlers/count_primes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use alloc::{vec, vec::Vec};

fn count_primes(n: u32) -> u32 {
if n < 2 {
return 0;
}

let mut is_prime = vec![true; (n + 1) as usize];
is_prime[0] = false;
is_prime[1] = false;

let mut i = 2;
let mut square = 4;
loop {
if square > n {
break;
}
if is_prime[i as usize] {
let mut multiple = i * i;
while multiple <= n {
is_prime[multiple as usize] = false;
multiple += i;
}
}
square += 2 * i + 1;
i += 1;
}

// Count the primes
is_prime.iter().filter(|&&p| p).count() as u32
}

pub fn handle_count_primes(data: &[u8]) -> Vec<u8> {
if data.len() != 4 {
return vec![];
}
let n = u32::from_be_bytes(data[0..4].try_into().unwrap());
let count = count_primes(n);
count.to_be_bytes().to_vec()
}

#[cfg(test)]
mod tests {
#[test]
fn test_count_primes() {
assert_eq!(super::count_primes(0), 0);
assert_eq!(super::count_primes(1), 0);
assert_eq!(super::count_primes(2), 1);
assert_eq!(super::count_primes(3), 2);
assert_eq!(super::count_primes(4), 2);
assert_eq!(super::count_primes(5), 3);
assert_eq!(super::count_primes(10000), 1229);
}
}
7 changes: 7 additions & 0 deletions apps/test/app/src/handlers/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
mod base58;
mod count_primes;
mod sha256;

pub use base58::handle_base58_encode;
pub use count_primes::handle_count_primes;
pub use sha256::handle_sha256;
6 changes: 6 additions & 0 deletions apps/test/app/src/handlers/sha256.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use alloc::vec::Vec;
use sha2::Digest;

pub fn handle_sha256(data: &[u8]) -> Vec<u8> {
sha2::Sha256::digest(data).to_vec()
}
66 changes: 42 additions & 24 deletions apps/test/app/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
#![feature(start)]
#![cfg_attr(target_arch = "riscv32", no_std, no_main)]

use core::ptr::addr_of;

#[cfg(target_arch = "riscv32")]
use sdk::fatal;

extern crate alloc;

mod commands;
mod handlers;

use commands::Command;
use handlers::*;

use alloc::vec;

// Temporary to force the creation of a data section
Expand Down Expand Up @@ -41,7 +49,7 @@ pub fn main(_: isize, _: *const *const u8) -> isize {

// TODO: remove
unsafe {
core::ptr::read_volatile(&APP_NAME);
core::ptr::read_volatile(addr_of!(APP_NAME));
}

// TODO: remove
Expand All @@ -58,31 +66,41 @@ pub fn main(_: isize, _: *const *const u8) -> isize {

// sdk::ux::app_loading_start("Handling request...\x00");

if msg.len() == 0 {
if msg.is_empty() {
sdk::exit(0);
}
if msg.len() == 1 {
panic!("Oh no, how can I reverse a single byte?");
}

// reverse the message
let mut reversed = msg.clone();
reversed.reverse();
sdk::xsend(&reversed);

// let result = handle_req(&buffer, &mut state);

// sdk::ux::app_loading_stop();
// sdk::ux::ux_idle();

// comm::send_message(&result).unwrap(); // TODO: what to do on error?
}
}

#[cfg(test)]
mod tests {
#[test]
fn test_placeholder() {
assert_eq!(1 + 1, 2);
let Ok(command) = Command::try_from(msg[0]) else {
panic!("Unknown command");
};

let response = match command {
Command::Reverse => {
let mut data = msg[1..].to_vec();
data.reverse();
data
}
Command::AddNumbers => {
// sum all the numbers from 0 to n
if msg.len() != 5 {
panic!("Invalid input");
}
let n = u32::from_be_bytes([msg[1], msg[2], msg[3], msg[4]]);
let mut result: u64 = 0;
for i in 0..=n {
result += i as u64;
}
result.to_be_bytes().to_vec()
}
Command::Base58Encode => handle_base58_encode(&msg[1..]),
Command::Sha256 => handle_sha256(&msg[1..]),
Command::CountPrimes => handle_count_primes(&msg[1..]),
Command::Panic => {
let panic_msg = core::str::from_utf8(&msg[1..]).unwrap();
panic!("{}", panic_msg);
}
};

sdk::xsend(&response);
}
}
14 changes: 14 additions & 0 deletions apps/test/client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@ version = "0.1.0"
edition = "2021"

[dependencies]
clap = { version = "4.5.17", features = ["derive"] }
hex = "0.4.3"
hidapi = "2.6.3"
ledger-transport-hid = "0.11.0"
sdk = { package = "vanadium-client-sdk", path = "../../../client-sdk"}
tokio = { version = "1.38.1", features = ["io-util", "macros", "net", "rt", "rt-multi-thread", "sync"] }

[lib]
name = "vnd_test_client"
path = "src/lib.rs"

[[bin]]
name = "vnd_test_cli"
path = "src/main.rs"


[workspace]
Loading

0 comments on commit f9d702b

Please sign in to comment.