From ebb65e9e7682c4c41b06bb3db67f185917d7f583 Mon Sep 17 00:00:00 2001 From: Alexis Grojean Date: Tue, 16 Jan 2024 09:54:48 +0100 Subject: [PATCH] Add StaticReview --- ledger_device_sdk/examples/stax.rs | 10 +- ledger_device_sdk/src/io.rs | 6 + ledger_device_sdk/src/nbgl.rs | 228 +++++++++++++++++++++++++++-- 3 files changed, 227 insertions(+), 17 deletions(-) diff --git a/ledger_device_sdk/examples/stax.rs b/ledger_device_sdk/examples/stax.rs index 7fa26efa..1c540885 100644 --- a/ledger_device_sdk/examples/stax.rs +++ b/ledger_device_sdk/examples/stax.rs @@ -6,7 +6,7 @@ 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::nbgl::{Home, StaticReview}; use ledger_device_sdk::uxapp::UxEvent; use ledger_secure_sdk_sys::seph; use ledger_secure_sdk_sys::*; @@ -91,12 +91,16 @@ extern "C" fn sample_main() { let mut comm = Comm::new(); - let mut myHome = Home::new(Some(&mut comm)) + let mut myHome = Home::new(Some(&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(); + + // myHome.show(); loop { match myHome.get_events::() { diff --git a/ledger_device_sdk/src/io.rs b/ledger_device_sdk/src/io.rs index d7b1af7e..30071688 100644 --- a/ledger_device_sdk/src/io.rs +++ b/ledger_device_sdk/src/io.rs @@ -292,8 +292,14 @@ 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(); + }, + #[cfg(target_os = "stax")] seph::Events::ScreenTouch => unsafe { ux_process_finger_event(spi_buffer.as_mut_ptr()); diff --git a/ledger_device_sdk/src/nbgl.rs b/ledger_device_sdk/src/nbgl.rs index 36684a27..7bd22714 100644 --- a/ledger_device_sdk/src/nbgl.rs +++ b/ledger_device_sdk/src/nbgl.rs @@ -6,13 +6,16 @@ use ledger_secure_sdk_sys::seph; use ledger_secure_sdk_sys::*; pub struct Home<'a> { - comm: Option<&'a mut Comm>, + comm: Option<&'a Comm>, } -struct info_struct { +struct NBGLContext { icon: Option<&'static [u8]>, name: [u8; 100], infocontents: [[u8; 20]; 2], + reviewPairs: [ledger_secure_sdk_sys::nbgl_layoutTagValue_t; 10], + nbPairs: u8, + displayHome: bool, } const infoTypes: [*const ::core::ffi::c_char; 2] = [ @@ -20,31 +23,31 @@ const infoTypes: [*const ::core::ffi::c_char; 2] = [ "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> { + pub fn new(comm: Option<&'a Comm>) -> Home<'a> { Home { comm } } pub fn app_name(self, app_name: &'static str) -> Home<'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> { unsafe { - infos.icon = Some(icon); + ctx.icon = Some(icon); } self } pub fn info_contents(self, version: &str, author: &str) -> Home<'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.infocontents[0][..version.len()].copy_from_slice(version.as_bytes()); + ctx.infocontents[1][..author.len()].copy_from_slice(author.as_bytes()); } self } @@ -57,10 +60,10 @@ impl<'a> Home<'a> { (*content).__bindgen_anon_1.infosList.nbInfos = 2; (*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.infocontents[0].as_ptr() as *const ::core::ffi::c_char, + ctx.infocontents[1].as_ptr() as *const ::core::ffi::c_char, ] - .as_ptr(); + .as_ptr() as *const *const ::core::ffi::c_char; } else { return false; } @@ -68,7 +71,7 @@ impl<'a> Home<'a> { }; 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, @@ -86,11 +89,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, @@ -109,6 +112,14 @@ impl<'a> Home<'a> { Reply: From<>::Error>, { loop { + + unsafe { + if ctx.displayHome { + self.show(); + ctx.displayHome = false; + } + } + match &mut self.comm { None => (), Some(comm) => { @@ -120,3 +131,192 @@ impl<'a> Home<'a> { } } } + +// pub struct nbgl_layoutTagValueList_t { +// pub pairs: *mut nbgl_layoutTagValue_t, +// pub callback: nbgl_tagValueCallback_t, +// pub nbPairs: u8, +// pub startIndex: u8, +// pub nbMaxLinesForValue: u8, +// pub token: u8, +// pub smallCaseForValue: bool, +// pub wrapping: bool, +// } + + +// pub struct nbgl_layoutTagValue_t { +// pub item: *const ::core::ffi::c_char, +// pub value: *const ::core::ffi::c_char, +// pub valueIcon: *const nbgl_icon_details_t, +// } + +// pub type nbgl_choiceCallback_t = ::core::option::Option; + +pub struct TagValue { + item: &'static str, + value: &'static str, +} + +// static void displayTransaction(void) { +// nbgl_useCaseStaticReview(&pairList, &infoLongPress, "Reject transaction", reviewChoice); +// } + +// static void reviewStart() { +// nbgl_callback_t displayFunction = displayTransaction; +// nbgl_useCaseReviewStart(&C_stax_app_tron_64px, +// txctx.flowTitle, +// txctx.flowSubtitle, +// "Reject transaction", +// displayFunction, +// rejectChoice); +// } + +// void nbgl_useCaseReviewStart(const nbgl_icon_details_t *icon, +// const char *reviewTitle, +// const char *reviewSubTitle, +// const char *rejectText, +// nbgl_callback_t continueCallback, +// nbgl_callback_t rejectCallback); + +// extern "C" { +// pub fn nbgl_useCaseStaticReview( +// tagValueList: *const nbgl_layoutTagValueList_t, +// infoLongPress: *const nbgl_pageInfoLongPress_t, +// rejectText: *const ::core::ffi::c_char, +// callback: nbgl_choiceCallback_t, +// ); +// } + +pub enum ReviewStatus { + Pending, + Validate, + Reject, +} + +pub struct StaticReview<'a> { + status: ReviewStatus, + comm: Option<&'a Comm>, +} + +impl<'a> StaticReview<'a> { + + pub fn new(comm: Option<&'a Comm>) -> StaticReview<'a> { + StaticReview { status: ReviewStatus::Pending, comm } + } + +// called when long press button on 3rd page is long-touched or when reject footer is touched +// static void review_choice(bool confirm) { +// if (confirm) { +// // display a status page and go back to main +// validate_transaction(true); +// nbgl_useCaseStatus("TRANSACTION\nSIGNED", true, ui_menu_main); +// } else { +// ask_transaction_rejection_confirmation(); +// } +// } + + fn choice(confirm: bool) { + let show_home = || { + Home::home(); + }; + + if confirm { + // display a status page and go back to main + // validate_transaction(true); + unsafe { + ledger_secure_sdk_sys::nbgl_useCaseStatus( + "TRANSACTION\nSIGNED\0".as_ptr() as *const ::core::ffi::c_char, + true, + transmute(show_home as fn())); + } + } else { + // ask_transaction_rejection_confirmation(); + } + } + + fn static_review() { + unsafe { + ctx.reviewPairs[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.reviewPairs[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(), + }; + + let tagValueList: nbgl_layoutTagValueList_t = nbgl_layoutTagValueList_t { + pairs: ctx.reviewPairs.as_mut_ptr() as *mut nbgl_layoutTagValue_t, + callback: None, + nbPairs: 2, + startIndex: 2, + nbMaxLinesForValue: 0, + token: 0, + smallCaseForValue: false, + wrapping: false, + }; + + + // pub struct nbgl_pageInfoLongPress_s { + // pub text: *const ::core::ffi::c_char, + // pub icon: *const nbgl_icon_details_t, + // pub longPressText: *const ::core::ffi::c_char, + // pub longPressToken: u8, + // pub tuneId: tune_index_e, + // } + + let icon = nbgl_icon_details_t { + width: 64, + height: 64, + bpp: 2, + isFile: true, + bitmap: ctx.icon.unwrap().as_ptr(), + }; + + let infoLongPress: 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( + &tagValueList as *const nbgl_layoutTagValueList_t, + &infoLongPress 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_and_wait_validation(&self) -> bool { + unsafe { + let icon = nbgl_icon_details_t { + width: 64, + height: 64, + bpp: 2, + isFile: true, + bitmap: ctx.icon.unwrap().as_ptr(), + }; + + 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 { + // if self. + // }; + true + } + } +}