Skip to content

Commit f7cbff0

Browse files
authored
Merge pull request #297 from Zondax/feat/intent
Add intent on review screen
2 parents 40ed24a + e5e92c3 commit f7cbff0

File tree

32 files changed

+80
-27
lines changed

32 files changed

+80
-27
lines changed

app/Makefile.version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ APPVERSION_M=4
33
# This is the minor version of this release
44
APPVERSION_N=0
55
# This is the patch version of this release
6-
APPVERSION_P=7
6+
APPVERSION_P=8

app/rust/include/rslib.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,7 @@ parser_error_t rs_getItem(int8_t displayIdx, char *outKey, uint16_t outKeyLen, c
2727
// use to clear resources after certificate verification and signing
2828
void rs_clear_resources(void);
2929
void rs_get_signing_hash(uint8_t *hash);
30+
31+
// Get the intent string from the parsed consent message
32+
parser_error_t rs_get_intent(char *out_intent, uint16_t intent_len);
3033
#endif

app/rust/src/ffi/ui.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,34 @@ pub unsafe extern "C" fn rs_getItem(
6666
Err(_) => ParserError::NoData as _,
6767
}
6868
}
69+
70+
#[no_mangle]
71+
pub unsafe extern "C" fn rs_get_intent(out_intent: *mut i8, intent_len: u16) -> u32 {
72+
if out_intent.is_null() || intent_len == 0 {
73+
return ParserError::NoData as u32;
74+
}
75+
76+
// Clear the output buffer first
77+
let out_slice = core::slice::from_raw_parts_mut(out_intent as *mut u8, intent_len as usize);
78+
out_slice[0] = 0;
79+
80+
if !UI.is_some() {
81+
return ParserError::ContextMismatch as u32;
82+
}
83+
84+
// Safe to unwrap due to previous check
85+
let ui = UI.as_ref().unwrap();
86+
87+
// Access the intent using the public method
88+
if let Some(intent) = ui.message.get_intent() {
89+
let intent_bytes = intent.as_bytes();
90+
let copy_len = core::cmp::min(intent_bytes.len(), intent_len as usize - 1);
91+
92+
out_slice[..copy_len].copy_from_slice(&intent_bytes[..copy_len]);
93+
out_slice[copy_len] = 0; // Null terminate
94+
95+
return ParserError::Ok as u32;
96+
}
97+
98+
ParserError::NoData as u32
99+
}

app/rust/src/parser/certificate/cert.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -423,11 +423,10 @@ mod test_certificate {
423423
let msg = &ui.message;
424424

425425
// Test number of items (should be 4 fields)
426-
assert_eq!(msg.num_items().unwrap(), 5);
426+
assert_eq!(msg.num_items().unwrap(), 4);
427427

428428
// Expected field pairs from the encoded data
429429
let expected_fields = [
430-
("Transaction Type", "Greet user"),
431430
("User", "Hello, world!"),
432431
("created_at", "2025-07-11 07:27:44"),
433432
("active_for", "10 minutes"),

app/rust/src/parser/consent_message/msg.rs

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,15 @@ pub struct Msg<'a> {
6565
msg: ConsentMessage<'a>,
6666
}
6767

68+
impl<'a> Msg<'a> {
69+
pub fn get_intent(&self) -> Option<&'a str> {
70+
match &self.msg {
71+
ConsentMessage::FieldsDisplayMessage { intent, .. } => Some(intent),
72+
_ => None,
73+
}
74+
}
75+
}
76+
6877
#[repr(u8)] // Important: same representation as MessageType
6978
#[cfg_attr(any(feature = "derive-debug", test), derive(Debug))]
7079
pub enum ConsentMessage<'a> {
@@ -111,8 +120,8 @@ impl<'a> FromCandidHeader<'a> for Msg<'a> {
111120
let m = consent_msg.assume_init_ref();
112121
match m {
113122
ConsentMessage::FieldsDisplayMessage { field_count, .. } => {
114-
// Add 1 to field_count to include intent as item 0
115-
addr_of_mut!((*out).num_items).write(*field_count + 1);
123+
// Don't include intent in the count
124+
addr_of_mut!((*out).num_items).write(*field_count);
116125
}
117126
ConsentMessage::GenericDisplayMessage(_) => {
118127
// Do not accept generic messages
@@ -351,7 +360,7 @@ impl DisplayableItem for ConsentMessage<'_> {
351360
fn num_items(&self) -> Result<u8, ViewError> {
352361
check_canary();
353362
match self {
354-
ConsentMessage::FieldsDisplayMessage { field_count, .. } => Ok(*field_count + 1), // +1 for intent
363+
ConsentMessage::FieldsDisplayMessage { field_count, .. } => Ok(*field_count), // intent not included in count
355364
ConsentMessage::GenericDisplayMessage(_) => Ok(1),
356365
}
357366
}
@@ -370,30 +379,17 @@ impl DisplayableItem for ConsentMessage<'_> {
370379
ConsentMessage::FieldsDisplayMessage {
371380
fields,
372381
field_count,
373-
intent,
382+
intent: _,
374383
} => {
375-
// Item 0 is the intent
376-
if item_n == 0 {
377-
let title_text = b"Transaction Type";
378-
let title_len = title_text.len().min(title.len() - 1);
379-
title[..title_len].copy_from_slice(&title_text[..title_len]);
380-
title[title_len] = 0;
381-
382-
// Set message to the intent text
383-
return handle_ui_message(intent.as_bytes(), message, page);
384-
}
385-
386-
// Adjust item_n for field access (subtract 1 since intent is item 0)
387-
let field_index = item_n - 1;
388-
389-
if field_index >= *field_count {
384+
// Intent is no longer displayed as an item
385+
if item_n >= *field_count {
390386
return Err(ViewError::NoData);
391387
}
392388

393389
let mut current = *fields;
394390

395391
// Skip to the desired field
396-
for _ in 0..field_index {
392+
for _ in 0..item_n {
397393
// Skip key
398394
let (rem, _) = parse_text(current).map_err(|_| ViewError::NoData)?;
399395
// Skip value - need to properly skip the variant

app/rust/src/parser/consent_message/snapshots/rslib__parser__consent_message__msg_response__msg_response_test__ui.snap

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ input_file: src/parser/consent_message/testvectors/ui_data.json
66
snapshot_kind: text
77
---
88
[
9-
"Transaction Type": "Greet user",
109
"User": "Hello, world!",
1110
"created_at": "2025-07-11 07:27:44",
1211
"active_for": "10 minutes",

app/src/candid/candid_types.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,6 @@ typedef struct {
385385
candid_RegisterVote_t vote;
386386
candid_Follow_t follow;
387387
candid_RefreshVotingPower_t refresh_voting_power;
388-
// candid_SetVisibility_t set_visibility;
389388

390389
sns_NeuronPermissions_t neuronPermissions;
391390
sns_Disburse_t sns_disburse;

app/src/common/parser.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ parser_error_t parser_certNumItems(uint8_t *num_items);
4949

5050
parser_error_t parser_certGetItem(uint8_t displayIdx, char *outKey, uint16_t outKeyLen, char *outVal, uint16_t outValLen,
5151
uint8_t pageIdx, uint8_t *pageCount);
52+
53+
parser_error_t parser_getIntent(char *outIntent, uint16_t intentLen);
5254
#endif
5355

5456
#ifdef __cplusplus

app/src/handlers/handle_bls.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "coin.h"
3232
#include "crypto.h"
3333
#include "nvdata.h"
34+
#include "parser.h"
3435
#include "parser_impl.h"
3536
#include "path.h"
3637
#include "process_chunks.h"
@@ -39,6 +40,9 @@
3940
#include "view_internal.h"
4041
#include "zxmacros.h"
4142

43+
// Static buffer to hold intent string for async UX flow
44+
static char G_intent_buffer[64];
45+
4246
__Z_INLINE void handleConsentRequest(__unused volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) {
4347
zemu_log_stack("handleConsentRequest");
4448
if (!process_chunk(tx, rx)) {
@@ -98,7 +102,20 @@ __Z_INLINE void handleSignBls(volatile uint32_t *flags, volatile uint32_t *tx, u
98102

99103
CHECK_APP_CANARY()
100104
view_review_init(tx_certGetItem, tx_certNumItems, app_sign_bls);
101-
view_review_show(REVIEW_TXN);
105+
106+
// Try to get the intent from the parsed consent message
107+
// Clear the global buffer first
108+
MEMZERO(G_intent_buffer, sizeof(G_intent_buffer));
109+
parser_error_t intent_err = parser_getIntent(G_intent_buffer, sizeof(G_intent_buffer));
110+
111+
if (intent_err == parser_ok && strlen(G_intent_buffer) > 0) {
112+
// Use the intent from the parsed message (from global buffer)
113+
view_review_show_with_intent(REVIEW_TXN, G_intent_buffer);
114+
} else {
115+
// Fallback to generic message if no intent available
116+
view_review_show(REVIEW_TXN);
117+
}
118+
102119
*flags |= IO_ASYNCH_REPLY;
103120
}
104121
#endif

app/src/parser.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,4 +249,11 @@ parser_error_t parser_certGetItem(uint8_t displayIdx, char *outKey, uint16_t out
249249
*pageCount = 1;
250250
return rs_getItem(displayIdx, outKey, outKeyLen, outVal, outValLen, pageIdx, pageCount);
251251
}
252+
253+
parser_error_t parser_getIntent(char *outIntent, uint16_t intentLen) {
254+
if (outIntent == NULL || intentLen == 0) {
255+
return parser_no_data;
256+
}
257+
return rs_get_intent(outIntent, intentLen);
258+
}
252259
#endif

0 commit comments

Comments
 (0)