Skip to content

Commit 133d699

Browse files
committed
Support going to settings page
1 parent 1a491e6 commit 133d699

File tree

10 files changed

+168
-27
lines changed

10 files changed

+168
-27
lines changed

app/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ utils= { path = "../utils" }
1212
numtoa = "=0.2.4"
1313
shlex = { version = "1.3.0", default-features = false } # patch for ledger_device_sdk dependency
1414
include_gif = "1.2.0"
15+
const-zero = "0.1.1"
1516

1617
[features]
1718
default = ["device"]

app/src/handler.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ fn handle_sign_tx(
181181
let tx_data = &data[PATH_LENGTH..];
182182
let is_tx_execute_script = tx_data[SCRIPT_OFFSET - 1] == CALL_CONTRACT_FLAG;
183183
if is_tx_execute_script {
184-
check_blind_signing()?;
184+
check_blind_signing(tx_reviewer)?;
185185
}
186186
tx_reviewer.set_tx_execute_script(is_tx_execute_script);
187187

app/src/ledger_sdk_stub/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
#[cfg(not(any(target_os = "stax", target_os = "flex")))]
22
pub mod multi_field_review;
33

4+
#[cfg(any(target_os = "stax", target_os = "flex"))]
5+
pub mod nbgl_display;
6+
47
#[cfg(any(target_os = "stax", target_os = "flex"))]
58
pub mod nbgl_review;
69

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// The purpose of this file is to address the issue of the ledger rust sdk not supporting
2+
// navigation to the settings page.
3+
// To maintain consistency with the ledger rust sdk code, we have ignored clipply warnings here.
4+
#![allow(clippy::all)]
5+
6+
use crate::settings::{SETTINGS_DATA, SETTINGS_SIZE};
7+
use const_zero::const_zero;
8+
9+
extern crate alloc;
10+
use alloc::ffi::CString;
11+
use alloc::vec::Vec;
12+
use core::ffi::*;
13+
use core::mem::transmute;
14+
use include_gif::include_gif;
15+
use ledger_device_sdk::io::{ApduHeader, Comm, Event, Reply};
16+
use ledger_device_sdk::nbgl::{NbglGlyph, TuneIndex};
17+
use ledger_device_sdk::nvm::{AtomicStorage, SingleStorage};
18+
use ledger_secure_sdk_sys::*;
19+
20+
static mut NVM_REF: Option<&mut AtomicStorage<[u8; SETTINGS_SIZE]>> = None;
21+
static mut SWITCH_ARRAY: [nbgl_contentSwitch_t; SETTINGS_SIZE] =
22+
[unsafe { const_zero!(nbgl_contentSwitch_t) }; SETTINGS_SIZE];
23+
24+
/// Information fields name to display in the dedicated
25+
/// page of the home screen.
26+
const INFO_FIELDS: [*const c_char; 2] = [
27+
"Version\0".as_ptr() as *const c_char,
28+
"Developer\0".as_ptr() as *const c_char,
29+
];
30+
31+
pub fn nbgl_display<'a, T: TryFrom<ApduHeader>>(
32+
comm: &mut Comm,
33+
settings_strings: &[[&'a str; 2]],
34+
page: u8,
35+
) -> Event<T>
36+
where
37+
Reply: From<<T as TryFrom<ApduHeader>>::Error>,
38+
{
39+
let mut info_contents: Vec<CString> = Vec::new();
40+
info_contents.push(CString::new("Alephium").unwrap());
41+
info_contents.push(CString::new(env!("CARGO_PKG_VERSION")).unwrap());
42+
info_contents.push(CString::new(env!("CARGO_PKG_AUTHORS")).unwrap());
43+
44+
unsafe {
45+
NVM_REF = Some(transmute(SETTINGS_DATA.get_mut()));
46+
}
47+
48+
let nb_settings = settings_strings.len() as u8;
49+
let setting_contents: Vec<[CString; 2]> = settings_strings
50+
.iter()
51+
.map(|s| [CString::new(s[0]).unwrap(), CString::new(s[1]).unwrap()])
52+
.collect();
53+
54+
const APP_ICON: NbglGlyph = NbglGlyph::from_include(include_gif!("alph_64x64.gif", NBGL));
55+
unsafe {
56+
loop {
57+
let info_contents: Vec<*const c_char> =
58+
info_contents.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
59+
60+
let info_list: nbgl_contentInfoList_t = nbgl_contentInfoList_t {
61+
infoTypes: INFO_FIELDS.as_ptr() as *const *const c_char,
62+
infoContents: info_contents[1..].as_ptr() as *const *const c_char,
63+
nbInfos: INFO_FIELDS.len() as u8,
64+
};
65+
66+
let icon: nbgl_icon_details_t = (&APP_ICON).into();
67+
68+
for (i, setting) in setting_contents.iter().enumerate() {
69+
SWITCH_ARRAY[i].text = setting[0].as_ptr();
70+
SWITCH_ARRAY[i].subText = setting[1].as_ptr();
71+
SWITCH_ARRAY[i].initState = NVM_REF.as_mut().unwrap().get_ref()[i] as nbgl_state_t;
72+
SWITCH_ARRAY[i].token = (FIRST_USER_TOKEN + i as u32) as u8;
73+
SWITCH_ARRAY[i].tuneId = TuneIndex::TapCasual as u8;
74+
}
75+
76+
let content: nbgl_content_t = nbgl_content_t {
77+
content: nbgl_content_u {
78+
switchesList: nbgl_pageSwitchesList_s {
79+
switches: &SWITCH_ARRAY as *const nbgl_contentSwitch_t,
80+
nbSwitches: nb_settings,
81+
},
82+
},
83+
contentActionCallback: Some(settings_callback),
84+
type_: SWITCHES_LIST,
85+
};
86+
87+
let generic_contents: nbgl_genericContents_t = nbgl_genericContents_t {
88+
callbackCallNeeded: false,
89+
__bindgen_anon_1: nbgl_genericContents_t__bindgen_ty_1 {
90+
contentsList: &content as *const nbgl_content_t,
91+
},
92+
nbContents: if nb_settings > 0 { 1 } else { 0 },
93+
};
94+
95+
match ux_sync_homeAndSettings(
96+
info_contents[0],
97+
&icon as *const nbgl_icon_details_t,
98+
core::ptr::null(),
99+
page,
100+
&generic_contents as *const nbgl_genericContents_t,
101+
&info_list as *const nbgl_contentInfoList_t,
102+
core::ptr::null(),
103+
) {
104+
UX_SYNC_RET_APDU_RECEIVED => {
105+
if let Some(event) = comm.check_event() {
106+
return event;
107+
}
108+
}
109+
_ => {
110+
panic!("Unexpected return value from ux_sync_homeAndSettings");
111+
}
112+
}
113+
}
114+
}
115+
}
116+
117+
/// Callback triggered by the NBGL API when a setting switch is toggled.
118+
unsafe extern "C" fn settings_callback(token: c_int, _index: u8, _page: c_int) {
119+
let idx = token - FIRST_USER_TOKEN as i32;
120+
if idx < 0 || idx >= SETTINGS_SIZE as i32 {
121+
panic!("Invalid token.");
122+
}
123+
124+
if let Some(data) = NVM_REF.as_mut() {
125+
let setting_idx: usize = idx as usize;
126+
let mut switch_values: [u8; SETTINGS_SIZE] = data.get_ref().clone();
127+
switch_values[setting_idx] = !switch_values[setting_idx];
128+
data.update(&switch_values);
129+
SWITCH_ARRAY[setting_idx].initState = switch_values[setting_idx] as nbgl_state_t;
130+
}
131+
}

app/src/main.rs

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@ use crate::ui::display::MainPages;
66
use crate::ui::tx_reviewer::TxReviewer;
77
use handler::{handle_apdu, Ins};
88
use ledger_device_sdk::io;
9-
#[cfg(any(target_os = "stax", target_os = "flex"))]
10-
use ledger_device_sdk::nbgl::{init_comm, NbglHomeAndSettings};
11-
#[cfg(any(target_os = "stax", target_os = "flex"))]
12-
use settings::SETTINGS_DATA;
139
use sign_tx_context::SignTxContext;
1410

1511
mod blake2b_hasher;
@@ -52,22 +48,18 @@ extern "C" fn sample_main() {
5248

5349
#[cfg(any(target_os = "stax", target_os = "flex"))]
5450
{
55-
use crate::ui::nbgl::APP_ICON;
56-
init_comm(&mut comm);
57-
let settings_strings = [["Blind signing", "Enable blind signing"]];
51+
use crate::ledger_sdk_stub::nbgl_display::nbgl_display;
52+
use ledger_device_sdk::nbgl::init_comm;
53+
use ledger_secure_sdk_sys::INIT_HOME_PAGE;
5854

59-
let mut home_and_settings = NbglHomeAndSettings::new()
60-
.glyph(&APP_ICON)
61-
.settings(unsafe { SETTINGS_DATA.get_mut() }, &settings_strings)
62-
.infos(
63-
"Alephium",
64-
env!("CARGO_PKG_VERSION"),
65-
env!("CARGO_PKG_AUTHORS"),
66-
);
55+
init_comm(&mut comm);
56+
let settings_strings = &[["Blind signing", "Enable blind signing"]];
6757

6858
loop {
69-
let event = if !tx_reviewer.nbgl_reviewer.review_started {
70-
home_and_settings.show::<Ins>()
59+
let event = if tx_reviewer.nbgl_reviewer.display_settings {
60+
nbgl_display::<Ins>(&mut comm, settings_strings, 0)
61+
} else if !tx_reviewer.nbgl_reviewer.review_started {
62+
nbgl_display::<Ins>(&mut comm, settings_strings, INIT_HOME_PAGE as u8)
7163
} else {
7264
comm.next_event()
7365
};

app/src/settings.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use ledger_device_sdk::nvm::{AtomicStorage, SingleStorage};
22
use ledger_device_sdk::NVMData;
33

44
// Keep the size consistent with the settings defined in the ledger sdk
5-
const SETTINGS_SIZE: usize = 10;
5+
pub const SETTINGS_SIZE: usize = 10;
66
#[link_section = ".nvm_data"]
77
pub static mut SETTINGS_DATA: NVMData<AtomicStorage<[u8; SETTINGS_SIZE]>> =
88
NVMData::new(AtomicStorage::new(&[0u8; SETTINGS_SIZE]));

app/src/sign_tx_context.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ impl SignTxContext {
162162
}
163163

164164
#[cfg(not(any(target_os = "stax", target_os = "flex")))]
165-
pub fn check_blind_signing() -> Result<(), ErrorCode> {
165+
pub fn check_blind_signing(_tx_reviewer: &mut TxReviewer) -> Result<(), ErrorCode> {
166166
use ledger_device_sdk::{
167167
buttons::{ButtonEvent, ButtonsState},
168168
ui::{
@@ -193,17 +193,20 @@ pub fn check_blind_signing() -> Result<(), ErrorCode> {
193193
}
194194

195195
#[cfg(any(target_os = "stax", target_os = "flex"))]
196-
pub fn check_blind_signing() -> Result<(), ErrorCode> {
196+
pub fn check_blind_signing(tx_reviewer: &mut TxReviewer) -> Result<(), ErrorCode> {
197197
use crate::ui::nbgl::nbgl_review_warning;
198198

199199
if is_blind_signing_enabled() {
200200
return Ok(());
201201
}
202-
let _ = nbgl_review_warning(
202+
let go_to_settings = nbgl_review_warning(
203203
"This transaction cannot be clear-signed",
204204
"Enable blind signing in the settings to sign this transaction.",
205-
"Go to home", // The ledger rust sdk does not support going to settings.
205+
"Go to settings",
206206
"Reject transaction",
207207
);
208+
if go_to_settings {
209+
tx_reviewer.nbgl_reviewer.set_display_settings(true);
210+
}
208211
Err(ErrorCode::BlindSigningDisabled)
209212
}

app/src/ui/nbgl.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,23 @@ fn new_nbgl_review(tx_type: TransactionType, blind: bool) -> NbglStreamingReview
1616

1717
pub struct NbglReviewer {
1818
pub review_started: bool,
19+
pub display_settings: bool,
1920
reviewer: Option<NbglStreamingReview>,
2021
}
2122

2223
impl NbglReviewer {
2324
pub fn new() -> NbglReviewer {
2425
NbglReviewer {
2526
review_started: false,
27+
display_settings: false,
2628
reviewer: None,
2729
}
2830
}
2931

3032
pub fn reset(&mut self) {
33+
// Since `reset` is called when blind signing checks fails,
34+
// we cannot reset the `display_settings` within the reset function.
35+
// Instead, we will reset the `display_settings` in the `finish_review` function.
3136
self.review_started = false;
3237
self.reviewer = None;
3338
}
@@ -38,6 +43,10 @@ impl NbglReviewer {
3843
self.reviewer.as_ref().unwrap()
3944
}
4045

46+
pub fn set_display_settings(&mut self, display_settings: bool) {
47+
self.display_settings = display_settings;
48+
}
49+
4150
pub fn set_reviewer(&mut self, blind: bool) {
4251
assert!(self.reviewer.is_none());
4352
self.reviewer = Some(new_nbgl_review(TransactionType::Transaction, blind));
@@ -62,7 +71,8 @@ impl NbglReviewer {
6271
}
6372
}
6473

65-
pub fn finish_review(&self, message: &str) -> Result<(), ErrorCode> {
74+
pub fn finish_review(&mut self, message: &str) -> Result<(), ErrorCode> {
75+
self.display_settings = false;
6676
if self.get_reviewer().finish(message) {
6777
NbglReviewStatus::new().show(true);
6878
Ok(())

app/src/ui/tx_reviewer.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,7 @@ impl TxReviewer {
448448
}
449449

450450
#[cfg(any(target_os = "stax", target_os = "flex"))]
451-
fn finish_review<'a>(&self, fields: &'a [Field<'a>]) -> Result<(), ErrorCode> {
451+
fn finish_review<'a>(&mut self, fields: &'a [Field<'a>]) -> Result<(), ErrorCode> {
452452
self.review(fields, "Fees")?;
453453
let message = if self.is_tx_execute_script {
454454
"Accept risk and sign transaction"
@@ -587,7 +587,7 @@ impl TxReviewer {
587587
}
588588

589589
// Review transfer that sends to self
590-
fn review_self_transfer(&self, fee_field: &Field) -> Result<(), ErrorCode> {
590+
fn review_self_transfer(&mut self, fee_field: &Field) -> Result<(), ErrorCode> {
591591
#[cfg(not(any(target_os = "stax", target_os = "flex")))]
592592
{
593593
let fields = &[Field {
@@ -670,7 +670,7 @@ impl TxReviewer {
670670
}
671671

672672
// Review the rest transaction details and approve it
673-
pub fn approve_tx(&self) -> Result<(), ErrorCode> {
673+
pub fn approve_tx(&mut self) -> Result<(), ErrorCode> {
674674
assert!(self.tx_fee.is_some());
675675
let mut amount_output = [0u8; 33];
676676
let amount_str = self

0 commit comments

Comments
 (0)