Skip to content

Commit

Permalink
Merge pull request #17 from LedgerHQ/y333_201224/appflags_hex
Browse files Browse the repository at this point in the history
Y333 201224/appflags hex
  • Loading branch information
yogh333 authored Jan 16, 2025
2 parents 5afed47 + 1771c38 commit 47f3a32
Show file tree
Hide file tree
Showing 10 changed files with 276 additions and 30 deletions.
18 changes: 18 additions & 0 deletions .github/workflows/swap_ci_workflow.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Swap functional tests

on:
workflow_dispatch:
push:
branches:
- master
- main
- develop
pull_request:

jobs:
job_functional_tests:
uses: LedgerHQ/app-exchange/.github/workflows/reusable_swap_functional_tests.yml@swap-near
with:
branch_for_near: ${{ github.ref }}
test_filter: '"NEAR or near or Near"'
branch_for_exchange: 'swap-near'
12 changes: 6 additions & 6 deletions Cargo.lock

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

13 changes: 7 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
[package]
name = "near"
version = "2.2.1"
version = "2.3.4"
authors = ["dj8yf0μl", "polyprogrammist"]
edition = "2021"

[dependencies]
ledger_device_sdk = "1.17.5"
ledger_device_sdk = "1.19.4"
include_gif = "1.2.0"
hex = { version = "0.4.3", default-features = false, features = ["serde"] }
bs58 = { version = "0.5.0", default-features = false }
Expand All @@ -22,13 +22,10 @@ lto = true

[package.metadata.ledger]
curve = ["ed25519"]
flags = "0"
flags = "0x800"
path = ["44'/397'"]
name = "NEAR"

[package.metadata.ledger.nanos]
icon = "icons/app_near_16px.gif"

[package.metadata.ledger.nanox]
icon = "icons/app_near_14px.gif"

Expand All @@ -43,4 +40,8 @@ icon = "icons/app_near_40px.gif"

[features]
default = []
debug = ["ledger_device_sdk/debug"]
speculos = ["ledger_device_sdk/speculos"]

[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(target_os, values("stax", "flex", "nanos", "nanox", "nanosplus"))'] }
3 changes: 0 additions & 3 deletions src/app_ui/fields_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@ pub struct FieldsWriter<'a, const N: usize> {
#[cfg(feature = "speculos")]
use ledger_device_sdk::testing;

#[derive(Debug)]
pub struct FieldsOverflow;

impl<'a, const N: usize> FieldsWriter<'a, N> {
pub fn new() -> Self {
let max_fields = [(); N].map(|_| Field {
Expand Down
74 changes: 74 additions & 0 deletions src/handlers/sign_tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,77 @@ pub fn handler(mut stream: SingleTxStream<'_>) -> Result<Signature, AppSW> {

finalize_sign::end(stream, &path)
}

use ledger_device_sdk::libcall::swap::CreateTxParams;

/// Sign handler for the swap transaction
/// This handler is called when the user wants to sign a swap transaction
/// The swap transaction is a transfer transaction with a specific amount and destination address
/// The handler checks the transaction parameters and signs the transaction
pub fn swap_handler(
mut stream: SingleTxStream<'_>,
tx_params: &CreateTxParams,
) -> Result<Signature, AppSW> {
ledger_device_sdk::testing::debug_print("sign_tx.rs: swap_handler()\n");

let path = <crypto::PathBip32 as BorshDeserialize>::deserialize_reader(&mut stream)
.map_err(|_| AppSW::Bip32PathParsingFail)?;

ledger_device_sdk::testing::debug_print("sign_tx.rs: path computed\n");

// Get the public key from the transaction
let mut stream = HashingStream::new(stream)?;
let mut tx_prefix = parsing::types::transaction::prefix::Prefix::new();
tx_prefix
.deserialize_reader_in_place(&mut stream)
.map_err(|_err| AppSW::TxParsingFail)?;
let tx_public_key = match PublicKeyBe::try_from(tx_prefix.public_key) {
Ok(tx_public_key) => tx_public_key,
Err(_) => return Err(AppSW::PublicKeyMismatch),
};

// Derive the public key from the path and compare it with the transaction public key
let dpath_public_key = {
let pk = ledger_device_sdk::ecc::Ed25519::derive_from_path_slip10(&path.0)
.public_key()
.map_err(|_| AppSW::KeyDeriveFail)?;
PublicKeyBe::from_little_endian(pk)
};

if tx_public_key != dpath_public_key {
return Err(AppSW::PublicKeyMismatch);
}

// Check nb of actions (shall be == 1 == Transfer in swap context)
if tx_prefix.number_of_actions != 1 {
return Err(AppSW::TxSignFail);
}
let action = crate::parsing::types::Action::deserialize_reader(&mut stream)
.map_err(|_err| AppSW::TxParsingFail)?;
if action != crate::parsing::types::Action::Transfer {
return Err(AppSW::TxSignFail);
}

// Check the tx parameters match with the ones previously validated in Exchange app (tx_params)
let transfer = crate::parsing::types::Transfer::deserialize_reader(&mut stream)
.map_err(|_err| AppSW::TxParsingFail)?;

let amount_match = near_token::NearToken::from_yoctonear(u128::from_be_bytes(tx_params.amount))
== transfer.deposit;
if !amount_match {
ledger_device_sdk::testing::debug_print("sign_tx.rs: amounts do not not match\n");
return Err(AppSW::TxSignFail);
}

let dest_address_match = tx_prefix.receiver_id.as_str()
== core::str::from_utf8(tx_params.dest_address[..tx_params.dest_address_len].as_ref())
.unwrap();
if !dest_address_match {
ledger_device_sdk::testing::debug_print(
"sign_tx.rs: receiver_id does not match with dest_address\n",
);
return Err(AppSW::TxSignFail);
}

finalize_sign::end(stream, &path)
}
30 changes: 19 additions & 11 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,20 +238,28 @@ impl TryFrom<ApduHeader> for Instruction {
#[cfg(any(target_os = "stax", target_os = "flex"))]
use ledger_device_sdk::nbgl::init_comm;

mod swap;

#[no_mangle]
extern "C" fn sample_main() {
let mut comm = Comm::new();
extern "C" fn sample_main(arg0: u32) {
if arg0 != 0 {
swap::swap_main(arg0);
} else {
ledger_device_sdk::testing::debug_print("call app-near as a standalone\n");

let mut comm = Comm::new();

#[cfg(any(target_os = "stax", target_os = "flex"))]
init_comm(&mut comm);
#[cfg(any(target_os = "stax", target_os = "flex"))]
init_comm(&mut comm);

loop {
// Wait for either a specific button push to exit the app
// or an APDU command
if let Event::Command(ins) = ui_menu_main(&mut comm) {
match handle_apdu(&mut comm, ins) {
Ok(()) => comm.reply_ok(),
Err(sw) => comm.reply(sw),
loop {
// Wait for either a specific button push to exit the app
// or an APDU command
if let Event::Command(ins) = ui_menu_main(&mut comm) {
match handle_apdu(&mut comm, ins) {
Ok(()) => comm.reply_ok(),
Err(sw) => comm.reply(sw),
}
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/parsing/transaction_stream_reader/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ impl<R: io::Read> io::Read for HashingStream<R> {
}
}

impl<'a> SingleTxStream<'a> {
impl SingleTxStream<'_> {
pub fn peek_u8(&mut self) -> io::Result<Option<u8>> {
let data = self
.comm
Expand Down Expand Up @@ -132,7 +132,7 @@ impl<'a> SingleTxStream<'a> {
}
}

impl<'a> io::Read for SingleTxStream<'a> {
impl io::Read for SingleTxStream<'_> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let data = self
.comm
Expand Down
1 change: 1 addition & 0 deletions src/parsing/types/common/action/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub mod function_call;
pub mod stake;
pub mod transfer;

#[derive(PartialEq)]
pub enum Action {
CreateAccount,
DeployContract,
Expand Down
142 changes: 142 additions & 0 deletions src/swap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
use crate::utils::crypto::{PathBip32, PublicKeyBe};
use fmt_buffer::Buffer;
use near_gas::{GasBuffer, NearGas};
use near_token::{NearToken, TokenBuffer};

use ledger_device_sdk::{
ecc,
io::Comm,
libcall::{
self,
swap::{self, CheckAddressParams, CreateTxParams, PrintableAmountParams},
},
testing::debug_print,
};

use crate::parsing::transaction_stream_reader::SingleTxStream;

pub fn swap_main(arg0: u32) {
debug_print("call app for swap \n");

let cmd = libcall::get_command(arg0);

match cmd {
libcall::LibCallCommand::SwapCheckAddress => {
let mut params = swap::get_check_address_params(arg0);
let res = match check_address(&params) {
Ok(_) => 1,
Err(err) => {
debug_print(err);
0
}
};
swap::swap_return(swap::SwapResult::CheckAddressResult(&mut params, res));
}
libcall::LibCallCommand::SwapGetPrintableAmount => {
let mut params = swap::get_printable_amount_params(arg0);
let mut s = get_printable_amount(&params);
swap::swap_return(swap::SwapResult::PrintableAmountResult(
&mut params,
s.as_str(),
));
}
libcall::LibCallCommand::SwapSignTransaction => {
let mut params = swap::sign_tx_params(arg0);

{
let mut comm = Comm::new().set_expected_cla(super::CLA);

debug_print("Wait for APDU\n");

loop {
// Wait for an APDU command
let ins: super::Instruction = comm.next_command();

debug_print("APDU received\n");

swap_handle_apdu(&mut comm, ins, &mut params);
}
}
}
}
}

fn swap_handle_apdu(comm: &mut Comm, ins: super::Instruction, tx_params: &mut CreateTxParams) {
match ins {
super::Instruction::SignTx {
is_last_chunk,
sign_mode,
} => {
debug_print("handle_swap_apdu => Sign Tx\n");
let stream = SingleTxStream::new(comm, is_last_chunk, sign_mode);
match sign_mode {
super::SignMode::Transaction => {
let signature = crate::handlers::sign_tx::swap_handler(stream, tx_params);
match signature {
Ok(sig) => {
comm.append(&sig.0);
comm.swap_reply_ok();
swap::swap_return(swap::SwapResult::CreateTxResult(tx_params, 1));
}
Err(sw) => {
comm.swap_reply(sw);
swap::swap_return(swap::SwapResult::CreateTxResult(tx_params, 0));
}
}
}
_ => {
comm.swap_reply(crate::AppSW::TxSignFail);
swap::swap_return(swap::SwapResult::CreateTxResult(tx_params, 0));
}
}
}
super::Instruction::GetPubkey { display } => match display {
true => comm.swap_reply(crate::AppSW::InsNotSupported),
false => match crate::handlers::get_public_key::handler(comm, display) {
Ok(()) => comm.swap_reply_ok(),
Err(sw) => comm.swap_reply(sw),
},
},
_ => comm.swap_reply(crate::AppSW::InsNotSupported),
}
}

fn check_address(params: &CheckAddressParams) -> Result<(), &'static str> {
let path = PathBip32::parse(&params.dpath[..params.dpath_len * 4])
.map_err(|_| "Derivation path failure")?;

let pk = ecc::Ed25519::derive_from_path_slip10(&path.0)
.public_key()
.map_err(|_| "Public key derivation failure")?;

let pk = PublicKeyBe::from_little_endian(pk);
let mut buf = [0u8; 64];
let address = pk.display_str_hex(&mut buf);

let ref_address = core::str::from_utf8(&params.ref_address[..params.ref_address_len])
.map_err(|_| "Invalid UTF-8 in reference address")?;

if address == ref_address {
Ok(())
} else {
Err("Address mismatch")
}
}

fn get_printable_amount(params: &PrintableAmountParams) -> Buffer<30> {
match params.is_fee {
true => {
let gas = NearGas::from_gas(450_000_000_000);
let mut near_gas_buffer = GasBuffer::new();
gas.display_as_buffer(&mut near_gas_buffer);
near_gas_buffer
}
false => {
let amount = u128::from_be_bytes(params.amount);
let near_token = NearToken::from_yoctonear(amount);
let mut near_token_buffer = TokenBuffer::new();
near_token.display_as_buffer(&mut near_token_buffer);
near_token_buffer
}
}
}
Loading

0 comments on commit 47f3a32

Please sign in to comment.