Skip to content

Commit

Permalink
Amount now as coin string and u64
Browse files Browse the repository at this point in the history
  • Loading branch information
kingofpayne committed Nov 22, 2023
1 parent 8a16637 commit 96fa745
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 51 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ include_gif = "1.0.0"
serde = {version="1.0.192", default_features = false, features = ["derive"]}
serde-json-core = { git = "https://github.com/rust-embedded-community/serde-json-core"}
hex = { version = "0.4.3", default-features = false, features = ["serde"] }
numtoa = "0.2.4"

[profile.release]
opt-level = 'z'
Expand Down
84 changes: 51 additions & 33 deletions src/app_ui/sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,42 +15,60 @@
* limitations under the License.
*****************************************************************************/

use core::str::from_utf8;

use crate::handlers::sign_tx::Tx;
use crate::utils::concatenate;
use ledger_device_ui_sdk::bitmaps::{CROSSMARK, EYE, VALIDATE_14};
use ledger_device_ui_sdk::ui::{Field, MultiFieldReview};
use numtoa::NumToA;

const MAX_COIN_LENGTH: usize = 10;

pub fn ui_display_tx(tx: &Tx) -> bool {
// Generate destination address string in hexadecimal format.
let mut to_str = [0u8; 42];
to_str[..2].copy_from_slice("0x".as_bytes());
hex::encode_to_slice(tx.to, &mut to_str[2..]).unwrap();

// Define transaction review fields
let my_fields = [
Field {
name: "Amount",
value: tx.value,
},
Field {
name: "Destination",
value: core::str::from_utf8(&to_str).unwrap(),
},
Field {
name: "Memo",
value: tx.memo,
},
];

// Create transaction review
let my_review = MultiFieldReview::new(
&my_fields,
&["Review ", "Transaction"],
Some(&EYE),
"Approve",
Some(&VALIDATE_14),
"Reject",
Some(&CROSSMARK),
);

my_review.show()
// Generate string for amount
let mut numtoa_buf = [0u8; 20];
let mut value_buf = [0u8; 20 + MAX_COIN_LENGTH + 1];

if let Ok(value_str) = concatenate(
&[tx.coin, &" ", tx.value.numtoa_str(10, &mut numtoa_buf)],
&mut value_buf,
) {
// Generate destination address string in hexadecimal format.
let mut to_str = [0u8; 42];
to_str[..2].copy_from_slice("0x".as_bytes());
hex::encode_to_slice(tx.to, &mut to_str[2..]).unwrap();

// Define transaction review fields
let my_fields = [
Field {
name: "Amount",
value: value_str,
},
Field {
name: "Destination",
value: core::str::from_utf8(&to_str).unwrap(),
},
Field {
name: "Memo",
value: tx.memo,
},
];

// Create transaction review
let my_review = MultiFieldReview::new(
&my_fields,
&["Review ", "Transaction"],
Some(&EYE),
"Approve",
Some(&VALIDATE_14),
"Reject",
Some(&CROSSMARK),
);

my_review.show()
} else {
// Coin name too long, concatenation buffer was too small.
return false;
}
}
5 changes: 3 additions & 2 deletions src/handlers/sign_tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ const MAX_TRANSACTION_LEN: usize = 510;
#[derive(Deserialize)]
pub struct Tx<'a> {
nonce: u64,
pub value: &'a str,
#[serde(with = "hex::serde")]
pub coin: &'a str,
pub value: u64,
#[serde(with = "hex::serde")] // Allows JSON deserialization from hex string
pub to: [u8; 20],
pub memo: &'a str,
}
Expand Down
21 changes: 10 additions & 11 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::AppSW;
use core::char;
use core::{char, str::from_utf8};

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

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

for s in strings {
let s_len = s.len();
let copy_len = core::cmp::min(s_len, output.len() - offset);

if copy_len > 0 {
output[offset..offset + copy_len].copy_from_slice(&s.as_bytes()[..copy_len]);
offset += copy_len;
} else {
// If the output buffer is full, stop concatenating.
break;
if offset + s_len > output.len() {
return Err(());
}

output[offset..offset + s_len].copy_from_slice(&s.as_bytes());
offset += s_len;
}

Ok(from_utf8(&output[..offset]).unwrap())
}
7 changes: 5 additions & 2 deletions tests/application_client/boilerplate_transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ class TransactionError(Exception):
class Transaction:
def __init__(self,
nonce: int,
value: float,
coin: str,
value: int,
to: str,
memo: str,
do_check: bool = True) -> None:
self.nonce: int = nonce
self.value: str = "CRAB " + str(value)
self.coin: str = coin
self.value: str = value
self.to: str = to
self.memo: str = memo

Expand All @@ -31,6 +33,7 @@ def serialize(self) -> bytes:
# Serialize the transaction data to a JSON-formatted string
return json.dumps({
"nonce": self.nonce,
"coin": self.coin,
"value": self.value,
"to": self.to,
"memo": self.memo
Expand Down
Binary file modified tests/snapshots/nanosp/test_sign_tx_short_tx/00001.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 6 additions & 3 deletions tests/test_sign_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ def test_sign_tx_short_tx(firmware, backend, navigator, test_name):
# Create the transaction that will be sent to the device for signing
transaction = Transaction(
nonce=1,
value=0.777,
coin="CRAB",
value=777,
to="de0b295669a9fd93d5f28d9ec85e40f4cb697bae",
memo="For u EthDev"
).serialize()
Expand Down Expand Up @@ -70,8 +71,9 @@ def test_sign_tx_long_tx(firmware, backend, navigator, test_name):

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

transaction = Transaction(
nonce=1,
to="de0b295669a9fd93d5f28d9ec85e40f4cb697bae",
coin="CRAB",
value=666,
to="de0b295669a9fd93d5f28d9ec85e40f4cb697bae",
memo="This transaction will be refused by the user"
).serialize()

Expand Down

0 comments on commit 96fa745

Please sign in to comment.