Skip to content

Commit 96fa745

Browse files
committed
Amount now as coin string and u64
1 parent 8a16637 commit 96fa745

File tree

8 files changed

+77
-51
lines changed

8 files changed

+77
-51
lines changed

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.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ include_gif = "1.0.0"
1212
serde = {version="1.0.192", default_features = false, features = ["derive"]}
1313
serde-json-core = { git = "https://github.com/rust-embedded-community/serde-json-core"}
1414
hex = { version = "0.4.3", default-features = false, features = ["serde"] }
15+
numtoa = "0.2.4"
1516

1617
[profile.release]
1718
opt-level = 'z'

src/app_ui/sign.rs

Lines changed: 51 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -15,42 +15,60 @@
1515
* limitations under the License.
1616
*****************************************************************************/
1717

18+
use core::str::from_utf8;
19+
1820
use crate::handlers::sign_tx::Tx;
21+
use crate::utils::concatenate;
1922
use ledger_device_ui_sdk::bitmaps::{CROSSMARK, EYE, VALIDATE_14};
2023
use ledger_device_ui_sdk::ui::{Field, MultiFieldReview};
24+
use numtoa::NumToA;
25+
26+
const MAX_COIN_LENGTH: usize = 10;
2127

2228
pub fn ui_display_tx(tx: &Tx) -> bool {
23-
// Generate destination address string in hexadecimal format.
24-
let mut to_str = [0u8; 42];
25-
to_str[..2].copy_from_slice("0x".as_bytes());
26-
hex::encode_to_slice(tx.to, &mut to_str[2..]).unwrap();
27-
28-
// Define transaction review fields
29-
let my_fields = [
30-
Field {
31-
name: "Amount",
32-
value: tx.value,
33-
},
34-
Field {
35-
name: "Destination",
36-
value: core::str::from_utf8(&to_str).unwrap(),
37-
},
38-
Field {
39-
name: "Memo",
40-
value: tx.memo,
41-
},
42-
];
43-
44-
// Create transaction review
45-
let my_review = MultiFieldReview::new(
46-
&my_fields,
47-
&["Review ", "Transaction"],
48-
Some(&EYE),
49-
"Approve",
50-
Some(&VALIDATE_14),
51-
"Reject",
52-
Some(&CROSSMARK),
53-
);
54-
55-
my_review.show()
29+
// Generate string for amount
30+
let mut numtoa_buf = [0u8; 20];
31+
let mut value_buf = [0u8; 20 + MAX_COIN_LENGTH + 1];
32+
33+
if let Ok(value_str) = concatenate(
34+
&[tx.coin, &" ", tx.value.numtoa_str(10, &mut numtoa_buf)],
35+
&mut value_buf,
36+
) {
37+
// Generate destination address string in hexadecimal format.
38+
let mut to_str = [0u8; 42];
39+
to_str[..2].copy_from_slice("0x".as_bytes());
40+
hex::encode_to_slice(tx.to, &mut to_str[2..]).unwrap();
41+
42+
// Define transaction review fields
43+
let my_fields = [
44+
Field {
45+
name: "Amount",
46+
value: value_str,
47+
},
48+
Field {
49+
name: "Destination",
50+
value: core::str::from_utf8(&to_str).unwrap(),
51+
},
52+
Field {
53+
name: "Memo",
54+
value: tx.memo,
55+
},
56+
];
57+
58+
// Create transaction review
59+
let my_review = MultiFieldReview::new(
60+
&my_fields,
61+
&["Review ", "Transaction"],
62+
Some(&EYE),
63+
"Approve",
64+
Some(&VALIDATE_14),
65+
"Reject",
66+
Some(&CROSSMARK),
67+
);
68+
69+
my_review.show()
70+
} else {
71+
// Coin name too long, concatenation buffer was too small.
72+
return false;
73+
}
5674
}

src/handlers/sign_tx.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ const MAX_TRANSACTION_LEN: usize = 510;
3131
#[derive(Deserialize)]
3232
pub struct Tx<'a> {
3333
nonce: u64,
34-
pub value: &'a str,
35-
#[serde(with = "hex::serde")]
34+
pub coin: &'a str,
35+
pub value: u64,
36+
#[serde(with = "hex::serde")] // Allows JSON deserialization from hex string
3637
pub to: [u8; 20],
3738
pub memo: &'a str,
3839
}

src/utils.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::AppSW;
2-
use core::char;
2+
use core::{char, str::from_utf8};
33

44
pub const MAX_ALLOWED_PATH_LEN: usize = 10;
55
const MAX_HEX_LEN: usize = 64;
@@ -31,20 +31,19 @@ pub fn read_bip32_path(data: &[u8], path: &mut [u32]) -> Result<usize, AppSW> {
3131
Ok(idx)
3232
}
3333

34-
/// Concatenate multiple strings into a fixed-size array
35-
pub fn concatenate(strings: &[&str], output: &mut [u8]) {
34+
/// Returns concatenated strings, or an error if the concatenation buffer is too small.
35+
pub fn concatenate<'a>(strings: &[&str], output: &'a mut [u8]) -> Result<&'a str, ()> {
3636
let mut offset = 0;
3737

3838
for s in strings {
3939
let s_len = s.len();
40-
let copy_len = core::cmp::min(s_len, output.len() - offset);
41-
42-
if copy_len > 0 {
43-
output[offset..offset + copy_len].copy_from_slice(&s.as_bytes()[..copy_len]);
44-
offset += copy_len;
45-
} else {
46-
// If the output buffer is full, stop concatenating.
47-
break;
40+
if offset + s_len > output.len() {
41+
return Err(());
4842
}
43+
44+
output[offset..offset + s_len].copy_from_slice(&s.as_bytes());
45+
offset += s_len;
4946
}
47+
48+
Ok(from_utf8(&output[..offset]).unwrap())
5049
}

tests/application_client/boilerplate_transaction.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ class TransactionError(Exception):
1111
class Transaction:
1212
def __init__(self,
1313
nonce: int,
14-
value: float,
14+
coin: str,
15+
value: int,
1516
to: str,
1617
memo: str,
1718
do_check: bool = True) -> None:
1819
self.nonce: int = nonce
19-
self.value: str = "CRAB " + str(value)
20+
self.coin: str = coin
21+
self.value: str = value
2022
self.to: str = to
2123
self.memo: str = memo
2224

@@ -31,6 +33,7 @@ def serialize(self) -> bytes:
3133
# Serialize the transaction data to a JSON-formatted string
3234
return json.dumps({
3335
"nonce": self.nonce,
36+
"coin": self.coin,
3437
"value": self.value,
3538
"to": self.to,
3639
"memo": self.memo
Loading

tests/test_sign_cmd.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ def test_sign_tx_short_tx(firmware, backend, navigator, test_name):
2626
# Create the transaction that will be sent to the device for signing
2727
transaction = Transaction(
2828
nonce=1,
29-
value=0.777,
29+
coin="CRAB",
30+
value=777,
3031
to="de0b295669a9fd93d5f28d9ec85e40f4cb697bae",
3132
memo="For u EthDev"
3233
).serialize()
@@ -70,8 +71,9 @@ def test_sign_tx_long_tx(firmware, backend, navigator, test_name):
7071

7172
transaction = Transaction(
7273
nonce=1,
73-
to="de0b295669a9fd93d5f28d9ec85e40f4cb697bae",
74+
coin="CRAB",
7475
value=666,
76+
to="de0b295669a9fd93d5f28d9ec85e40f4cb697bae",
7577
memo=("This is a very long memo. "
7678
"It will force the app client to send the serialized transaction to be sent in chunk. "
7779
"As the maximum chunk size is 255 bytes we will make this memo greater than 255 characters. "
@@ -109,8 +111,9 @@ def test_sign_tx_refused(firmware, backend, navigator, test_name):
109111

110112
transaction = Transaction(
111113
nonce=1,
112-
to="de0b295669a9fd93d5f28d9ec85e40f4cb697bae",
114+
coin="CRAB",
113115
value=666,
116+
to="de0b295669a9fd93d5f28d9ec85e40f4cb697bae",
114117
memo="This transaction will be refused by the user"
115118
).serialize()
116119

0 commit comments

Comments
 (0)