What changed, and why it matters
This commit is a code review and cleanup pass for the Avalanche (AVAX) app in the Keystone 3 hardware wallet firmware. It fixes typos in transaction type names, adds a few length checks before reading transaction bytes, replaces direct low-level signing with a safer helper, and consolidates signing code so AVAX, IOTA, and SUI share the same secure path. Most changes are defensive hardening and refactoring rather than a single obvious critical bug fix.
Treat as a routine hardening/refactoring commit. Reviewers should verify that the new bounds checks cover all parser entry points, that sign_message_by_seed handles low-S and recoverable ID correctly, and that the shared SignInternal helper is adopted consistently across other chains. No immediate incident response is indicated by the diff alone.
Security signals we found
Bounds check added before advancing transaction bytes in get_avax_tx_type_id
Bounds check added before splitting memo bytes in BaseTx parsing
Manual secp256k1 signing replaced by keystore helper
Seed buffer now explicitly zeroized after signing via memset_s
Shared SignInternal helper checks seed retrieval return code and clears secret cache
Multiple typos in transaction type/module names corrected
Evidence from the diff
The diff touches 25 files in the Rust AVAX parser and C UI layers. Notable changes: (1) renames misspelled transaction types AddPermissLessionValidator/Delegator to AddPermissionlessValidator/Delegator and updates module paths from C_chain/P_chain to c_chain/p_chain; (2) adds bounds checks in get_avax_tx_type_id (>=6 bytes) and BaseTx memo parsing (remaining >= memo_len); (3) replaces manual Secp256k1 signing in avax_base_sign with keystore::algorithms::secp256k1::sign_message_by_seed; (4) refactors AVAX, IOTA, and SUI UI signing to use a shared SignInternal helper that checks GetAccountSeed return value, zeroizes the seed buffer with memset_s, and clears the secret cache; (5) removes an unused PsbtSignStatus struct and cleans up clippy warnings. The commit message is only “avax review” and provides no security framing.
Changed components
rust/apps/avalanche transaction parserrust/rust_c/src/avalanche FFI bindingssrc/ui/gui_chain/gui_chain.c/h shared signing infrastructuresrc/ui/gui_chain/multi/web3/gui_avax.csrc/ui/gui_chain/multi/web3/gui_iota.csrc/ui/gui_chain/multi/web3/gui_sui.cInspect captured patch +189 / −223
diff --git a/rust/apps/avalanche/src/address.rs b/rust/apps/avalanche/src/address.rs
index d923499..f6e2370 100644
--- a/rust/apps/avalanche/src/address.rs
+++ b/rust/apps/avalanche/src/address.rs
@@ -18,17 +18,18 @@ pub struct Address {
impl ParsedSizeAble for Address {
fn parsed_size(&self) -> usize {
- ADDRESS_LEN as usize
+ ADDRESS_LEN
}
}
impl Address {
pub fn encode(&self) -> String {
- bech32::encode::<Bech32>(bech32::Hrp::parse_unchecked("avax"), &self.address).unwrap()
+ bech32::encode::<Bech32>(bech32::Hrp::parse_unchecked("avax"), &self.address)
+ .expect("bech32 encode should not fail for constant HRP and 20 bytes")
}
pub fn to_evm_address(&self) -> String {
- format!("0x{}", hex::encode(&self.address))
+ format!("0x{}", hex::encode(self.address))
}
}
@@ -48,17 +49,14 @@ pub fn get_address(
root_x_pub: &str,
root_path: &str,
) -> Result<String> {
- let prefix = "avax";
- match network {
- Network::AvaxMainNet => {}
+ let hrp = match network {
+ Network::AvaxMainNet => "avax",
#[cfg(feature = "testnet")]
- Network::AvaxTestNet => {
- prefix = "fuji";
- }
+ Network::AvaxTestNet => "fuji",
_ => {
- return Err(AvaxError::UnsupportedNetwork(format!("{:?}", network)));
+ return Err(AvaxError::UnsupportedNetwork(format!("{network:?}")));
}
- }
+ };
let root_path = if !root_path.ends_with('/') {
root_path.to_string() + "/"
@@ -75,19 +73,17 @@ pub fn get_address(
.ok_or(AvaxError::InvalidHDPath(hd_path.to_string()))?
),
)
- .map_err(|e| AvaxError::InvalidHex(format!("derive public key error: {}", e)))?;
+ .map_err(|e| AvaxError::InvalidHex(format!("derive public key error: {e}")))?;
bech32::encode::<Bech32>(
- bech32::Hrp::parse_unchecked(prefix),
+ bech32::Hrp::parse_unchecked(hrp),
&hash160(&public_key.serialize()),
)
- .map_err(|e| AvaxError::InvalidHex(format!("bech32 encode error: {}", e)))
+ .map_err(|e| AvaxError::InvalidHex(format!("bech32 encode error: {e}")))
}
#[cfg(test)]
mod tests {
use super::*;
- use alloc::string::ToString;
- extern crate std;
#[test]
fn get_avax_address_test() {
@@ -95,7 +91,7 @@ mod tests {
let hd_path = "m/44'/9000'/0'/0/0";
let root_x_pub = "xpub6CPE4bhTujy9CeJJbyskjJsp8FGgyWBsWV2W9GfZwuP9aeDBEoPRBsLk3agq32Gp5gkb9nJSjCn9fgZmuvmV3nPLk5Bc2wfKUQZREp4eG13";
let root_path = "m/44'/9000'/0'";
- let address = get_address(Network::AvaxMainNet, &hd_path, &root_x_pub, &root_path);
+ let address = get_address(Network::AvaxMainNet, hd_path, root_x_pub, root_path);
assert_eq!(
address.unwrap(),
"avax1fmlmwakmgkezcg95lk97m8p3tgc9anuxemenwh"
diff --git a/rust/apps/avalanche/src/encode/cb58.rs b/rust/apps/avalanche/src/encode/cb58.rs
index 971cff1..f6a6a87 100644
--- a/rust/apps/avalanche/src/encode/cb58.rs
+++ b/rust/apps/avalanche/src/encode/cb58.rs
@@ -1,5 +1,4 @@
use alloc::string::String;
-use alloc::string::ToString;
use bitcoin::base58;
use cryptoxide::digest::Digest;
use cryptoxide::sha2::Sha256;
@@ -21,7 +20,7 @@ pub trait Cb58Encodable {
format!(
"{}-{}",
self.get_prefix(),
- base58::encode(&with_checksum).to_string()
+ base58::encode(&with_checksum)
)
}
}
diff --git a/rust/apps/avalanche/src/errors.rs b/rust/apps/avalanche/src/errors.rs
index 848f2fd..e531836 100644
--- a/rust/apps/avalanche/src/errors.rs
+++ b/rust/apps/avalanche/src/errors.rs
@@ -59,21 +59,21 @@ pub enum AvaxError {
impl From<io::Error> for AvaxError {
fn from(value: io::Error) -> Self {
- Self::TransactionConsensusEncodeError(format!("{}", value))
+ Self::TransactionConsensusEncodeError(format!("{value}"))
}
}
impl From<bech32::segwit::DecodeError> for AvaxError {
fn from(value: bech32::segwit::DecodeError) -> Self {
- Self::Bech32DecodeError(format!("{}", value))
+ Self::Bech32DecodeError(format!("{value}"))
}
}
impl From<KeystoreError> for AvaxError {
fn from(value: KeystoreError) -> Self {
match value {
- KeystoreError::DerivePubKey(data) => Self::KeystoreError(format!("{}", data)),
- KeystoreError::InvalidDerivationPath(data) => Self::KeystoreError(format!("{}", data)),
+ KeystoreError::DerivePubKey(data) => Self::KeystoreError(data.to_string()),
+ KeystoreError::InvalidDerivationPath(data) => Self::KeystoreError(data.to_string()),
KeystoreError::XPubError(data) => Self::KeystoreError(data),
KeystoreError::SeedError(data) => Self::KeystoreError(data),
KeystoreError::DerivationError(data) => Self::KeystoreError(data),
diff --git a/rust/apps/avalanche/src/lib.rs b/rust/apps/avalanche/src/lib.rs
index ff6cb62..5471111 100644
--- a/rust/apps/avalanche/src/lib.rs
+++ b/rust/apps/avalanche/src/lib.rs
@@ -4,7 +4,10 @@
#[allow(unused_imports)]
#[macro_use]
extern crate alloc;
-use alloc::{string::String, vec::Vec};
+use alloc::{
+ string::ToString,
+ vec::Vec,
+};
pub use address::get_address;
use bytes::{Buf, Bytes};
@@ -22,10 +25,6 @@ pub mod encode {
mod address;
pub mod network;
mod ripple_keypair;
-pub struct PsbtSignStatus {
- pub sign_status: Option<String>,
- pub is_completed: bool,
-}
use transactions::type_id::TypeId;
@@ -36,13 +35,17 @@ where
let bytes = Bytes::from(data);
match T::try_from(bytes) {
Ok(data) => Ok(data),
- Err(e) => Err(AvaxError::InvalidInput),
+ Err(_) => Err(AvaxError::InvalidInput),
}
}
pub fn get_avax_tx_type_id(data: Vec<u8>) -> Result<TypeId> {
let mut bytes = Bytes::from(data);
- // codec_id 2 bytes
+ if bytes.remaining() < 6 {
+ return Err(AvaxError::InvalidTransaction(
+ "Insufficient data".to_string(),
+ ));
+ }
bytes.advance(2);
let type_id = TypeId::try_from(bytes.get_u32())?;
Ok(type_id)
diff --git a/rust/apps/avalanche/src/network.rs b/rust/apps/avalanche/src/network.rs
index 60e8202..eda3a03 100644
--- a/rust/apps/avalanche/src/network.rs
+++ b/rust/apps/avalanche/src/network.rs
@@ -59,7 +59,7 @@ impl FromStr for Network {
"avax" => Ok(Network::AvaxMainNet),
#[cfg(feature = "testnet")]
"fuji" => Ok(Network::AvaxTestNet),
- _ => Err(AvaxError::UnsupportedNetwork(format!("{:?}", network))),
+ _ => Err(AvaxError::UnsupportedNetwork(format!("{network:?}"))),
}
}
}
diff --git a/rust/apps/avalanche/src/ripple_keypair.rs b/rust/apps/avalanche/src/ripple_keypair.rs
index 614b86c..196de5d 100644
--- a/rust/apps/avalanche/src/ripple_keypair.rs
+++ b/rust/apps/avalanche/src/ripple_keypair.rs
@@ -4,7 +4,7 @@ use cryptoxide::hashing;
use cryptoxide::ripemd160::Ripemd160;
pub(crate) fn sha256_digest(data: &[u8]) -> Vec<u8> {
- hashing::sha256(&data).to_vec()
+ hashing::sha256(data).to_vec()
}
fn ripemd160_digest(data: &[u8]) -> [u8; 20] {
diff --git a/rust/apps/avalanche/src/transactions/C_chain/evm_export.rs b/rust/apps/avalanche/src/transactions/C_chain/evm_export.rs
index f472afb..f09d735 100644
--- a/rust/apps/avalanche/src/transactions/C_chain/evm_export.rs
+++ b/rust/apps/avalanche/src/transactions/C_chain/evm_export.rs
@@ -98,7 +98,7 @@ impl AvaxTxInfo for ExportTx {
C_BLOCKCHAIN_ID | C_TEST_BLOCKCHAIN_ID => "C",
_ => "Unknown",
};
- format!("C to {} Export", dest)
+ format!("C to {dest} Export")
}
_ => "Unknown".to_string(),
};
@@ -148,7 +148,7 @@ mod tests {
// f22ATVQT11pSdMwvWLwXgeHNshauTDZxzErFKT5nd64esWXjU
let input_bytes = "000000000001000000057fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d5000000000000000000000000000000000000000000000000000000000000000000000001a9b548da818607e83cbcf6802370691948cbd4160000000005f60cde3d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa0000000000000000000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000070000000005f5e1000000000000000000000000010000000169bc9b5b6cbbbd490abbd79a37ad6cd643be87ab000000010000000900000001683728d1c682680aa80e702dcc8e349f79b5da8cac4b7fb59b28750f1c4c977014a70f69d5ef97dec616a21d69e3560cf2db78257710af3c03f959a487bcab04000218c13e";
let binary_data = hex::decode(input_bytes).expect("Failed to decode hex string");
- let mut bytes = Bytes::from(binary_data);
+ let bytes = Bytes::from(binary_data);
let result = ExportTx::try_from(bytes.clone()).unwrap();
assert_eq!(result.codec_id, 0);
assert_eq!(result.type_id, TypeId::CchainExportTx);
diff --git a/rust/apps/avalanche/src/transactions/C_chain/evm_import.rs b/rust/apps/avalanche/src/transactions/C_chain/evm_import.rs
index 1f3e98e..e3513f9 100644
--- a/rust/apps/avalanche/src/transactions/C_chain/evm_import.rs
+++ b/rust/apps/avalanche/src/transactions/C_chain/evm_import.rs
@@ -137,7 +137,7 @@ mod tests {
fn test_avax_c_import() {
let input_bytes = "000000000000000000057fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d5ab68eb1ee142a05cfe768c36e11f0b596db5a3c6c77aabe665dad9e638ca94f7000000013f407c793a9a31b5f281623e479bcaee760be3da54969b85f44ffc51a67c1c64000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000050000000005f5e1000000000100000000000000013fe51338992d913b6a999693d7c345646ca3bb4c0000000005f5b5223d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa0000000100000009000000017e79e5f2fba29e48d9d329f23e8e27bf20d79714555bb04a175e57136b95b282528654583b1fd568e7f8322962fb31e4174896a50114511d124bf74a92559c1f0048c457c5";
let binary_data = hex::decode(input_bytes).expect("Failed to decode hex string");
- let mut bytes = Bytes::from(binary_data);
+ let bytes = Bytes::from(binary_data);
let result = ImportTx::try_from(bytes.clone()).unwrap();
assert_eq!(result.type_id, TypeId::BaseTx);
assert_eq!(result.codec_id, 0);
diff --git a/rust/apps/avalanche/src/transactions/P_chain/add_permissionless_delegator.rs b/rust/apps/avalanche/src/transactions/P_chain/add_permissionless_delegator.rs
index 12885fd..9c98ea2 100644
--- a/rust/apps/avalanche/src/transactions/P_chain/add_permissionless_delegator.rs
+++ b/rust/apps/avalanche/src/transactions/P_chain/add_permissionless_delegator.rs
@@ -15,7 +15,7 @@ use bytes::{Buf, Bytes};
use core::convert::TryFrom;
#[derive(Debug)]
-pub struct AddPermissLessionDelegatorTx {
+pub struct AddPermissionlessDelegatorTx {
base_tx: BaseTx,
validator: Validator,
subnet_id: SubnetId,
@@ -23,7 +23,7 @@ pub struct AddPermissLessionDelegatorTx {
delegator_owner: OutputOwner,
}
-impl AvaxTxInfo for AddPermissLessionDelegatorTx {
+impl AvaxTxInfo for AddPermissionlessDelegatorTx {
fn get_total_input_amount(&self) -> u64 {
self.base_tx.get_total_input_amount()
}
@@ -70,14 +70,13 @@ impl AvaxTxInfo for AddPermissLessionDelegatorTx {
Some(
self.delegator_owner
.addresses
- .get(0)
- .and_then(|addr| Some(addr.encode()))
+ .get(0).map(|addr| addr.encode())
.unwrap_or_default(),
)
}
}
-impl TryFrom<Bytes> for AddPermissLessionDelegatorTx {
+impl TryFrom<Bytes> for AddPermissionlessDelegatorTx {
type Error = AvaxError;
fn try_from(mut bytes: Bytes) -> Result<Self> {
@@ -96,7 +95,7 @@ impl TryFrom<Bytes> for AddPermissLessionDelegatorTx {
let delegator_owner = OutputOwner::try_from(bytes.clone())?;
bytes.advance(delegator_owner.parsed_size());
- Ok(AddPermissLessionDelegatorTx {
+ Ok(AddPermissionlessDelegatorTx {
base_tx,
validator,
subnet_id,
@@ -113,8 +112,8 @@ mod tests {
#[test]
fn test_add_permissionless_delegator() {
let input_bytes = "00000000001a000000050000000000000000000000000000000000000000000000000000000000000000000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000003b9a9e0400000000000000000000000100000001e0beb088f94b8224eb5d6f1115561d7173cd6e7f00000002295a7b15e26c6cafda8883afd0f724e0e0b1dad4517148711434cb96fb3c8a61000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000003b9aca0000000001000000006109bc613691602ca0811312357676416252412a87ded6c56c240baba1afe042000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000003b9aca000000000100000000000000007072a3df0cd056d9b9ef00c09630bad3027dc312000000006760c3b100000000676215a9000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000003b9aca0000000000000000000000000100000001e0beb088f94b8224eb5d6f1115561d7173cd6e7f0000000b00000000000000000000000100000001a0f4d4d9a0ea219da5ed5499ad083e1942a0846a000000020000000900000001438c3a393f49bb27791ca830effec456c2642a487ee4ce89300dd2e591fc22ab6b2aa8e08515ca229f2a2f14168700e05a1f96bd61d1fc3ab31e9e71ef9f16bb000000000900000001438c3a393f49bb27791ca830effec456c2642a487ee4ce89300dd2e591fc22ab6b2aa8e08515ca229f2a2f14168700e05a1f96bd61d1fc3ab31e9e71ef9f16bb005c3d047c";
- let mut bytes = Bytes::from(hex::decode(input_bytes).expect("Failed to decode hex string"));
- let result = AddPermissLessionDelegatorTx::try_from(bytes.clone()).unwrap();
+ let bytes = Bytes::from(hex::decode(input_bytes).expect("Failed to decode hex string"));
+ let result = AddPermissionlessDelegatorTx::try_from(bytes.clone()).unwrap();
assert_eq!(result.base_tx.get_blockchain_id(), P_BLOCKCHAIN_ID);
}
}
diff --git a/rust/apps/avalanche/src/transactions/P_chain/add_permissionless_validator.rs b/rust/apps/avalanche/src/transactions/P_chain/add_permissionless_validator.rs
index d9e6aac..9540c9c 100644
--- a/rust/apps/avalanche/src/transactions/P_chain/add_permissionless_validator.rs
+++ b/rust/apps/avalanche/src/transactions/P_chain/add_permissionless_validator.rs
@@ -17,7 +17,7 @@ use bytes::{Buf, Bytes};
use core::convert::TryFrom;
#[derive(Debug)]
-pub struct AddPermissLessionValidatorTx {
+pub struct AddPermissionlessValidatorTx {
base_tx: BaseTx,
validator: Validator,
subnet_id: SubnetId,
@@ -28,7 +28,7 @@ pub struct AddPermissLessionValidatorTx {
delegator_share: u32,
}
-impl AvaxTxInfo for AddPermissLessionValidatorTx {
+impl AvaxTxInfo for AddPermissionlessValidatorTx {
fn get_total_input_amount(&self) -> u64 {
self.base_tx.get_total_input_amount()
}
@@ -75,14 +75,13 @@ impl AvaxTxInfo for AddPermissLessionValidatorTx {
Some(
self.validator_owner
.addresses
- .get(0)
- .and_then(|addr| Some(addr.encode()))
+ .get(0).map(|addr| addr.encode())
.unwrap_or_default(),
)
}
}
-impl TryFrom<Bytes> for AddPermissLessionValidatorTx {
+impl TryFrom<Bytes> for AddPermissionlessValidatorTx {
type Error = AvaxError;
fn try_from(mut bytes: Bytes) -> Result<Self> {
@@ -109,7 +108,7 @@ impl TryFrom<Bytes> for AddPermissLessionValidatorTx {
let delegator_share = bytes.get_u32();
- Ok(AddPermissLessionValidatorTx {
+ Ok(AddPermissionlessValidatorTx {
base_tx,
validator,
subnet_id,
@@ -130,8 +129,8 @@ mod tests {
fn test_add_permissionless_validator() {
// 23HbZUQ7ijjrDHfqnjKpd4MTMRY18Gc2JxCz79ZBwZAsCLfntb
let input_bytes = "000000000019000000050000000000000000000000000000000000000000000000000000000000000000000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000070000000005e57a370000000000000000000000010000000161cd7d26c72edc631d4114d6eef2c4e069ec9206000000020033b7653ffbf19a2352591e8b6aea8e7c75f38d8e8f5f781cf15aad8425010a000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000003b9ab9d9000000010000000029ec95b1c9df6cd2598852d78fea7766c1aece1a7b5d24f6cf58adc98107f927000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000003b8ab46a0000000100000000000000009e843011540909cc62c64dc85a8a1507813777410000000067614160000000006851245000000000713fb30000000000000000000000000000000000000000000000000000000000000000000000001c87c87cef2e92bface778c711c752168a6e858d58ba62463e8bab336f9b05c98c695acf3c7da02b05c667ce5627e63a60ad53ad7da84734084394dedf6b3c4bb6c85922c2b08b09c55508d49d348ad0dcd9678be58197fef69bad862b1d170f4b0c24f189d9d4b6b5103d28b5e8146d305e28d3dcfb3279f089c7152535a24800c7a1a212868a5c76e3559ea9d4a64d9d000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa0000000700000000713fb3000000000000000000000000010000000161cd7d26c72edc631d4114d6eef2c4e069ec92060000000b0000000000000000000000010000000161cd7d26c72edc631d4114d6eef2c4e069ec92060000000b0000000000000000000000010000000161cd7d26c72edc631d4114d6eef2c4e069ec92060000c350";
- let mut bytes = Bytes::from(hex::decode(input_bytes).expect("Failed to decode hex string"));
- let result = AddPermissLessionValidatorTx::try_from(bytes.clone()).unwrap();
+ let bytes = Bytes::from(hex::decode(input_bytes).expect("Failed to decode hex string"));
+ let result = AddPermissionlessValidatorTx::try_from(bytes.clone()).unwrap();
assert_eq!(
"avax1v8xh6fk89mwxx82pzntwaukyup57eysx7xzuwa".to_string(),
result.get_reward_address().unwrap()
diff --git a/rust/apps/avalanche/src/transactions/P_chain/validator.rs b/rust/apps/avalanche/src/transactions/P_chain/validator.rs
index 3a66b7b..c284ca9 100644
--- a/rust/apps/avalanche/src/transactions/P_chain/validator.rs
+++ b/rust/apps/avalanche/src/transactions/P_chain/validator.rs
@@ -43,7 +43,7 @@ mod tests {
fn test_validator_parse() {
let input_bytes = "000000023d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000070000000005f5e100000000000000000000000001000000018771921301d5bffff592dae86695a615bdb4a4413d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000070000000017c771d2000000000000000000000001000000010969ea62e2bb30e66d82e82fe267edf6871ea5f7";
let binary_data = hex::decode(input_bytes).expect("Failed to decode hex string");
- let mut bytes = Bytes::from(binary_data);
+ let bytes = Bytes::from(binary_data);
let result = Validator::try_from(bytes.clone()).unwrap();
assert_eq!(
"Node-1118zVrK8tN1ic5wUb5dcsECsf2BtfQ",
diff --git a/rust/apps/avalanche/src/transactions/base_tx.rs b/rust/apps/avalanche/src/transactions/base_tx.rs
index 004fd42..24d33a5 100644
--- a/rust/apps/avalanche/src/transactions/base_tx.rs
+++ b/rust/apps/avalanche/src/transactions/base_tx.rs
@@ -5,11 +5,10 @@ use super::type_id::TypeId;
use crate::constants::*;
use crate::errors::{AvaxError, Result};
use alloc::{
- format,
string::{String, ToString},
vec::Vec,
};
-use bitcoin::secp256k1::{Message, Secp256k1};
+use bitcoin::secp256k1::Message;
use bytes::{Buf, Bytes};
use core::convert::TryFrom;
use cryptoxide::hashing::sha256;
@@ -46,16 +45,13 @@ impl BaseTx {
pub fn avax_base_sign(seed: &[u8], path: String, unsigned_data: Vec<u8>) -> Result<[u8; 65]> {
let mut bytes: [u8; 65] = [0; 65];
- let sig = Secp256k1::new()
- .sign_ecdsa_recoverable(
- &Message::from_slice(&sha256(unsigned_data.as_slice())).expect("Invalid hash length"),
- &keystore::algorithms::secp256k1::get_private_key_by_seed(&seed, &path.to_string())
- .map_err(|_| AvaxError::InvalidHex(format!("get private key error")))?,
- )
- .serialize_compact();
+ let msg = Message::from_digest_slice(&sha256(unsigned_data.as_slice()))
+ .map_err(|_| AvaxError::InvalidHex("invalid sha256 length".to_string()))?;
- bytes[..64].copy_from_slice(&sig.1);
- bytes[64] = sig.0.to_i32() as u8;
+ let (rec_id, sig64) = keystore::algorithms::secp256k1::sign_message_by_seed(seed, &path, &msg)?;
+
+ bytes[..64].copy_from_slice(&sig64);
+ bytes[64] = rec_id as u8;
Ok(bytes)
}
@@ -119,6 +115,11 @@ impl TryFrom<Bytes> for BaseTx {
bytes.advance(inputs.parsed_size());
let memo_len = bytes.get_u32();
+ if bytes.remaining() < memo_len as usize {
+ return Err(AvaxError::InvalidHex(
+ "Insufficient data for memo".to_string(),
+ ));
+ }
let memo = bytes.split_to(memo_len as usize).to_vec();
let tx_size = initial_len - bytes.len();
@@ -143,24 +144,21 @@ mod tests {
fn test_avax_base_transaction() {
{
// x-chain fuji test case
- let input_bytes = "00000000000000000005ab68eb1ee142a05cfe768c36e11f0b596db5a3c6c77aabe665dad9e638ca94f7000000023d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000000bbdfb400000000000000000000000010000000169bc9b5b6cbbbd490abbd79a37ad6cd643be87ab3d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000002faf08000000000000000000000000010000000132336f8715dd313a426155cccc15ba27c3033dae0000000163c5b29498bf6a9f1e2a5d20f8eeddaf92096c0ce1c9c2cf6b93fd9a0d12f725000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000003b7c4580000000010000000000000000";
- let input_bytes = "00000000000000000005ab68eb1ee142a05cfe768c36e11f0b596db5a3c6c77aabe665dad9e638ca94f7000000023d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000000bbdfb400000000000000000000000010000000169bc9b5b6cbbbd490abbd79a37ad6cd643be87ab3d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000002faf0800000000000000000000000001000000016498cb45e255f5937b816a59c34a7559a2d437b10000000163c5b29498bf6a9f1e2a5d20f8eeddaf92096c0ce1c9c2cf6b93fd9a0d12f725000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000003b7c4580000000010000000000000000";
+ let _input_bytes = "00000000000000000005ab68eb1ee142a05cfe768c36e11f0b596db5a3c6c77aabe665dad9e638ca94f7000000023d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000000bbdfb400000000000000000000000010000000169bc9b5b6cbbbd490abbd79a37ad6cd643be87ab3d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000002faf08000000000000000000000000010000000132336f8715dd313a426155cccc15ba27c3033dae0000000163c5b29498bf6a9f1e2a5d20f8eeddaf92096c0ce1c9c2cf6b93fd9a0d12f725000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000003b7c4580000000010000000000000000";
+ let _input_bytes = "00000000000000000005ab68eb1ee142a05cfe768c36e11f0b596db5a3c6c77aabe665dad9e638ca94f7000000023d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000000bbdfb400000000000000000000000010000000169bc9b5b6cbbbd490abbd79a37ad6cd643be87ab3d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000002faf0800000000000000000000000001000000016498cb45e255f5937b816a59c34a7559a2d437b10000000163c5b29498bf6a9f1e2a5d20f8eeddaf92096c0ce1c9c2cf6b93fd9a0d12f725000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000003b7c4580000000010000000000000000";
// x-chain mainnet test case
let input_bytes = "00000000000000000001ed5f38341e436e5d46e2bb00b45d62ae97d1b050c64bc634ae10626739e35c4b0000000221e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff000000070000000218711a00000000000000000000000001000000017c949a8013befa47e992078764ff735b18a26b5b21e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff0000000700000003cf87a80c00000000000000000000000100000001d5ae9a7d5b31660f08c0aefc1547fb195fbfc85d000000021ddbc2d7d67f14df1e36111bbeef2adae97067c4ceb9db94b73e8883a5a6dd640000000121e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff000000050000000395e95a000000000100000000885eea33e82eff5130de90152c0ebb98f5cfdc7c7529596fe2473a35654aac830000000021e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff0000000500000002522030ec00000001000000000000000400000000000000020000000900000001a6810c96af6f4e4281031b795f78c37f3395b6d35806179d37b40603d547e2f262969f5363e168c064712607679b01ed13a76daab84addc94a3745b0549a53e5000000000900000001cefe480034588db7b5e0993410b6dbdd2e37e3ec94e75b450dd4c56c32f3b4c61cd9dab507232eb1211a846165336a7d7d975b39612df8d88174e1a92c27535f004a454d1e";
- let mut bytes =
- Bytes::from(hex::decode(input_bytes).expect("Failed to decode hex string"));
+ let bytes = Bytes::from(hex::decode(input_bytes).expect("Failed to decode hex string"));
let result = BaseTx::try_from(bytes).unwrap();
assert_eq!(result.get_blockchain_id(), X_BLOCKCHAIN_ID);
assert_eq!(
"avax10j2f4qqnhmay06vjq7rkflmntvv2y66mzhk6s9",
result
- .get_outputs_addresses()
- .get(0)
+ .get_outputs_addresses().first()
.unwrap()
- .address
- .get(0)
+ .address.first()
.unwrap()
);
assert_eq!(result.get_inputs_len(), 2);
@@ -170,8 +168,7 @@ mod tests {
// x chain base tx
{
let input_bytes = "00000000000000000005ab68eb1ee142a05cfe768c36e11f0b596db5a3c6c77aabe665dad9e638ca94f7000000023d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000070000000001312d00000000000000000000000001000000018771921301d5bffff592dae86695a615bdb4a4413d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000070000000004b571c0000000000000000000000001000000010969ea62e2bb30e66d82e82fe267edf6871ea5f7000000019eae34633c2103aaee5253bb3ca3046c2ab4718a109ffcdb77b51d0427be6bb7000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000050000000005f5e100000000010000000000000000";
- let mut bytes =
- Bytes::from(hex::decode(input_bytes).expect("Failed to decode hex string"));
+ let bytes = Bytes::from(hex::decode(input_bytes).expect("Failed to decode hex string"));
match BaseTx::try_from(bytes) {
Ok(result) => {
assert_eq!(
@@ -188,14 +185,11 @@ mod tests {
);
assert_eq!(result.get_outputs_len(), 2);
}
- Err(e) => match e {
- AvaxError::InvalidHex(msg) => {
- assert_eq!(
- msg, "Unsupported output type found in input bytes.",
- "Unexpected error message"
- );
- }
- _ => {}
+ Err(e) => if let AvaxError::InvalidHex(msg) = e {
+ assert_eq!(
+ msg, "Unsupported output type found in input bytes.",
+ "Unexpected error message"
+ );
},
}
}
diff --git a/rust/apps/avalanche/src/transactions/export.rs b/rust/apps/avalanche/src/transactions/export.rs
index bac0b52..951460d 100644
--- a/rust/apps/avalanche/src/transactions/export.rs
+++ b/rust/apps/avalanche/src/transactions/export.rs
@@ -59,7 +59,7 @@ impl AvaxTxInfo for ExportTx {
C_BLOCKCHAIN_ID => "C",
_ => "Unknown",
};
- format!("{} to {} Export", source, dest)
+ format!("{source} to {dest} Export")
}
_ => "Unknown".to_string(),
};
@@ -110,17 +110,31 @@ mod tests {
fn test_avax_base_export_tx() {
{
// export to C
- let input_bytes = "00000000000400000005ab68eb1ee142a05cfe768c36e11f0b596db5a3c6c77aabe665dad9e638ca94f7000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000070000000057eab6c0000000000000000000000001000000013d200933d192f824fc532519e8ae826990fee64000000001d3f3594499a1e5ce4fcf9aeb139dafb51c1ba18b89c5dc5cefe380a39ca9ba04000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000050000000058928f800000000100000000000000007fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d5000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000000098968000000000000000000000000100000001d6178ba78d772ea8a01782488a6d1937a4a5cc2b000000010000000900000001d2c31dc8565f8b70438abd550dc7ecfc8f33e23c7bd2b3954c31572b991eeb8d6c2990148f8a59b33b3481719c759355c6036f1eaf2e6210313cfae6d6cfe87600d44e2649";
+ {
+ let input_bytes = "00000000000400000005ab68eb1ee142a05cfe768c36e11f0b596db5a3c6c77aabe665dad9e638ca94f7000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000070000000057eab6c0000000000000000000000001000000013d200933d192f824fc532519e8ae826990fee64000000001d3f3594499a1e5ce4fcf9aeb139dafb51c1ba18b89c5dc5cefe380a39ca9ba04000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000050000000058928f800000000100000000000000007fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d5000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000000098968000000000000000000000000100000001d6178ba78d772ea8a01782488a6d1937a4a5cc2b000000010000000900000001d2c31dc8565f8b70438abd550dc7ecfc8f33e23c7bd2b3954c31572b991eeb8d6c2990148f8a59b33b3481719c759355c6036f1eaf2e6210313cfae6d6cfe87600d44e2649";
+ let bytes =
+ Bytes::from(hex::decode(input_bytes).expect("Failed to decode hex string"));
+ let result = ExportTx::try_from(bytes).unwrap();
+ assert_eq!(result.get_dest_chain(), C_TEST_BLOCKCHAIN_ID);
+ }
// x export to p
- let input_bytes = "00000000000400000005ab68eb1ee142a05cfe768c36e11f0b596db5a3c6c77aabe665dad9e638ca94f7000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000005742de00000000000000000000000001000000013d200933d192f824fc532519e8ae826990fee64000000001bb4002f89f58f40649a197e557549cc9a2481f54c62b720e5289d5b8d44e2649000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000050000000057eab6c00000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000070000000000989680000000000000000000000001000000013d200933d192f824fc532519e8ae826990fee640000000010000000900000001a0d23a3d847854238fdc20fcd55f280f144dead9b4ac5e7704efcedce789970e1cce1656d5d603bbb9fe268bff5b99b13a6390b0eec8608fed7696372d1a2f58007d974fba";
+ {
+ let input_bytes = "00000000000400000005ab68eb1ee142a05cfe768c36e11f0b596db5a3c6c77aabe665dad9e638ca94f7000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000005742de00000000000000000000000001000000013d200933d192f824fc532519e8ae826990fee64000000001bb4002f89f58f40649a197e557549cc9a2481f54c62b720e5289d5b8d44e2649000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000050000000057eab6c00000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000070000000000989680000000000000000000000001000000013d200933d192f824fc532519e8ae826990fee640000000010000000900000001a0d23a3d847854238fdc20fcd55f280f144dead9b4ac5e7704efcedce789970e1cce1656d5d603bbb9fe268bff5b99b13a6390b0eec8608fed7696372d1a2f58007d974fba";
+ let bytes =
+ Bytes::from(hex::decode(input_bytes).expect("Failed to decode hex string"));
+ let result = ExportTx::try_from(bytes).unwrap();
+ assert_eq!(result.get_dest_chain(), X_TEST_BLOCKCHAIN_ID);
+ }
// p chain export to x
- let input_bytes = "000000000012000000050000000000000000000000000000000000000000000000000000000000000000000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa0000000700000000001e848000000000000000000000000100000001cc4822028594f008045b77cf8342e8f62db1a1da000000011ebc7d69c8e7d50ce59dac9c0de8c1f22a76274d8454d3427be44d5776335156000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa0000000500000000004c4b400000000100000000000000007fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d5000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa0000000700000000001e84800000000000000000000000010000000100739a08d627492cea5abec92998ddc891cd6177000000010000000900000001cda36ebb7b114f0479bfa2eec2ccda8030fd9fd088a1ba19109971c0ffccc9293e21f827502d0210dc7a2a8af11ff7d63f1f858777709e9053b6296287f7abde00e89a6479";
- let mut bytes =
- Bytes::from(hex::decode(input_bytes).expect("Failed to decode hex string"));
- let result = ExportTx::try_from(bytes).unwrap();
- assert_eq!(result.get_dest_chain(), C_TEST_BLOCKCHAIN_ID);
+ {
+ let input_bytes = "000000000012000000050000000000000000000000000000000000000000000000000000000000000000000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa0000000700000000001e848000000000000000000000000100000001cc4822028594f008045b77cf8342e8f62db1a1da000000011ebc7d69c8e7d50ce59dac9c0de8c1f22a76274d8454d3427be44d5776335156000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa0000000500000000004c4b400000000100000000000000007fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d5000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa0000000700000000001e84800000000000000000000000010000000100739a08d627492cea5abec92998ddc891cd6177000000010000000900000001cda36ebb7b114f0479bfa2eec2ccda8030fd9fd088a1ba19109971c0ffccc9293e21f827502d0210dc7a2a8af11ff7d63f1f858777709e9053b6296287f7abde00e89a6479";
+ let bytes =
+ Bytes::from(hex::decode(input_bytes).expect("Failed to decode hex string"));
+ let result = ExportTx::try_from(bytes).unwrap();
+ assert_eq!(result.get_dest_chain(), C_TEST_BLOCKCHAIN_ID);
+ }
}
}
}
diff --git a/rust/apps/avalanche/src/transactions/import.rs b/rust/apps/avalanche/src/transactions/import.rs
index 6c41b86..c120a5d 100644
--- a/rust/apps/avalanche/src/transactions/import.rs
+++ b/rust/apps/avalanche/src/transactions/import.rs
@@ -80,16 +80,13 @@ impl TryFrom<Bytes> for ImportTx {
#[cfg(test)]
mod tests {
use super::*;
- use crate::transactions::type_id::TypeId;
- use core::result;
#[test]
fn test_avax_base_import_tx() {
// x chain import tx from p chain
{
let input_bytes = "00000000000300000005ab68eb1ee142a05cfe768c36e11f0b596db5a3c6c77aabe665dad9e638ca94f7000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000003b8b87c00000000000000000000000010000000132336f8715dd313a426155cccc15ba27c3033dae00000000000000007fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d5000000011fbfcaa954ca294e4754e75b4c1232cd5e7539a1286027d395a136e57cc6e917000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000003b9aca000000000100000000000000010000000900000001257ee33547f045cdf50b1811a52f82bd3087e6ca99d16a0461e09d667d0814c01e43d705cedb99728d08341b2550751e79d4c396d9554b99319a2140b5b9677d012e783c9a";
- let mut bytes =
- Bytes::from(hex::decode(input_bytes).expect("Failed to decode hex string"));
+ let bytes = Bytes::from(hex::decode(input_bytes).expect("Failed to decode hex string"));
let result = ImportTx::try_from(bytes).unwrap();
assert_eq!(result.base_tx.get_blockchain_id(), X_TEST_BLOCKCHAIN_ID);
}
@@ -97,8 +94,7 @@ mod tests {
// x-chain import from c-chain
{
let input_bytes = "00000000000300000005ab68eb1ee142a05cfe768c36e11f0b596db5a3c6c77aabe665dad9e638ca94f7000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000070000000005e69ec0000000000000000000000001000000014effb776db45b22c20b4fd8bed9c315a305ecf8600000000000000007fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d50000000178479532682bda3cca3ecd79a19a60ead0b929632fa6652b88ae39f4771b2ace000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000050000000005f5e10000000001000000000000000100000009000000018133ecce7737d6449415f7a532c4314fd005d89fc1d4b1d239fe5275673162f86d06852bb1a0881a5454c7646c586d55f422f695532accc3bd7e4a387c745259011c1b1afd";
- let mut bytes =
- Bytes::from(hex::decode(input_bytes).expect("Failed to decode hex string"));
+ let bytes = Bytes::from(hex::decode(input_bytes).expect("Failed to decode hex string"));
let result = ImportTx::try_from(bytes).unwrap();
assert_eq!(result.source_chain, C_TEST_BLOCKCHAIN_ID);
}
@@ -106,8 +102,7 @@ mod tests {
// p-chain import form c-chain xZAN6Dr6snqq3LzAhQsCJWpXntGaMVrQRDqDE1ZdCsCgBkwWS
{
let input_bytes = "000000000011000000050000000000000000000000000000000000000000000000000000000000000000000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000003b9ab9d900000000000000000000000100000001d45b64545e31a4159cab2e9ebd51a56e60fb418300000000000000007fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d5000000011d2fe74495f92313aed48d73aef1b540730870a21f44b1b3fbb833994d8f9a79000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000003b9aca0000000001000000000000000100000009000000017eeec21ac8841dec1f0782889a9fd9ae9509888b2f1b38e912ebfe189be371420b1c1d0a0868647fa82824e07a48271658bcf57f68eb5e1d6c31adcc604263e6002ac028d9";
- let mut bytes =
- Bytes::from(hex::decode(input_bytes).expect("Failed to decode hex string"));
+ let bytes = Bytes::from(hex::decode(input_bytes).expect("Failed to decode hex string"));
let result = ImportTx::try_from(bytes).unwrap();
assert_eq!(result.get_fee_amount(), 4135);
assert_eq!(result.get_total_input_amount(), 1_000_000_000);
diff --git a/rust/apps/avalanche/src/transactions/inputs/secp256k1_transfer_input.rs b/rust/apps/avalanche/src/transactions/inputs/secp256k1_transfer_input.rs
index a6b3e26..ba6f965 100644
--- a/rust/apps/avalanche/src/transactions/inputs/secp256k1_transfer_input.rs
+++ b/rust/apps/avalanche/src/transactions/inputs/secp256k1_transfer_input.rs
@@ -56,7 +56,7 @@ mod tests {
fn test_secp256k1_transfer_input_try_from() {
let input_bytes = "00000005000000001dbd670d0000000100000000";
let binary_data = hex::decode(input_bytes).expect("Failed to decode hex string");
- let mut bytes = Bytes::from(binary_data);
+ let bytes = Bytes::from(binary_data);
let result = SECP256K1TransferInput::try_from(bytes.clone()).unwrap();
assert_eq!(result.type_id, 5);
assert_eq!(result.amount, 498951949);
diff --git a/rust/apps/avalanche/src/transactions/mod.rs b/rust/apps/avalanche/src/transactions/mod.rs
index aa37b2f..40fb9b8 100644
--- a/rust/apps/avalanche/src/transactions/mod.rs
+++ b/rust/apps/avalanche/src/transactions/mod.rs
@@ -16,12 +16,12 @@ pub mod outputs {
pub mod secp256k1_transfer_output;
}
-pub mod C_chain {
+pub mod c_chain {
pub mod evm_export;
pub mod evm_import;
}
-pub mod P_chain {
+pub mod p_chain {
pub mod add_permissionless_delegator;
pub mod add_permissionless_validator;
pub mod node_id;
diff --git a/rust/apps/avalanche/src/transactions/transferable.rs b/rust/apps/avalanche/src/transactions/transferable.rs
index c82d0f6..9368eda 100644
--- a/rust/apps/avalanche/src/transactions/transferable.rs
+++ b/rust/apps/avalanche/src/transactions/transferable.rs
@@ -6,7 +6,6 @@ use super::type_id::TypeId;
use crate::constants::*;
use crate::errors::{AvaxError, Result};
use alloc::{
- format,
string::{String, ToString},
vec::Vec,
};
@@ -54,17 +53,11 @@ impl TryFrom<Bytes> for OutputType {
let mut type_bytes = bytes.clone();
let type_id = type_bytes.get_u32();
match TypeId::try_from(type_id)? {
- TypeId::CchainExportTx => {
- todo!()
- }
TypeId::Secp256k1TransferOutput => {
SECP256K1TransferOutput::try_from(bytes).map(OutputType::SECP256K1)
}
- TypeId::XchainImportTx => {
- todo!()
- }
_ => {
- return Err(AvaxError::InvalidHex(
+ Err(AvaxError::InvalidHex(
"Unsupported output type found in input bytes.".to_string(),
))
}
@@ -101,7 +94,7 @@ impl TransferableOutput {
impl ParsedSizeAble for TransferableOutput {
fn parsed_size(&self) -> usize {
- self.output.get_transfer_output_len() + ASSET_ID_LEN as usize
+ self.output.get_transfer_output_len() + ASSET_ID_LEN
}
}
@@ -142,7 +135,7 @@ impl TryFrom<Bytes> for TransferableInput {
fn try_from(mut bytes: Bytes) -> Result<Self> {
let tx_id: [u8; TX_ID_LEN] = bytes.split_to(TX_ID_LEN)[..]
.try_into()
- .map_err(|_| AvaxError::InvalidHex(format!("error data to tx_id")))?;
+ .map_err(|_| AvaxError::InvalidHex("error data to tx_id".to_string()))?;
let utxo_index = bytes.get_u32();
let asset_id = AssetId::try_from(bytes.split_to(ASSET_ID_LEN))?;
Ok(TransferableInput {
@@ -156,7 +149,7 @@ impl TryFrom<Bytes> for TransferableInput {
impl ParsedSizeAble for TransferableInput {
fn parsed_size(&self) -> usize {
- self.input.get_transfer_input_len() + TX_ID_LEN as usize + ASSET_ID_LEN as usize + 4
+ self.input.get_transfer_input_len() + TX_ID_LEN + ASSET_ID_LEN + 4
}
}
@@ -177,7 +170,7 @@ impl TryFrom<Bytes> for InputType {
SECP256K1TransferInput::try_from(bytes)?,
)),
_ => {
- return Err(AvaxError::InvalidHex(
+ Err(AvaxError::InvalidHex(
"Unsupported input type found in input bytes.".to_string(),
))
}
diff --git a/rust/apps/avalanche/src/transactions/tx_header.rs b/rust/apps/avalanche/src/transactions/tx_header.rs
index 8cf7dcb..69d0c6a 100644
--- a/rust/apps/avalanche/src/transactions/tx_header.rs
+++ b/rust/apps/avalanche/src/transactions/tx_header.rs
@@ -2,6 +2,7 @@ use crate::constants::*;
use crate::errors::{AvaxError, Result};
use bytes::{Buf, Bytes};
use core::convert::TryFrom;
+use alloc::string::ToString;
pub type BlockChainId = [u8; BLOCKCHAIN_ID_LEN];
@@ -17,7 +18,7 @@ impl Header {
}
pub fn get_blockchain_id(&self) -> BlockChainId {
- self.blockchain_id.clone()
+ self.blockchain_id
}
pub fn parsed_size(&self) -> usize {
@@ -33,7 +34,7 @@ impl TryFrom<Bytes> for Header {
network_id: bytes.get_u32(),
blockchain_id: bytes[..32]
.try_into()
- .map_err(|_| AvaxError::InvalidHex(format!("error data to blockchain_id")))?,
+ .map_err(|_| AvaxError::InvalidHex("error data to blockchain_id".to_string()))?,
})
}
}
diff --git a/rust/apps/avalanche/src/transactions/type_id.rs b/rust/apps/avalanche/src/transactions/type_id.rs
index 5f4dd22..98b33f0 100644
--- a/rust/apps/avalanche/src/transactions/type_id.rs
+++ b/rust/apps/avalanche/src/transactions/type_id.rs
@@ -16,8 +16,8 @@ pub enum TypeId {
NftTransferOperation = 0x0000000D,
PchainImportTx = 0x00000011,
PchainExportTx = 0x00000012,
- AddPermissLessionValidator = 0x00000019,
- AddPermissLessionDelegator = 0x0000001A,
+ AddPermissionlessValidator = 0x00000019,
+ AddPermissionlessDelegator = 0x0000001A,
}
impl TryFrom<u32> for TypeId {
@@ -40,8 +40,8 @@ impl TryFrom<u32> for TypeId {
0x0000_000D => Ok(TypeId::NftTransferOperation),
0x0000_0011 => Ok(TypeId::PchainImportTx),
0x0000_0012 => Ok(TypeId::PchainExportTx),
- 0x0000_0019 => Ok(TypeId::AddPermissLessionValidator),
- 0x0000_001A => Ok(TypeId::AddPermissLessionDelegator),
+ 0x0000_0019 => Ok(TypeId::AddPermissionlessValidator),
+ 0x0000_001A => Ok(TypeId::AddPermissionlessDelegator),
_ => Err(AvaxError::UnknownTypeId(value)),
}
}
diff --git a/rust/rust_c/src/avalanche/mod.rs b/rust/rust_c/src/avalanche/mod.rs
index 3b4d6ea..0dfe6b7 100644
--- a/rust/rust_c/src/avalanche/mod.rs
+++ b/rust/rust_c/src/avalanche/mod.rs
@@ -8,18 +8,14 @@ pub mod structs;
use crate::common::{
errors::RustCError,
- ffi::{CSliceFFI, VecFFI},
- structs::{ExtendedPublicKey, SimpleResponse, TransactionCheckResult, TransactionParseResult},
- types::{Ptr, PtrBytes, PtrString, PtrT, PtrUR},
+ ffi::CSliceFFI,
+ structs::{ExtendedPublicKey, TransactionCheckResult, TransactionParseResult},
+ types::{PtrBytes, PtrT, PtrUR},
ur::{UREncodeResult, FRAGMENT_MAX_LENGTH_DEFAULT, FRAGMENT_UNLIMITED_LENGTH},
utils::{recover_c_array, recover_c_char},
};
-use crate::{extract_array, extract_ptr_with_type, impl_c_ptr};
-use alloc::{
- format,
- string::{String, ToString},
- vec::Vec,
-};
+use crate::{extract_array, extract_ptr_with_type};
+use alloc::{format, string::ToString, string::String};
use app_avalanche::{
constants::{
C_BLOCKCHAIN_ID, C_CHAIN_PREFIX, C_TEST_BLOCKCHAIN_ID, P_BLOCKCHAIN_ID, X_BLOCKCHAIN_ID,
@@ -29,17 +25,16 @@ use app_avalanche::{
get_avax_tx_header, get_avax_tx_type_id, parse_avax_tx,
transactions::{
base_tx::{avax_base_sign, BaseTx},
+ c_chain::{evm_export::ExportTx as CchainExportTx, evm_import::ImportTx as CchainImportTx},
export::ExportTx,
import::ImportTx,
- type_id::{self, TypeId},
- C_chain::{evm_export::ExportTx as CchainExportTx, evm_import::ImportTx as CchainImportTx},
- P_chain::{
- add_permissionless_delegator::AddPermissLessionDelegatorTx,
- add_permissionless_validator::AddPermissLessionValidatorTx,
+ p_chain::{
+ add_permissionless_delegator::AddPermissionlessDelegatorTx,
+ add_permissionless_validator::AddPermissionlessValidatorTx,
},
+ type_id::{self, TypeId},
},
};
-use bitcoin::ecdsa::Signature;
use structs::DisplayAvaxTx;
use {
hex,
@@ -48,6 +43,7 @@ use {
traits::RegistryItem,
},
};
+
#[derive(Debug, Clone)]
pub struct DerivationPath {
pub base_path: String,
@@ -69,14 +65,15 @@ unsafe fn parse_transaction_by_type(
public_keys: PtrT<CSliceFFI<ExtendedPublicKey>>,
) -> PtrT<TransactionParseResult<DisplayAvaxTx>> {
let tx_data = sign_request.get_tx_data();
- let type_id = get_avax_tx_type_id(sign_request.get_tx_data()).unwrap();
-
- let mut path = get_avax_tx_type_id(sign_request.get_tx_data())
- .map_err(|_| AvaxError::InvalidInput)
- .and_then(|type_id| {
- determine_derivation_path(type_id, sign_request, sign_request.get_wallet_index())
- })
- .unwrap();
+ let type_id = match get_avax_tx_type_id(sign_request.get_tx_data()) {
+ Ok(type_id) => type_id,
+ Err(_) => return TransactionParseResult::from(RustCError::InvalidData("invalid avax tx type id".to_string())).c_ptr(),
+ };
+
+ let mut path = match determine_derivation_path(type_id, sign_request, sign_request.get_wallet_index()) {
+ Ok(path) => path,
+ Err(_) => return TransactionParseResult::from(RustCError::InvalidData("invalid derivation path".to_string())).c_ptr(),
+ };
let mut address = String::new();
for key in recover_c_array(public_keys).iter() {
@@ -87,18 +84,21 @@ unsafe fn parse_transaction_by_type(
&recover_c_char(key.xpub),
path.base_path.as_str(),
)
- .unwrap(),
+ .unwrap_or("".to_string()),
_ => app_avalanche::get_address(
app_avalanche::network::Network::AvaxMainNet,
path.full_path.as_str(),
- recover_c_char(key.xpub).as_str(),
+ &recover_c_char(key.xpub),
path.base_path.as_str(),
- )
- .unwrap(),
+ ).unwrap_or("".to_string()),
}
}
}
+ // Helper macro: given a concrete tx type `$tx_type`, parse raw tx bytes (`tx_data`)
+ // into that type with `parse_avax_tx::<$tx_type>`, then convert it to the
+ // UI-friendly `DisplayAvaxTx` and wrap it into `TransactionParseResult` (C pointer).
+ // On parse error, returns a unified `InvalidData` result.
macro_rules! parse_tx {
($tx_type:ty) => {
parse_avax_tx::<$tx_type>(tx_data)
@@ -116,7 +116,10 @@ unsafe fn parse_transaction_by_type(
.c_ptr()
})
.unwrap_or_else(|_| {
- TransactionParseResult::from(RustCError::InvalidMasterFingerprint).c_ptr()
+ TransactionParseResult::from(RustCError::InvalidData(
+ "invalid data".to_string(),
+ ))
+ .c_ptr()
})
};
}
@@ -136,8 +139,8 @@ unsafe fn parse_transaction_by_type(
TypeId::PchainExportTx | TypeId::XchainExportTx => parse_tx!(ExportTx),
TypeId::XchainImportTx | TypeId::PchainImportTx => parse_tx!(ImportTx),
TypeId::CchainExportTx => parse_tx!(CchainExportTx),
- TypeId::AddPermissLessionValidator => parse_tx!(AddPermissLessionValidatorTx),
- TypeId::AddPermissLessionDelegator => parse_tx!(AddPermissLessionDelegatorTx),
+ TypeId::AddPermissionlessValidator => parse_tx!(AddPermissionlessValidatorTx),
+ TypeId::AddPermissionlessDelegator => parse_tx!(AddPermissionlessDelegatorTx),
_ => TransactionParseResult::from(RustCError::InvalidData(format!(
"{type_id:?} not support"
)))
@@ -173,6 +176,13 @@ unsafe fn avax_sign_dynamic(
)
}
+/// Derive the HD path used for address/key lookup and signing.
+///
+/// Rationale: raw Avalanche transaction bytes do not contain HD path.
+/// We therefore compute the final path from:
+/// - the transaction type / blockchain_id (to choose the base path prefix, e.g. C vs X/P chain)
+/// - the `wallet_index` provided in `AvaxSignRequest` (appended as `/0/{index}`)
+/// This allows the device to reconstruct the exact address/key path for validation and signing.
pub fn determine_derivation_path(
type_id: TypeId,
sign_request: &AvaxSignRequest,
diff --git a/src/ui/gui_chain/gui_chain.c b/src/ui/gui_chain/gui_chain.c
index 5750ecf..887cd70 100644
--- a/src/ui/gui_chain/gui_chain.c
+++ b/src/ui/gui_chain/gui_chain.c
@@ -1,5 +1,6 @@
#include "define.h"
#include "gui_chain.h"
+#include "keystore.h"
typedef TransactionCheckResult *(*CheckUrResultHandler)(void);
@@ -184,3 +185,29 @@ static const ViewHandlerEntry *GetViewHandlerEntry(ViewType viewType)
}
return NULL;
}
+
+UREncodeResult *SignInternal(SignFn sign_func, void *data)
+{
+ bool enable = IsPreviousLockScreenEnable();
+ SetLockScreen(false);
+ UREncodeResult *encodeResult = NULL;
+ uint8_t seed[SEED_LEN] = {0};
+ int ret = 0;
+
+ do {
+ ret = GetAccountSeed(GetCurrentAccountIndex(), seed, SecretCacheGetPassword());
+ if (ret != 0) {
+ break;
+ }
+
+ int len = GetMnemonicType() == MNEMONIC_TYPE_BIP39 ? sizeof(seed) : GetCurrentAccountEntropyLen();
+ encodeResult = sign_func(data, seed, len);
+ CHECK_CHAIN_BREAK(encodeResult);
+ } while (0);
+
+ memset_s(seed, sizeof(seed), 0, sizeof(seed));
+ ClearSecretCache();
+ SetLockScreen(enable);
+
+ return encodeResult;
+}
\ No newline at end of file
diff --git a/src/ui/gui_chain/gui_chain.h b/src/ui/gui_chain/gui_chain.h
index bf9fa40..869e852 100644
--- a/src/ui/gui_chain/gui_chain.h
+++ b/src/ui/gui_chain/gui_chain.h
@@ -145,6 +145,8 @@ typedef struct {
SetChainDataFunc func;
} SetChainData_t;
+typedef UREncodeResult *(*SignFn)(void *data, PtrBytes seed, uint32_t seed_len);
+
#define CHECK_CHAIN_BREAK(result) \
if (result->error_code != 0) { \
printf("result->code = %d\n", result->error_code); \
@@ -181,6 +183,7 @@ PtrT_TransactionCheckResult CheckUrResult(uint8_t viewType);
GenerateUR GetUrGenerator(ViewType viewType);
GenerateUR GetSingleUrGenerator(ViewType viewType);
bool CheckViewTypeIsAllow(uint8_t viewType);
+UREncodeResult *SignInternal(SignFn sign_func, void *data);
#ifndef BTC_ONLY
bool IsMessageType(uint8_t type);
bool isTonSignProof(uint8_t type);
diff --git a/src/ui/gui_chain/multi/web3/gui_avax.c b/src/ui/gui_chain/multi/web3/gui_avax.c
index afed427..290bd23 100644
--- a/src/ui/gui_chain/multi/web3/gui_avax.c
+++ b/src/ui/gui_chain/multi/web3/gui_avax.c
@@ -5,6 +5,7 @@
#include "secret_cache.h"
#include "gui_chain.h"
#include "gui_chain_components.h"
+#include "keystore.h"
#define CHECK_FREE_PARSE_RESULT(result) \
if (result != NULL) \
@@ -35,34 +36,14 @@ void GuiSetAvaxUrData(URParseResult *urResult, URParseMultiResult *urMultiResult
UREncodeResult *GuiGetAvaxSignQrCodeData(void)
{
- return GetAvaxSignDataDynamic(false);
+ void *data = g_isMulti ? g_urMultiResult->data : g_urResult->data;
+ return SignInternal(avax_sign, data);
}
UREncodeResult *GuiGetAvaxSignUrDataUnlimited(void)
{
- return GetAvaxSignDataDynamic(true);
-}
-
-UREncodeResult *GetAvaxSignDataDynamic(bool isUnlimited)
-{
- bool enable = IsPreviousLockScreenEnable();
- SetLockScreen(false);
- UREncodeResult *encodeResult;
void *data = g_isMulti ? g_urMultiResult->data : g_urResult->data;
- do {
- uint8_t seed[64];
- int len = GetMnemonicType() == MNEMONIC_TYPE_BIP39 ? sizeof(seed) : GetCurrentAccountEntropyLen();
- GetAccountSeed(GetCurrentAccountIndex(), seed, SecretCacheGetPassword());
- if (isUnlimited) {
- encodeResult = avax_sign_unlimited(data, seed, len);
- } else {
- encodeResult = avax_sign(data, seed, len);
- }
- ClearSecretCache();
- CHECK_CHAIN_BREAK(encodeResult);
- } while (0);
- SetLockScreen(enable);
- return encodeResult;
+ return SignInternal(avax_sign_unlimited, data);
}
PtrT_TransactionCheckResult GuiGetAvaxCheckResult(void)
diff --git a/src/ui/gui_chain/multi/web3/gui_iota.c b/src/ui/gui_chain/multi/web3/gui_iota.c
index f9faadc..9331946 100644
--- a/src/ui/gui_chain/multi/web3/gui_iota.c
+++ b/src/ui/gui_chain/multi/web3/gui_iota.c
@@ -86,32 +86,10 @@ void FreeIotaMemory(void)
CHECK_FREE_PARSE_RESULT(g_parseResult);
}
-static UREncodeResult *IotaSignInternal(UREncodeResult * (*sign_func)(void *, PtrBytes, uint32_t), void *data)
-{
- bool enable = IsPreviousLockScreenEnable();
- SetLockScreen(false);
- UREncodeResult *encodeResult = NULL;
- uint8_t seed[SEED_LEN] = {0};
- int ret = 0;
- do {
- ret = GetAccountSeed(GetCurrentAccountIndex(), seed, SecretCacheGetPassword());
- if (ret != 0) {
- break;
- }
- int len = GetMnemonicType() == MNEMONIC_TYPE_BIP39 ? sizeof(seed) : GetCurrentAccountEntropyLen();
- encodeResult = sign_func(data, seed, len);
- CHECK_CHAIN_BREAK(encodeResult);
- } while (0);
- memset_s(seed, sizeof(seed), 0, sizeof(seed));
- ClearSecretCache();
- SetLockScreen(enable);
- return encodeResult;
-}
-
UREncodeResult *GuiGetIotaSignQrCodeData(void)
{
void *data = g_isMulti ? g_urMultiResult->data : g_urResult->data;
- return IotaSignInternal(iota_sign_intent, data);
+ return SignInternal(iota_sign_intent, data);
}
bool GetIotaIsTransaction(void *indata, void *param)
@@ -212,5 +190,5 @@ void GuiIotaTxRawData(lv_obj_t *parent, void *totalData)
UREncodeResult *GuiGetIotaSignHashQrCodeData(void)
{
void *data = g_isMulti ? g_urMultiResult->data : g_urResult->data;
- return IotaSignInternal(iota_sign_hash, data);
+ return SignInternal(iota_sign_hash, data);
}
\ No newline at end of file
diff --git a/src/ui/gui_chain/multi/web3/gui_sui.c b/src/ui/gui_chain/multi/web3/gui_sui.c
index 8ee4ba1..778f7ff 100644
--- a/src/ui/gui_chain/multi/web3/gui_sui.c
+++ b/src/ui/gui_chain/multi/web3/gui_sui.c
@@ -266,40 +266,14 @@ void GetSuiDetail(void *indata, void *param, uint32_t maxLen)
strcpy((char *)indata, tx->detail);
}
-static UREncodeResult *SuiSignInternal(UREncodeResult * (*sign_func)(void *, PtrBytes, uint32_t), void *data)
-{
- bool enable = IsPreviousLockScreenEnable();
- SetLockScreen(false);
- UREncodeResult *encodeResult = NULL;
- uint8_t seed[SEED_LEN] = {0};
- int ret = 0;
-
- do {
- ret = GetAccountSeed(GetCurrentAccountIndex(), seed, SecretCacheGetPassword());
- if (ret != 0) {
- break;
- }
-
- int len = GetMnemonicType() == MNEMONIC_TYPE_BIP39 ? sizeof(seed) : GetCurrentAccountEntropyLen();
- encodeResult = sign_func(data, seed, len);
- CHECK_CHAIN_BREAK(encodeResult);
- } while (0);
-
- memset_s(seed, sizeof(seed), 0, sizeof(seed));
- ClearSecretCache();
- SetLockScreen(enable);
-
- return encodeResult;
-}
-
UREncodeResult *GuiGetSuiSignQrCodeData(void)
{
void *data = g_isMulti ? g_urMultiResult->data : g_urResult->data;
- return SuiSignInternal(sui_sign_intent, data);
+ return SignInternal(sui_sign_intent, data);
}
UREncodeResult *GuiGetSuiSignHashQrCodeData(void)
{
void *data = g_isMulti ? g_urMultiResult->data : g_urResult->data;
- return SuiSignInternal(sui_sign_hash, data);
+ return SignInternal(sui_sign_hash, data);
}
\ No newline at end of file
Why this scored 35/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.