Skip to content

Commit 1e7b585

Browse files
Merge pull request #170 from LedgerHQ/add_streaming_review
Add streaming review support in NBGL module
2 parents 3c22c1c + eed4139 commit 1e7b585

File tree

10 files changed

+1116
-1370
lines changed

10 files changed

+1116
-1370
lines changed

Cargo.lock

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

ledger_device_sdk/Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "ledger_device_sdk"
3-
version = "1.11.1"
3+
version = "1.12.0"
44
authors = ["yhql", "yogh333", "agrojean-ledger", "kingofpayne"]
55
edition = "2021"
66
license.workspace = true
@@ -23,10 +23,10 @@ numtoa = "0.2.4"
2323
const-zero = "0.1.1"
2424

2525
[target.'cfg(target_os="nanos")'.dependencies]
26-
ledger_secure_sdk_sys = {path = "../ledger_secure_sdk_sys", version = "1.4.2"}
26+
ledger_secure_sdk_sys = {path = "../ledger_secure_sdk_sys", version = "1.4.3"}
2727

2828
[target.'cfg(not(target_os="nanos"))'.dependencies]
29-
ledger_secure_sdk_sys = {path = "../ledger_secure_sdk_sys", version = "1.4.2", features = ["heap"]}
29+
ledger_secure_sdk_sys = {path = "../ledger_secure_sdk_sys", version = "1.4.3", features = ["heap"]}
3030

3131
[features]
3232
speculos = []

ledger_device_sdk/examples/nbgl_review.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ use ledger_device_sdk::io::*;
99
use ledger_device_sdk::nbgl::{init_comm, Field, NbglGlyph, NbglReview};
1010
use ledger_secure_sdk_sys::*;
1111

12+
#[panic_handler]
13+
fn panic(_: &core::panic::PanicInfo) -> ! {
14+
exit_app(1);
15+
}
16+
1217
#[no_mangle]
1318
extern "C" fn sample_main() {
1419
unsafe {
@@ -46,7 +51,8 @@ extern "C" fn sample_main() {
4651
"To send CRAB",
4752
"Sign transaction\nto send CRAB",
4853
)
49-
.glyph(&FERRIS);
54+
.glyph(&FERRIS)
55+
.blind();
5056

5157
review.show(&my_fields);
5258
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#![no_std]
2+
#![no_main]
3+
4+
// Force boot section to be embedded in
5+
use ledger_device_sdk as _;
6+
7+
use include_gif::include_gif;
8+
use ledger_device_sdk::io::*;
9+
use ledger_device_sdk::nbgl::{init_comm, Field, NbglGlyph, NbglStreamingReview, TransactionType};
10+
use ledger_secure_sdk_sys::*;
11+
12+
#[panic_handler]
13+
fn panic(_: &core::panic::PanicInfo) -> ! {
14+
exit_app(1);
15+
}
16+
17+
#[no_mangle]
18+
extern "C" fn sample_main() {
19+
unsafe {
20+
nbgl_refreshReset();
21+
}
22+
23+
let mut comm = Comm::new();
24+
// Initialize reference to Comm instance for NBGL
25+
// API calls.
26+
init_comm(&mut comm);
27+
28+
// Load glyph from 64x64 4bpp gif file with include_gif macro. Creates an NBGL compatible glyph.
29+
const FERRIS: NbglGlyph =
30+
NbglGlyph::from_include(include_gif!("examples/crab_64x64.gif", NBGL));
31+
32+
let mut review: NbglStreamingReview = NbglStreamingReview::new()
33+
.glyph(&FERRIS)
34+
.tx_type(TransactionType::Message);
35+
36+
review.start("Example Title", "Example Subtitle");
37+
38+
let fields = [
39+
Field {
40+
name: "Name1",
41+
value: "Value1",
42+
},
43+
Field {
44+
name: "Name2",
45+
value: "Value2",
46+
},
47+
Field {
48+
name: "Name3",
49+
value: "Value3",
50+
},
51+
Field {
52+
name: "Name4",
53+
value: "Value4",
54+
},
55+
Field {
56+
name: "Name5",
57+
value: "Value5",
58+
},
59+
];
60+
61+
for i in 0..fields.len() {
62+
review.continue_review(&fields[i..i + 1]);
63+
}
64+
65+
review.finish("Sign to send token\n");
66+
}

ledger_device_sdk/src/nbgl.rs

Lines changed: 196 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,55 @@ impl<'a> Into<nbgl_icon_details_t> for &NbglGlyph<'a> {
8080
}
8181
}
8282

83+
pub enum TransactionType {
84+
Transaction,
85+
Message,
86+
Operation,
87+
}
88+
89+
impl TransactionType {
90+
pub fn to_c_type(&self, blind: bool, skippable: bool) -> nbgl_operationType_t {
91+
let mut tx_type = match self {
92+
TransactionType::Transaction => TYPE_TRANSACTION.into(),
93+
TransactionType::Message => TYPE_MESSAGE.into(),
94+
TransactionType::Operation => TYPE_OPERATION.into(),
95+
};
96+
if blind {
97+
tx_type |= BLIND_OPERATION;
98+
}
99+
if skippable {
100+
tx_type |= SKIPPABLE_OPERATION;
101+
}
102+
tx_type
103+
}
104+
105+
pub fn to_message(&self, success: bool) -> nbgl_reviewStatusType_t {
106+
match self {
107+
TransactionType::Transaction => {
108+
if success {
109+
STATUS_TYPE_TRANSACTION_SIGNED
110+
} else {
111+
STATUS_TYPE_TRANSACTION_REJECTED
112+
}
113+
}
114+
TransactionType::Message => {
115+
if success {
116+
STATUS_TYPE_MESSAGE_SIGNED
117+
} else {
118+
STATUS_TYPE_MESSAGE_REJECTED
119+
}
120+
}
121+
TransactionType::Operation => {
122+
if success {
123+
STATUS_TYPE_OPERATION_SIGNED
124+
} else {
125+
STATUS_TYPE_OPERATION_REJECTED
126+
}
127+
}
128+
}
129+
}
130+
}
131+
83132
/// Initialize the global COMM_REF variable with the provided Comm instance.
84133
/// This function should be called from the main function of the application.
85134
/// The COMM_REF variable is used by the NBGL API to detect touch events and
@@ -290,6 +339,8 @@ pub struct NbglReview<'a> {
290339
subtitle: CString,
291340
finish_title: CString,
292341
glyph: Option<&'a NbglGlyph<'a>>,
342+
tx_type: TransactionType,
343+
blind: bool,
293344
}
294345

295346
impl<'a> NbglReview<'a> {
@@ -299,6 +350,19 @@ impl<'a> NbglReview<'a> {
299350
subtitle: CString::new("").unwrap(),
300351
finish_title: CString::new("").unwrap(),
301352
glyph: None,
353+
tx_type: TransactionType::Transaction,
354+
blind: false,
355+
}
356+
}
357+
358+
pub fn tx_type(self, tx_type: TransactionType) -> NbglReview<'a> {
359+
NbglReview { tx_type, ..self }
360+
}
361+
362+
pub fn blind(self) -> NbglReview<'a> {
363+
NbglReview {
364+
blind: true,
365+
..self
302366
}
303367
}
304368

@@ -358,7 +422,7 @@ impl<'a> NbglReview<'a> {
358422

359423
// Show the review on the device.
360424
let sync_ret = ledger_secure_sdk_sys::ux_sync_review(
361-
TYPE_TRANSACTION.into(),
425+
self.tx_type.to_c_type(self.blind, false),
362426
&tag_value_list as *const nbgl_contentTagValueList_t,
363427
&icon as *const nbgl_icon_details_t,
364428
self.title.as_ptr() as *const c_char,
@@ -369,11 +433,11 @@ impl<'a> NbglReview<'a> {
369433
// Return true if the user approved the transaction, false otherwise.
370434
match sync_ret {
371435
ledger_secure_sdk_sys::UX_SYNC_RET_APPROVED => {
372-
ledger_secure_sdk_sys::ux_sync_reviewStatus(STATUS_TYPE_TRANSACTION_SIGNED);
436+
ledger_secure_sdk_sys::ux_sync_reviewStatus(self.tx_type.to_message(true));
373437
return true;
374438
}
375439
_ => {
376-
ledger_secure_sdk_sys::ux_sync_reviewStatus(STATUS_TYPE_TRANSACTION_REJECTED);
440+
ledger_secure_sdk_sys::ux_sync_reviewStatus(self.tx_type.to_message(false));
377441
return false;
378442
}
379443
}
@@ -749,6 +813,10 @@ impl From<&NbglPageContent>
749813
}
750814
}
751815

816+
/// A wrapper around the synchronous NBGL ux_sync_genericReview C API binding.
817+
/// Used to display custom built review screens. User can add different kind of
818+
/// contents (CenteredInfo, InfoLongPress, InfoButton, TagValueList, TagValueConfirm, InfosList)
819+
/// to the review screen using the add_content method.
752820
pub struct NbglGenericReview {
753821
content_list: Vec<NbglPageContent>,
754822
}
@@ -821,6 +889,131 @@ impl NbglGenericReview {
821889
}
822890
}
823891

892+
/// A wrapper around the synchronous NBGL ux_sync_reviewStreaming (start, continue and finish)
893+
/// C API binding. Used to display streamed transaction review screens.
894+
pub struct NbglStreamingReview {
895+
icon: nbgl_icon_details_t,
896+
tx_type: TransactionType,
897+
blind: bool,
898+
}
899+
900+
impl NbglStreamingReview {
901+
pub fn new() -> NbglStreamingReview {
902+
NbglStreamingReview {
903+
icon: nbgl_icon_details_t::default(),
904+
tx_type: TransactionType::Transaction,
905+
blind: false,
906+
}
907+
}
908+
909+
pub fn tx_type(self, tx_type: TransactionType) -> NbglStreamingReview {
910+
NbglStreamingReview { tx_type, ..self }
911+
}
912+
913+
pub fn blind(self) -> NbglStreamingReview {
914+
NbglStreamingReview {
915+
blind: true,
916+
..self
917+
}
918+
}
919+
920+
pub fn glyph(self, glyph: &NbglGlyph) -> NbglStreamingReview {
921+
NbglStreamingReview {
922+
icon: glyph.into(),
923+
..self
924+
}
925+
}
926+
927+
pub fn start(&mut self, title: &str, subtitle: &str) -> bool {
928+
unsafe {
929+
let title = CString::new(title).unwrap();
930+
let subtitle = CString::new(subtitle).unwrap();
931+
932+
let sync_ret = ux_sync_reviewStreamingStart(
933+
self.tx_type.to_c_type(self.blind, false),
934+
&self.icon as *const nbgl_icon_details_t,
935+
title.as_ptr() as *const c_char,
936+
subtitle.as_ptr() as *const c_char,
937+
);
938+
939+
// Return true if the user approved the transaction, false otherwise.
940+
match sync_ret {
941+
UX_SYNC_RET_APPROVED => {
942+
return true;
943+
}
944+
_ => {
945+
ux_sync_reviewStatus(self.tx_type.to_message(false));
946+
return false;
947+
}
948+
}
949+
}
950+
}
951+
952+
pub fn continue_review(&mut self, fields: &[Field]) -> bool {
953+
unsafe {
954+
let v: Vec<CField> = fields
955+
.iter()
956+
.map(|f| CField {
957+
name: CString::new(f.name).unwrap(),
958+
value: CString::new(f.value).unwrap(),
959+
})
960+
.collect();
961+
962+
// Fill the tag_value_array with the fields converted to nbgl_contentTagValue_t
963+
let mut tag_value_array: Vec<nbgl_contentTagValue_t> = Vec::new();
964+
for field in v.iter() {
965+
let val = nbgl_contentTagValue_t {
966+
item: field.name.as_ptr() as *const i8,
967+
value: field.value.as_ptr() as *const i8,
968+
..Default::default()
969+
};
970+
tag_value_array.push(val);
971+
}
972+
973+
// Create the tag_value_list with the tag_value_array.
974+
let tag_value_list = nbgl_contentTagValueList_t {
975+
pairs: tag_value_array.as_ptr() as *const nbgl_contentTagValue_t,
976+
nbPairs: fields.len() as u8,
977+
..Default::default()
978+
};
979+
980+
let sync_ret = ux_sync_reviewStreamingContinue(
981+
&tag_value_list as *const nbgl_contentTagValueList_t,
982+
);
983+
984+
// Return true if the user approved the transaction, false otherwise.
985+
match sync_ret {
986+
UX_SYNC_RET_APPROVED => {
987+
return true;
988+
}
989+
_ => {
990+
ux_sync_reviewStatus(self.tx_type.to_message(false));
991+
return false;
992+
}
993+
}
994+
}
995+
}
996+
997+
pub fn finish(&mut self, finish_title: &str) -> bool {
998+
unsafe {
999+
let finish_title = CString::new(finish_title).unwrap();
1000+
let sync_ret = ux_sync_reviewStreamingFinish(finish_title.as_ptr() as *const c_char);
1001+
1002+
// Return true if the user approved the transaction, false otherwise.
1003+
match sync_ret {
1004+
ledger_secure_sdk_sys::UX_SYNC_RET_APPROVED => {
1005+
ux_sync_reviewStatus(self.tx_type.to_message(true));
1006+
return true;
1007+
}
1008+
_ => {
1009+
ux_sync_reviewStatus(self.tx_type.to_message(false));
1010+
return false;
1011+
}
1012+
}
1013+
}
1014+
}
1015+
}
1016+
8241017
/// A wrapper around the synchronous NBGL ux_sync_addressReview C API binding.
8251018
/// Used to display address confirmation screens.
8261019
pub struct NbglAddressReview<'a> {

ledger_secure_sdk_sys/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "ledger_secure_sdk_sys"
3-
version = "1.4.2"
3+
version = "1.4.3"
44
authors = ["yhql", "agrojean-ledger"]
55
edition = "2021"
66
license.workspace = true

ledger_secure_sdk_sys/build.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ fn clone_sdk(device: &Device) -> PathBuf {
224224
),
225225
Device::Stax => (
226226
Path::new("https://github.com/LedgerHQ/ledger-secure-sdk"),
227-
"API_LEVEL_15",
227+
"API_LEVEL_21",
228228
),
229229
Device::Flex => (
230230
Path::new("https://github.com/LedgerHQ/ledger-secure-sdk"),

0 commit comments

Comments
 (0)