Skip to content

Commit

Permalink
Created initial module for the vanadium_client_sdk. Deleted the 'host…
Browse files Browse the repository at this point in the history
…' crate.

The 'host' crate was split among the 'vanadium_client_sdk' and the vnd-test-client executable crate.
  • Loading branch information
bigspider committed Sep 18, 2024
1 parent 677c26c commit 9303d3b
Show file tree
Hide file tree
Showing 12 changed files with 161 additions and 167 deletions.
5 changes: 5 additions & 0 deletions apps/test/client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ 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", "sync"] }

[workspace]
130 changes: 122 additions & 8 deletions apps/test/client/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,125 @@
fn main() {
println!("Hello, world!");
use clap::Parser;
use hidapi::HidApi;
use ledger_transport_hid::TransportNativeHID;

use sdk::{
elf::ElfFile,
manifest::Manifest,
transport::{Transport, TransportHID, TransportTcp, TransportWrapper},
vanadium_client::{Callbacks, ReceiveBufferError, VanadiumClient},
};

use std::io::{stdin, Write};
use std::sync::Arc;
use std::{io::stdout, path::Path};
#[derive(Parser)]
#[command(name = "Vanadium", about = "Run a V-App on Vanadium")]
struct Args {
/// Path to the ELF file of the V-App (if not the default one)
elf: Option<String>,

/// Use the HID interface for a real device, instead of Speculos
#[arg(long)]
hid: bool,
}

struct TestAppCallbacks;

impl Callbacks for TestAppCallbacks {
fn receive_buffer(&self) -> Result<Vec<u8>, ReceiveBufferError> {
// Prompt the user to input a data buffer in hex; send it to the V-App
let mut buffer = String::new();
let bytes = loop {
print!("Enter a data buffer in hexadecimal: ");
stdout().flush().unwrap();
buffer.clear();
stdin().read_line(&mut buffer).unwrap();
buffer = buffer.trim().to_string();

if let Ok(bytes) = hex::decode(&buffer) {
break bytes;
} else {
println!("Invalid hexadecimal input. Please try again.");
}
};

Ok(bytes)
}

fn send_buffer(&self, buffer: &[u8]) {
println!("Received buffer: {}", hex::encode(&buffer));
}

fn send_panic(&self, msg: &[u8]) {
println!(
"Received panic message:\n{}",
core::str::from_utf8(&msg).unwrap()
);
}
}

#[cfg(test)]
mod tests {
#[test]
fn test_placeholder() {
assert_eq!(1 + 1, 2);
}
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let args = Args::parse();

let default_elf_path = "../app/target/riscv32i-unknown-none-elf/release/vnd-test";
let elf_path_str = args.elf.unwrap_or(default_elf_path.to_string());
let elf_path = Path::new(&elf_path_str);
let elf_file = ElfFile::new(elf_path)?;

// println!("Entrypoint: {:?}", elf_file.entrypoint);
// println!("{:?}", elf_file);

let transport_raw: Arc<dyn Transport<Error = Box<dyn std::error::Error>> + Send + Sync> =
if args.hid {
Arc::new(TransportHID::new(
TransportNativeHID::new(
&HidApi::new().expect("Unable to get connect to the device"),
)
.unwrap(),
))
} else {
Arc::new(
TransportTcp::new()
.await
.expect("Unable to get TCP transport. Is speculos running?"),
)
};
let transport = TransportWrapper::new(transport_raw);

let manifest = Manifest::new(
0,
"Test",
"0.1.0",
[0u8; 32], // TODO
elf_file.entrypoint,
65536, // TODO
elf_file.code_segment.start,
elf_file.code_segment.end,
0xd47a2000 - 65536, // TODO
0xd47a2000, // TODO
elf_file.data_segment.start,
elf_file.data_segment.end,
[0u8; 32], // TODO
0, // TODO
)
.unwrap();

let callbacks = TestAppCallbacks;

let client = VanadiumClient::new(transport);
let app_hmac = client.register_vapp(&manifest).await?;

println!("HMAC: {:?}", app_hmac);

let result = client
.run_vapp(&manifest, &app_hmac, &elf_file, &callbacks)
.await?;
if result.len() != 4 {
return Err("The V-App exited, but did not return correctly return a status".into());
}
let status = i32::from_be_bytes([result[0], result[1], result[2], result[3]]);
println!("App exited with status: {}", status);

Ok(())
}
9 changes: 9 additions & 0 deletions client-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,12 @@ version = "0.1.0"
edition = "2021"

[dependencies]
async-trait = "0.1.81"
common = { path = "../common" }
goblin = "0.8.2"
hidapi = "2.6.3"
ledger-apdu = "0.11.0"
ledger-transport-hid = "0.11.0"
postcard = { version = "1.0.8", features = ["alloc"] }
sha2 = "0.10.8"
tokio = { version = "1.38.1", features = ["io-util", "macros", "net", "rt", "sync"] }
File renamed without changes.
File renamed without changes.
18 changes: 5 additions & 13 deletions client-sdk/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
pub fn add(left: u64, right: u64) -> u64 {
left + right
}
mod apdu;
pub mod elf;
pub mod transport;
pub mod vanadium_client;

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
pub use common::manifest;
2 changes: 0 additions & 2 deletions host/src/transport.rs → client-sdk/src/transport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,13 @@ use tokio::{

use crate::apdu::{APDUCommand, StatusWord};


/// Communication layer between the bitcoin client and the Ledger device.
#[async_trait]
pub trait Transport {
type Error: Debug;
async fn exchange(&self, command: &APDUCommand) -> Result<(StatusWord, Vec<u8>), Self::Error>;
}


/// Transport with the Ledger device.
pub struct TransportHID(TransportNativeHID);

Expand Down
39 changes: 20 additions & 19 deletions host/src/vanadium_client.rs → client-sdk/src/vanadium_client.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::cmp::min;
use std::io::{stdin, stdout, Write};

use common::accumulator::{HashOutput, Hasher, MerkleAccumulator, VectorAccumulator};
use common::client_commands::{
Expand All @@ -13,7 +12,7 @@ use sha2::{Digest, Sha256};

use crate::apdu::{APDUCommand, StatusWord};
use crate::elf::ElfFile;
use crate::Transport;
use crate::transport::Transport;

fn apdu_continue(data: Vec<u8>) -> APDUCommand {
APDUCommand {
Expand Down Expand Up @@ -124,6 +123,7 @@ struct VAppEngine<'a> {
code_seg: MemorySegment,
data_seg: MemorySegment,
stack_seg: MemorySegment,
callbacks: &'a dyn Callbacks,
}

impl<'a> VAppEngine<'a> {
Expand Down Expand Up @@ -266,7 +266,7 @@ impl<'a> VAppEngine<'a> {
remaining_len -= msg.data.len() as u32;
}

println!("Received buffer: {}", hex::encode(&buf));
self.callbacks.send_buffer(&buf);

Ok(self
.exchange_and_process_page_requests(transport, &apdu_continue(vec![]))
Expand All @@ -282,21 +282,10 @@ impl<'a> VAppEngine<'a> {
) -> Result<(StatusWord, Vec<u8>), &'static str> {
ReceiveBufferMessage::deserialize(command)?;

// Prompt the user to input a data buffer in hex; send it to the V-App
let mut buffer = String::new();
let bytes = loop {
print!("Enter a data buffer in hexadecimal: ");
stdout().flush().unwrap();
buffer.clear();
stdin().read_line(&mut buffer).unwrap();
buffer = buffer.trim().to_string();

if let Ok(bytes) = hex::decode(&buffer) {
break bytes;
} else {
println!("Invalid hexadecimal input. Please try again.");
}
};
let bytes = self
.callbacks
.receive_buffer()
.map_err(|_| "receive buffer callback failed")?;

let mut remaining_len = bytes.len() as u32;
let mut offset: usize = 0;
Expand Down Expand Up @@ -443,6 +432,16 @@ pub struct VanadiumClient<T: Transport> {
transport: T,
}

pub enum ReceiveBufferError {
ReceiveFailed, // TODO: do we need to distinguish between more errors?
}

pub trait Callbacks {
fn receive_buffer(&self) -> Result<Vec<u8>, ReceiveBufferError>;
fn send_buffer(&self, buffer: &[u8]);
fn send_panic(&self, msg: &[u8]);
}

impl<T: Transport> VanadiumClient<T> {
pub fn new(transport: T) -> Self {
Self { transport }
Expand Down Expand Up @@ -479,11 +478,12 @@ impl<T: Transport> VanadiumClient<T> {
}
}

pub async fn run_vapp(
pub async fn run_vapp<C: Callbacks>(
&self,
manifest: &Manifest,
app_hmac: &[u8; 32],
elf: &ElfFile,
callbacks: &C,
) -> Result<Vec<u8>, &'static str> {
// concatenate the serialized manifest and the app_hmac
let mut data =
Expand All @@ -503,6 +503,7 @@ impl<T: Transport> VanadiumClient<T> {
code_seg,
data_seg,
stack_seg,
callbacks,
};

// initial APDU to start the V-App
Expand Down
20 changes: 0 additions & 20 deletions host/Cargo.toml

This file was deleted.

14 changes: 0 additions & 14 deletions host/README.md

This file was deleted.

Loading

0 comments on commit 9303d3b

Please sign in to comment.