Skip to content

Commit

Permalink
Add StaticReview
Browse files Browse the repository at this point in the history
  • Loading branch information
agrojean-ledger committed Jan 16, 2024
1 parent 5e67dca commit 2adf1ca
Show file tree
Hide file tree
Showing 3 changed files with 212 additions and 42 deletions.
26 changes: 16 additions & 10 deletions ledger_device_sdk/examples/stax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,9 @@ use ledger_device_sdk as _;

use const_zero::const_zero;
use ledger_device_sdk::io::*;
use ledger_device_sdk::nbgl::Home;
use ledger_device_sdk::uxapp::UxEvent;
use ledger_secure_sdk_sys::seph;
use ledger_device_sdk::nbgl::NbglUI;
use ledger_secure_sdk_sys::*;

use include_gif::include_gif;
use ledger_device_sdk::testing::debug_print;

#[no_mangle]
pub static mut G_ux_params: bolos_ux_params_t = unsafe { const_zero!(bolos_ux_params_t) };
Expand Down Expand Up @@ -91,21 +88,30 @@ extern "C" fn sample_main() {

let mut comm = Comm::new();

let mut myHome = Home::new(Some(&mut comm))
let mut nbgl_ui = NbglUI::new(Some(&mut comm))
.app_name("Stax Sample\0")
.info_contents(env!("CARGO_PKG_VERSION"), env!("CARGO_PKG_AUTHORS"))
.icon(&BTC_BMP);

myHome.show();
// let myStaticReview = StaticReview::new(Some(&comm));

// myStaticReview.show_and_wait_validation();

// myNBGL.show_home();
if nbgl_ui.show_review_and_wait_validation() {
debug_print("Validation result: true\n");
} else {
debug_print("Validation result: false\n");
}

loop {
match myHome.get_events::<Instruction>() {
Event::Command(ins) => (),
match nbgl_ui.get_events::<Instruction>() {
Event::Command(_) => (),
_ => (),
};
}

exit_app(0);
// exit_app(0);
}

const BTC_BMP: [u8; 573] = [
Expand Down
12 changes: 12 additions & 0 deletions ledger_device_sdk/src/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,10 @@ pub enum Event<T> {
/// APDU event
Command(T),
/// Button press or release event
#[cfg(not(target_os = "stax"))]
Button(ButtonEvent),
#[cfg(target_os = "stax")]
TouchEvent,
/// Ticker
Ticker,
}
Expand Down Expand Up @@ -271,6 +274,7 @@ impl Comm {
// If this is an APDU, return with the "received command" event
// Any other event (usb, xfer, ticker) is silently handled
match seph::Events::from(tag) {
#[cfg(not(target_os = "stax"))]
seph::Events::ButtonPush => {
let button_info = spi_buffer[3] >> 1;
if let Some(btn_evt) = get_button_event(&mut self.buttons, button_info) {
Expand All @@ -292,11 +296,19 @@ impl Comm {
#[cfg(target_os = "nanox")]
seph::Events::BleReceive => ble::receive(&mut self.apdu_buffer, spi_buffer),

#[cfg(not(target_os = "stax"))]
seph::Events::TickerEvent => return Some(Event::Ticker),

#[cfg(target_os = "stax")]
seph::Events::TickerEvent => unsafe {
ux_process_ticker_event();
return Some(Event::Ticker);
},

#[cfg(target_os = "stax")]
seph::Events::ScreenTouch => unsafe {
ux_process_finger_event(spi_buffer.as_mut_ptr());
return Some(Event::TouchEvent);
},

_ => (),
Expand Down
216 changes: 184 additions & 32 deletions ledger_device_sdk/src/nbgl.rs
Original file line number Diff line number Diff line change
@@ -1,50 +1,76 @@
use crate::io::{self, ApduHeader, Comm, Event, Reply};
use crate::io::{ApduHeader, Comm, Event, Reply, StatusWords};
use const_zero::const_zero;
use core::mem::transmute;
use ledger_secure_sdk_sys::nbgl_icon_details_t;
use ledger_secure_sdk_sys::seph;
use ledger_secure_sdk_sys::*;
use crate::testing::debug_print;

pub struct Home<'a> {

enum DummyEvent {
DummyValue,
}

impl TryFrom<ApduHeader> for DummyEvent {
type Error = StatusWords;

fn try_from(value: ApduHeader) -> Result<Self, Self::Error> {
match value.ins {
1 => Ok(DummyEvent::DummyValue),
_ => Err(StatusWords::NothingReceived),
}
}
}

pub struct NbglUI<'a> {
comm: Option<&'a mut Comm>,
}

struct info_struct {
#[derive(PartialEq)]
pub enum ReviewStatus {
Pending,
Validate,
Reject,
}

struct NbglContext {
icon: Option<&'static [u8]>,
name: [u8; 100],
infocontents: [[u8; 20]; 2],
info_contents: [[u8; 20]; 2],
review_pairs: [ledger_secure_sdk_sys::nbgl_layoutTagValue_t; 10],
nb_pairs: u8,
review_status: ReviewStatus,
}

const infoTypes: [*const ::core::ffi::c_char; 2] = [
const INFOTYPES: [*const ::core::ffi::c_char; 2] = [
"Version\0".as_ptr() as *const ::core::ffi::c_char,
"Developer\0".as_ptr() as *const ::core::ffi::c_char,
];

static mut infos: info_struct = unsafe { const_zero!(info_struct) };
static mut CTX: NbglContext = unsafe { const_zero!(NbglContext) };

impl<'a> Home<'a> {
pub fn new(comm: Option<&'a mut Comm>) -> Home<'a> {
Home { comm }
impl<'a> NbglUI<'a> {
pub fn new(comm: Option<&'a mut Comm>) -> NbglUI<'a> {
NbglUI { comm }
}

pub fn app_name(self, app_name: &'static str) -> Home<'a> {
pub fn app_name(self, app_name: &'static str) -> NbglUI<'a> {
unsafe {
infos.name[..app_name.len()].copy_from_slice(app_name.as_bytes());
CTX.name[..app_name.len()].copy_from_slice(app_name.as_bytes());
}
self
}

pub fn icon(self, icon: &'static [u8]) -> Home<'a> {
pub fn icon(self, icon: &'static [u8]) -> NbglUI<'a> {
unsafe {
infos.icon = Some(icon);
CTX.icon = Some(icon);
}
self
}

pub fn info_contents(self, version: &str, author: &str) -> Home<'a> {
pub fn info_contents(self, version: &str, author: &str) -> NbglUI<'a> {
unsafe {
infos.infocontents[0][..version.len()].copy_from_slice(version.as_bytes());
infos.infocontents[1][..author.len()].copy_from_slice(author.as_bytes());
CTX.info_contents[0][..version.len()].copy_from_slice(version.as_bytes());
CTX.info_contents[1][..author.len()].copy_from_slice(author.as_bytes());
}
self
}
Expand All @@ -55,20 +81,20 @@ impl<'a> Home<'a> {
if page == 0 {
(*content).type_ = ledger_secure_sdk_sys::INFOS_LIST;
(*content).__bindgen_anon_1.infosList.nbInfos = 2;
(*content).__bindgen_anon_1.infosList.infoTypes = infoTypes.as_ptr();
(*content).__bindgen_anon_1.infosList.infoTypes = INFOTYPES.as_ptr();
(*content).__bindgen_anon_1.infosList.infoContents = [
infos.infocontents[0].as_ptr() as *const ::core::ffi::c_char,
infos.infocontents[1].as_ptr() as *const ::core::ffi::c_char,
CTX.info_contents[0].as_ptr() as *const ::core::ffi::c_char,
CTX.info_contents[1].as_ptr() as *const ::core::ffi::c_char,
]
.as_ptr();
.as_ptr() as *const *const ::core::ffi::c_char;
} else {
return false;
}
true
};

ledger_secure_sdk_sys::nbgl_useCaseSettings(
infos.name.as_ptr() as *const core::ffi::c_char,
CTX.name.as_ptr() as *const core::ffi::c_char,
0 as u8,
1 as u8,
false as bool,
Expand All @@ -86,11 +112,11 @@ impl<'a> Home<'a> {
height: 64,
bpp: 2,
isFile: true,
bitmap: infos.icon.unwrap().as_ptr(),
bitmap: CTX.icon.unwrap().as_ptr(),
};

ledger_secure_sdk_sys::nbgl_useCaseHome(
infos.name.as_ptr() as *const core::ffi::c_char,
CTX.name.as_ptr() as *const core::ffi::c_char,
&icon as *const nbgl_icon_details_t,
core::ptr::null(),
true as bool,
Expand All @@ -100,22 +126,148 @@ impl<'a> Home<'a> {
}
}

pub fn show(&mut self) {
pub fn show_home(&mut self) {
Self::home();
}

fn choice(confirm: bool) {
let show_home = || {
Self::home();
};

let result_string: *const ::core::ffi::c_char;
unsafe {
if confirm {
CTX.review_status = ReviewStatus::Validate;
result_string = "TRANSACTION\nSIGNED\0".as_ptr() as *const ::core::ffi::c_char;
} else {
CTX.review_status = ReviewStatus::Reject;
result_string = "Transaction\nRejected\0".as_ptr() as *const ::core::ffi::c_char;
}
}

unsafe {
ledger_secure_sdk_sys::nbgl_useCaseStatus(
result_string,
confirm,
transmute(show_home as fn()));
}
}

fn static_review() {
unsafe {
CTX.review_pairs[0] = nbgl_layoutTagValue_t {
item: "Pair 1\0".as_ptr() as *const ::core::ffi::c_char,
value: "Value 1\0".as_ptr() as *const ::core::ffi::c_char,
valueIcon: core::ptr::null(),
};
CTX.review_pairs[1] = nbgl_layoutTagValue_t {
item: "Pair 2\0".as_ptr() as *const ::core::ffi::c_char,
value: "Value 2\0".as_ptr() as *const ::core::ffi::c_char,
valueIcon: core::ptr::null(),
};
CTX.nb_pairs = 2;

let tag_value_list: nbgl_layoutTagValueList_t = nbgl_layoutTagValueList_t {
pairs: CTX.review_pairs.as_mut_ptr() as *mut nbgl_layoutTagValue_t,
callback: None,
nbPairs: CTX.nb_pairs,
startIndex: 2,
nbMaxLinesForValue: 0,
token: 0,
smallCaseForValue: false,
wrapping: false,
};

// Using this icon causes a crash
// let icon = nbgl_icon_details_t {
// width: 64,
// height: 64,
// bpp: 2,
// isFile: true,
// bitmap: CTX.icon.unwrap().as_ptr(),
// };

let info_long_press: nbgl_pageInfoLongPress_t = nbgl_pageInfoLongPress_t {
text: "Validate tx\0".as_ptr() as *const ::core::ffi::c_char,
icon: core::ptr::null(),
longPressText: "Hold to validate\0".as_ptr() as *const ::core::ffi::c_char,
longPressToken: 0,
tuneId: 0,
};

ledger_secure_sdk_sys::nbgl_useCaseStaticReview(
&tag_value_list as *const nbgl_layoutTagValueList_t,
&info_long_press as *const nbgl_pageInfoLongPress_t,
"Reject tx\0".as_ptr() as *const ::core::ffi::c_char,
// None,
transmute((|confirm| Self::choice(confirm)) as fn(confirm: bool)),
);
}
}


pub fn show_review_and_wait_validation(&mut self) -> bool {
unsafe {
let icon = nbgl_icon_details_t {
width: 64,
height: 64,
bpp: 2,
isFile: true,
bitmap: CTX.icon.unwrap().as_ptr(),
};

CTX.review_status = ReviewStatus::Pending;

ledger_secure_sdk_sys::nbgl_useCaseReviewStart(
&icon as *const nbgl_icon_details_t,
"Review tx\0".as_ptr() as *const ::core::ffi::c_char,
"Subtitle\0".as_ptr() as *const ::core::ffi::c_char,
"Reject transaction\0".as_ptr() as *const ::core::ffi::c_char,
transmute((|| Self::static_review()) as fn()),
None,
);

loop {
self.get_event::<DummyEvent>();

if CTX.review_status == ReviewStatus::Validate {
debug_print("Validate\n");
return true;
}
else if CTX.review_status == ReviewStatus::Reject {
debug_print("Reject\n");
return false;
}
}
}
}

fn get_event<T: TryFrom<ApduHeader>>(&mut self) -> Option<Event<T>>
where
Reply: From<<T as TryFrom<ApduHeader>>::Error>,
{
match &mut self.comm {
None => None,
Some(comm) => {
if let Event::Command(ins) = comm.next_event() {
return Some(Event::Command(ins));
}
else
{
return None;
}
}
}
}

pub fn get_events<T: TryFrom<ApduHeader>>(&mut self) -> Event<T>
where
Reply: From<<T as TryFrom<ApduHeader>>::Error>,
{
loop {
match &mut self.comm {
None => (),
Some(comm) => {
if let Event::Command(ins) = comm.next_event() {
return Event::Command(ins);
}
}
if let Some(event) = self.get_event::<T>() {
return event;
}
}
}
Expand Down

0 comments on commit 2adf1ca

Please sign in to comment.