What changed, and why it matters
This commit adds support for the Core Wallet in the Keystone 3 firmware. It mainly expands the number of Avalanche X/P-chain derivation paths from one to ten, updates the Rust SDK dependency, and changes how signing requests extract derivation paths and fingerprints. There is no explicit security fix or vulnerability disclosure in the commit message or diff. The changes are feature-oriented, but they touch sensitive code paths (transaction signing, HD path handling, and master-fingerprint checks), so they warrant careful review.
Treat as a normal feature commit but perform a focused security review on: (1) whether the new multi-path signing API correctly binds each signature to its intended path and prevents cross-path signing; (2) whether the source-fingerprint check covers all supplied derivation keypaths or only the first one; (3) whether the SDK bump introduced any parsing or validation changes that affect transaction integrity; (4) whether the expanded AVAX X/P account range (0-9) is fully reflected in UI, export, and verification flows to avoid user confusion or address mismatch.
Security signals we found
Change to cryptographic signing API (multiple paths / multiple signatures)
Change to HD derivation path validation and selection logic
Change to master-fingerprint / source-fingerprint verification
Dependency bump of keystone-sdk-rust (ur-registry/ur-parse-lib) without disclosed changelog
Removal of local determine_derivation_path logic in favor of paths supplied in sign request
Evidence from the diff
The patch is a feature commit titled ‘support core wallet’. It: (1) bumps ur-registry/ur-parse-lib from SDK tag 0.0.52 to 0.0.56; (2) replaces a single AVAX X/P chain xpub/path (44’/9000’/0’) with ten account indices (44’/9000’/0’ through 44’/9000’/9’); (3) changes avax_base_sign to accept a Vec
Changed components
rust/apps/avalanche/src/transactions/base_tx.rsrust/apps/wallets/src/core_wallet.rsrust/rust_c/src/avalanche/mod.rsrust/rust_c/src/avalanche/structs.rssrc/crypto/account_public_info.csrc/crypto/account_public_info.hsrc/ui/gui_chain/multi/web3/gui_avax.csrc/ui/gui_wallet/multi/web3/gui_wallet.csrc/ui/gui_widgets/multi/web3/gui_multi_path_coin_receive_widgets.crust/Cargo.tomlrust/Cargo.lockInspect captured patch +165 / −137
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index e102ba8..01d425c 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -4771,7 +4771,7 @@ dependencies = [
[[package]]
name = "ur-parse-lib"
version = "0.2.0"
-source = "git+https://git@github.com/KeystoneHQ/keystone-sdk-rust.git?tag=0.0.52#12c4d08dad2e0fb7b7dd05c4c11540dccc2f63bc"
+source = "git+https://git@github.com/KeystoneHQ/keystone-sdk-rust.git?tag=0.0.56#c526d9ccd9b47b978797a038d77ddd406ac91ec5"
dependencies = [
"hex",
"ur",
@@ -4781,7 +4781,7 @@ dependencies = [
[[package]]
name = "ur-registry"
version = "0.1.1"
-source = "git+https://git@github.com/KeystoneHQ/keystone-sdk-rust.git?tag=0.0.52#12c4d08dad2e0fb7b7dd05c4c11540dccc2f63bc"
+source = "git+https://git@github.com/KeystoneHQ/keystone-sdk-rust.git?tag=0.0.56#c526d9ccd9b47b978797a038d77ddd406ac91ec5"
dependencies = [
"bs58 0.5.1",
"core2",
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 8f40c5c..a30479f 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -74,8 +74,8 @@ core2 = { version = "0.3.3", default-features = false, features = ["alloc"] }
thiserror = { version = "1.0", package = "thiserror-core", default-features = false }
rsa = { version = "0.8.2", default-features = false }
sha1 = { version = "0.10.5", default-features = false }
-ur-registry = { git = "https://git@github.com/KeystoneHQ/keystone-sdk-rust.git", tag = "0.0.52" }
-ur-parse-lib = { git = "https://git@github.com/KeystoneHQ/keystone-sdk-rust.git", tag = "0.0.52" }
+ur-registry = { git = "https://git@github.com/KeystoneHQ/keystone-sdk-rust.git", tag = "0.0.56" }
+ur-parse-lib = { git = "https://git@github.com/KeystoneHQ/keystone-sdk-rust.git", tag = "0.0.56" }
ed25519-bip32-core = { version = "0.1.1", default-features = false }
cryptoxide = "0.4"
arrayref = "0.3.6"
diff --git a/rust/apps/avalanche/src/transactions/base_tx.rs b/rust/apps/avalanche/src/transactions/base_tx.rs
index 2ad8d39..dbda5d7 100644
--- a/rust/apps/avalanche/src/transactions/base_tx.rs
+++ b/rust/apps/avalanche/src/transactions/base_tx.rs
@@ -42,17 +42,27 @@ impl BaseTx {
}
}
-pub fn avax_base_sign(seed: &[u8], path: String, unsigned_data: Vec<u8>) -> Result<[u8; 65]> {
+pub fn avax_base_sign(
+ seed: &[u8],
+ paths: Vec<String>,
+ unsigned_data: Vec<u8>,
+) -> Result<Vec<[u8; 65]>> {
let mut bytes: [u8; 65] = [0; 65];
let msg = Message::from_digest_slice(&sha256(unsigned_data.as_slice()))
.map_err(|_| AvaxError::InvalidHex("invalid sha256 length".to_string()))?;
- 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)
+ Ok(paths
+ .iter()
+ .map(|path| -> Result<[u8; 65]> {
+ let (rec_id, sig64) =
+ keystore::algorithms::secp256k1::sign_message_by_seed(seed, path, &msg)?;
+ let mut sig: [u8; 65] = [0u8; 65];
+ sig[..64].copy_from_slice(&sig64);
+ sig[64] = rec_id as u8;
+ Ok(sig)
+ })
+ .collect::<Result<Vec<[u8; 65]>>>()?)
}
impl AvaxTxInfo for BaseTx {
@@ -200,36 +210,36 @@ mod tests {
let seed = hex::decode("b75a396d4965e5352b6c2c83e4a59ad3d243fbd58133ea9fe0631e5c1576808cb7c1a578099f35278ba00fccd2709a2ef73d7e31380898a63a15b5b3f4532010").unwrap();
// c chain import 2NqkXZiNn9KkcM8AzRApymjbp556LRWhTeF6icL5BovqdRL39j
- assert_eq!(hex::encode(avax_base_sign(&seed, String::from("m/44'/60'/0'/0/5"),
+ assert_eq!(hex::encode(avax_base_sign(&seed, vec![String::from("m/44'/60'/0'/0/5")[0]],
hex::decode("000000000000000000057fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d5000000000000000000000000000000000000000000000000000000000000000000000001281937d79ec913734e3705754b5a930b2b1899ab47dce7ecb002fca77e16dffb000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000050000000005f5e100000000010000000000000001a9b548da818607e83cbcf6802370691948cbd4160000000005f589443d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa").unwrap()).unwrap()),
"8e8d5d48956752364d1f5deac5dc32e9034cbc0c94d077934fff59dcb2f468ba7448aa4f3a13ae7907254770c4914c078ee90c5ab856eff8c679daeeee92dc7500".to_string());
// c chain export 2JrHpYEKKwQuFxDg51uNk8pCAzEbYp3UUiAZeEsFrjEEjZVT1A
- assert_eq!(hex::encode(avax_base_sign(&seed, String::from("m/44'/60'/0'/0/5"),
+ assert_eq!(hex::encode(avax_base_sign(&seed, vec![String::from("m/44'/60'/0'/0/5")[0]],
hex::decode("000000000001000000057fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d5000000000000000000000000000000000000000000000000000000000000000000000001a9b548da818607e83cbcf6802370691948cbd416000000005371d2663d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa0000000000000001000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000070000000053717aaa0000000000000000000000010000000169bc9b5b6cbbbd490abbd79a37ad6cd643be87ab").unwrap()).unwrap()),
"0a5dd882621638bad2c76f5c88ccf23d08a17d00b12eafe6e0c0de4a4268edbb0c774e850cc77823edf6afcf51975b8f37e99dc394e5f929d77be2c239a1720500".to_string());
- assert_eq!(hex::encode(avax_base_sign(&seed, String::from("m/44'/9000'/0'/0/0"),
+ assert_eq!(hex::encode(avax_base_sign(&seed, vec![String::from("m/44'/9000'/0'/0/0")[0]],
hex::decode("000000000022000000050000000000000000000000000000000000000000000000000000000000000000000000023d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa0000000700000000000f42400000000000000000000000010000000132336f8715dd313a426155cccc15ba27c3033dae3d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000004d58ade90000000000000000000000010000000132336f8715dd313a426155cccc15ba27c3033dae00000001410b47f7c7aa13f88122be58735c5e985edc65d86fb0baf0b016359c22253d75000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000004d680464000000010000000000000000").unwrap()).unwrap()),
"a3b62b557e703e59c2e63f0124c8d12b13f50ba695b234ff2d39def50df2f7ad255d161ce088d318f4dad1a8df5321387461e56af72621d4074ff6459cbd09bc00".to_string());
// p-chain send
- assert_eq!(hex::encode(avax_base_sign(&seed, String::from("m/44'/9000'/0'/0/5"),
+ assert_eq!(hex::encode(avax_base_sign(&seed, vec![String::from("m/44'/9000'/0'/0/5")[0]],
hex::decode("a3b62b557e703e59c2e63f0124c8d12b13f50ba695b234ff2d39def50df2f7ad255d161ce088d318f4dad1a8df5321387461e56af72621d4074ff6459cbd09bc00").unwrap()).unwrap()),
"02baf08b64b33b8bf017cc15a4ddd1c48b9e4231b900b74b2026b4fae9a512885bc8dc2110d5aad04424b91caa0b7263c4aab41ae74f4c783f8e505182b026b300".to_string());
// p-chain import
- assert_eq!(hex::encode(avax_base_sign(&seed, String::from("m/44'/9000'/0'/0/5"),
+ assert_eq!(hex::encode(avax_base_sign(&seed, vec![String::from("m/44'/9000'/0'/0/5")[0]],
hex::decode("000000000011000000050000000000000000000000000000000000000000000000000000000000000000000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000070000000053716a830000000000000000000000010000000169bc9b5b6cbbbd490abbd79a37ad6cd643be87ab00000000000000007fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d500000001ac39ef990eb8b9dec7df7a8c0accd6203fa22ff354442aa5626b54ef2d58f42d000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000050000000053717aaa0000000100000000").unwrap()).unwrap()),
"0d4d8d482ca9e206fd60ffb0000e52fd56693dd7c5a688da4e5fa620b3fcd1155210ce8ef5efeceae3d53657de0cb8ac17a5e35fecb1d6732c48ed30c4b1031501".to_string());
// p-chain export
- assert_eq!(hex::encode(avax_base_sign(&seed, String::from("m/44'/9000'/0'/0/5"),
+ assert_eq!(hex::encode(avax_base_sign(&seed, vec![String::from("m/44'/9000'/0'/0/5")[0]],
hex::decode("000000000012000000050000000000000000000000000000000000000000000000000000000000000000000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000070000000017d68c240000000000000000000000010000000169bc9b5b6cbbbd490abbd79a37ad6cd643be87ab00000001b9376e9a05dac917513e5385e64bf2dfb1cee8a29848a80ada96ebe6d6a04050000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000050000000053716a83000000010000000000000000ab68eb1ee142a05cfe768c36e11f0b596db5a3c6c77aabe665dad9e638ca94f7000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000003b9aca000000000000000000000000010000000169bc9b5b6cbbbd490abbd79a37ad6cd643be87ab").unwrap()).unwrap()),
"9efee613e2cdbca7c70d197913a402f2ff93e070e7edf5ca60d5e441579403e33687bf6c2632fc40dc6fdacd07a4d6383fc6eec35f6173251a3afd26176ad9d500".to_string());
// x-chain import
- assert_eq!(hex::encode(avax_base_sign(&seed, String::from("m/44'/9000'/0'/0/5"),
+ assert_eq!(hex::encode(avax_base_sign(&seed, vec![String::from("m/44'/9000'/0'/0/5")[0]],
hex::decode("00000000000300000005ab68eb1ee142a05cfe768c36e11f0b596db5a3c6c77aabe665dad9e638ca94f7000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000003b8b87c00000000000000000000000010000000169bc9b5b6cbbbd490abbd79a37ad6cd643be87ab000000000000000000000000000000000000000000000000000000000000000000000000000000000000000143286d1ed254c32dbdd03fdd0935a1324788f5de8f87e8f2d809206472575dba000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000003b9aca000000000100000000").unwrap()).unwrap()),
"5a5bc0444e1a18bc730694618a0d10ba320796d66b2c6bb9aab0cdda77fc31502ed22ed50454935f8914c2e036f9e38280d08dc36b83c59f2b9ba31e9c16914101".to_string());
}
diff --git a/rust/apps/wallets/src/core_wallet.rs b/rust/apps/wallets/src/core_wallet.rs
index ee23c39..6ddbd6b 100644
--- a/rust/apps/wallets/src/core_wallet.rs
+++ b/rust/apps/wallets/src/core_wallet.rs
@@ -15,7 +15,20 @@ use {
use crate::{common::get_path_component, ExtendedPublicKey};
const AVAX_STANDARD_PREFIX: &str = "44'/60'/0'";
-const AVAX_X_P_PREFIX: &str = "44'/9000'/0'";
+
+fn is_avax_x_p_path(path: &[ChildNumber]) -> bool {
+ if path.len() < 3 {
+ return false;
+ }
+ matches!(
+ (&path[0], &path[1], &path[2]),
+ (
+ ChildNumber::Hardened { index: i0 },
+ ChildNumber::Hardened { index: i1 },
+ ChildNumber::Hardened { index: i2 }
+ ) if *i0 == 44 && *i1 == 9000 && *i2 <= 9
+ )
+}
pub fn generate_crypto_multi_accounts(
master_fingerprint: [u8; 4],
@@ -32,11 +45,7 @@ pub fn generate_crypto_multi_accounts(
Some("account.standard".to_string()),
)?);
}
- _path
- if _path
- .to_string()
- .to_lowercase()
- .starts_with(AVAX_X_P_PREFIX) =>
+ _path if is_avax_x_p_path(_path.as_ref()) =>
{
keys.push(generate_k1_normal_key(
master_fingerprint,
diff --git a/rust/rust_c/src/avalanche/mod.rs b/rust/rust_c/src/avalanche/mod.rs
index 69d8b99..c933b05 100644
--- a/rust/rust_c/src/avalanche/mod.rs
+++ b/rust/rust_c/src/avalanche/mod.rs
@@ -15,7 +15,7 @@ use crate::common::{
utils::{recover_c_array, recover_c_char},
};
use crate::{extract_array, extract_ptr_with_type};
-use alloc::{format, string::String, string::ToString};
+use alloc::{format, string::String, string::ToString, vec::Vec};
use app_avalanche::{
constants::{
C_BLOCKCHAIN_ID, C_CHAIN_PREFIX, C_TEST_BLOCKCHAIN_ID, P_BLOCKCHAIN_ID, X_BLOCKCHAIN_ID,
@@ -44,12 +44,6 @@ use {
},
};
-#[derive(Debug, Clone)]
-pub struct DerivationPath {
- pub base_path: String,
- pub full_path: String,
-}
-
#[no_mangle]
pub unsafe extern "C" fn avax_parse_transaction(
ptr: PtrUR,
@@ -75,35 +69,46 @@ unsafe fn parse_transaction_by_type(
}
};
- 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()
- }
- };
+ // Get derivation path from sign_request (avoid borrowing from a temporary)
+ let derivation_paths_vec = sign_request.get_derivation_path();
+ if derivation_paths_vec.is_empty() {
+ return TransactionParseResult::from(RustCError::InvalidData(
+ "invalid derivation path".to_string(),
+ ))
+ .c_ptr();
+ }
+ let derivation_path = &derivation_paths_vec[0];
+ let full_path = match derivation_path.get_path() {
+ Some(p) => format!("m/{}", p),
+ None => {
+ return TransactionParseResult::from(RustCError::InvalidData(
+ "invalid derivation path".to_string(),
+ ))
+ .c_ptr()
+ }
+ };
+ // Derive address by matching full_path with available keys
let mut address = String::new();
for key in recover_c_array(public_keys).iter() {
- if recover_c_char(key.path) == path.base_path {
- address = match (type_id, path.base_path.as_str()) {
- (TypeId::CchainExportTx, "m/44'/60'/0'") => app_ethereum::address::derive_address(
- path.full_path.as_str(),
+ let key_path = recover_c_char(key.path).to_lowercase();
+ if full_path.starts_with(&key_path) {
+ address = match key_path.as_str() {
+ "m/44'/60'/0'" => app_ethereum::address::derive_address(
+ full_path.as_str(),
&recover_c_char(key.xpub),
- path.base_path.as_str(),
+ &key_path,
)
- .unwrap_or("".to_string()),
+ .unwrap_or("no address".to_string()),
_ => app_avalanche::get_address(
app_avalanche::network::Network::AvaxMainNet,
- path.full_path.as_str(),
+ full_path.as_str(),
&recover_c_char(key.xpub),
- path.base_path.as_str(),
+ &key_path,
)
- .unwrap_or("".to_string()),
- }
+ .unwrap_or("no address".to_string()),
+ };
+ break;
}
}
@@ -118,9 +123,8 @@ unsafe fn parse_transaction_by_type(
TransactionParseResult::success(
DisplayAvaxTx::from_tx_info(
parse_data,
- path.full_path,
- address,
- sign_request.get_wallet_index(),
+ full_path.clone(),
+ address.clone(),
type_id,
)
.c_ptr(),
@@ -142,8 +146,26 @@ unsafe fn parse_transaction_by_type(
if header.get_blockchain_id() == C_BLOCKCHAIN_ID
|| header.get_blockchain_id() == C_TEST_BLOCKCHAIN_ID
{
- path.full_path = "".to_string();
- parse_tx!(CchainImportTx)
+ // For C-chain import, use empty path
+ parse_avax_tx::<CchainImportTx>(tx_data)
+ .map(|parse_data| {
+ TransactionParseResult::success(
+ DisplayAvaxTx::from_tx_info(
+ parse_data,
+ "".to_string(),
+ address.clone(),
+ type_id,
+ )
+ .c_ptr(),
+ )
+ .c_ptr()
+ })
+ .unwrap_or_else(|_| {
+ TransactionParseResult::from(RustCError::InvalidData(
+ "invalid data".to_string(),
+ ))
+ .c_ptr()
+ })
} else {
parse_tx!(BaseTx)
}
@@ -188,60 +210,25 @@ 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,
- wallet_index: u64,
-) -> Result<DerivationPath, AvaxError> {
- let wallet_suffix = format!("/0/{wallet_index}");
- let blockchain_id = get_avax_tx_header(sign_request.get_tx_data())?.get_blockchain_id();
- let is_c_chain = |id: &[u8; 32]| *id == C_BLOCKCHAIN_ID || *id == C_TEST_BLOCKCHAIN_ID;
-
- let (base_path, full_path) = match type_id {
- TypeId::CchainExportTx => (C_CHAIN_PREFIX, format!("{C_CHAIN_PREFIX}{wallet_suffix}")),
- TypeId::XchainImportTx | TypeId::PchainImportTx => {
- let source_chain_id =
- parse_avax_tx::<ImportTx>(sign_request.get_tx_data())?.get_source_chain_id();
- (
- X_P_CHAIN_PREFIX,
- format!("{X_P_CHAIN_PREFIX}{wallet_suffix}"),
- )
- }
- _ => {
- let prefix = if is_c_chain(&blockchain_id) {
- C_CHAIN_PREFIX
- } else {
- X_P_CHAIN_PREFIX
- };
- (prefix, format!("{prefix}{wallet_suffix}"))
- }
- };
-
- Ok(DerivationPath {
- base_path: base_path.to_string(),
- full_path,
- })
-}
-
unsafe fn build_sign_result(ptr: PtrUR, seed: &[u8]) -> Result<AvaxSignature, AvaxError> {
let sign_request = extract_ptr_with_type!(ptr, AvaxSignRequest);
- let 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())
- })?
- .full_path;
+ let derivation_keypaths = sign_request.get_derivation_path();
+ if derivation_keypaths.is_empty() {
+ return Err(AvaxError::InvalidInput);
+ }
+ let mut paths: Vec<String> = Vec::new();
+ for kp in derivation_keypaths.iter() {
+ match kp.get_path() {
+ Some(p) => paths.push(format!("m/{}", p)),
+ None => return Err(AvaxError::InvalidInput),
+ }
+ }
- avax_base_sign(seed, path, sign_request.get_tx_data())
- .map(|signature| AvaxSignature::new(sign_request.get_request_id(), signature.to_vec()))
+ avax_base_sign(seed, paths, sign_request.get_tx_data()).map(|signature| {
+ let signatures: Vec<Vec<u8>> = signature.into_iter().map(|arr| arr.to_vec()).collect();
+ AvaxSignature::new(sign_request.get_request_id(), signatures)
+ })
}
#[no_mangle]
@@ -275,9 +262,9 @@ pub unsafe extern "C" fn avax_check_transaction(
return TransactionCheckResult::from(RustCError::InvalidMasterFingerprint).c_ptr();
}
};
- if avax_tx.get_master_fingerprint() == mfp {
- TransactionCheckResult::new().c_ptr()
- } else {
- TransactionCheckResult::from(RustCError::MasterFingerprintMismatch).c_ptr()
+
+ match avax_tx.get_derivation_path()[0].get_source_fingerprint() {
+ Some(fingerprint) if fingerprint == mfp => TransactionCheckResult::new().c_ptr(),
+ _ => TransactionCheckResult::from(RustCError::MasterFingerprintMismatch).c_ptr(),
}
}
diff --git a/rust/rust_c/src/avalanche/structs.rs b/rust/rust_c/src/avalanche/structs.rs
index bbb0a14..1ef6361 100644
--- a/rust/rust_c/src/avalanche/structs.rs
+++ b/rust/rust_c/src/avalanche/structs.rs
@@ -72,7 +72,7 @@ impl_c_ptr!(DisplayAvaxFromToInfo);
impl DisplayAvaxFromToInfo {
fn from_index(
value: &AvaxFromToInfo,
- wallet_index: u64,
+ from_path: &str,
from_address: String,
type_id: TypeId,
) -> Self {
@@ -87,7 +87,7 @@ impl DisplayAvaxFromToInfo {
let path = if !is_change {
null_mut()
} else {
- convert_c_char(format!("{}/0/{}", value.path_prefix, wallet_index))
+ convert_c_char(from_path.to_string())
};
DisplayAvaxFromToInfo {
address: convert_c_char(address.clone()),
@@ -134,18 +134,10 @@ impl DisplayAvaxTx {
value: T,
from_path: String,
from_address: String,
- wallet_index: u64,
type_id: TypeId,
) -> Self {
DisplayAvaxTx {
- data: DisplayTxAvaxData::from_tx_info(
- value,
- from_path,
- from_address,
- wallet_index,
- type_id,
- )
- .c_ptr(),
+ data: DisplayTxAvaxData::from_tx_info(value, from_path, from_address, type_id).c_ptr(),
}
}
}
@@ -155,7 +147,6 @@ impl DisplayTxAvaxData {
value: T,
from_path: String,
from_address: String,
- wallet_index: u64,
type_id: TypeId,
) -> Self {
DisplayTxAvaxData {
@@ -165,7 +156,7 @@ impl DisplayTxAvaxData {
"{} AVAX",
value.get_total_input_amount() as f64 / NAVAX_TO_AVAX_RATIO
)),
- path: convert_c_char(from_path),
+ path: convert_c_char(from_path.clone()),
is_change: false,
}
.c_ptr(),
@@ -193,7 +184,7 @@ impl DisplayTxAvaxData {
.map(|v| {
DisplayAvaxFromToInfo::from_index(
v,
- wallet_index,
+ &from_path,
from_address.clone(),
type_id,
)
diff --git a/src/crypto/account_public_info.c b/src/crypto/account_public_info.c
index 189e7b8..2950d85 100644
--- a/src/crypto/account_public_info.c
+++ b/src/crypto/account_public_info.c
@@ -332,7 +332,16 @@ static const ChainItem_t g_chainTable[] = {
{XPUB_TYPE_XRP, SECP256K1, "xrp", "M/44'/144'/0'" },
{XPUB_TYPE_THOR, SECP256K1, "thor", "M/44'/931'/0'" },
{XPUB_TYPE_AVAX_BIP44_STANDARD, SECP256K1, "avax_c", "M/44'/60'/0'" },
- {XPUB_TYPE_AVAX_X_P, SECP256K1, "avax_x_p", "M/44'/9000'/0'" },
+ {XPUB_TYPE_AVAX_X_P_0, SECP256K1, "avax_x_p_0", "M/44'/9000'/0'" },
+ {XPUB_TYPE_AVAX_X_P_1, SECP256K1, "avax_x_p_1", "M/44'/9000'/1'" },
+ {XPUB_TYPE_AVAX_X_P_2, SECP256K1, "avax_x_p_2", "M/44'/9000'/2'" },
+ {XPUB_TYPE_AVAX_X_P_3, SECP256K1, "avax_x_p_3", "M/44'/9000'/3'" },
+ {XPUB_TYPE_AVAX_X_P_4, SECP256K1, "avax_x_p_4", "M/44'/9000'/4'" },
+ {XPUB_TYPE_AVAX_X_P_5, SECP256K1, "avax_x_p_5", "M/44'/9000'/5'" },
+ {XPUB_TYPE_AVAX_X_P_6, SECP256K1, "avax_x_p_6", "M/44'/9000'/6'" },
+ {XPUB_TYPE_AVAX_X_P_7, SECP256K1, "avax_x_p_7", "M/44'/9000'/7'" },
+ {XPUB_TYPE_AVAX_X_P_8, SECP256K1, "avax_x_p_8", "M/44'/9000'/8'" },
+ {XPUB_TYPE_AVAX_X_P_9, SECP256K1, "avax_x_p_9", "M/44'/9000'/9'" },
{XPUB_TYPE_IOTA_0, ED25519, "iota_0", "M/44'/4218'/0'/0'/0'"},
{XPUB_TYPE_IOTA_1, ED25519, "iota_1", "M/44'/4218'/1'/0'/0'"},
{XPUB_TYPE_IOTA_2, ED25519, "iota_2", "M/44'/4218'/2'/0'/0'"},
@@ -1140,6 +1149,11 @@ void DeleteAccountPublicInfo(uint8_t accountIndex)
FreePublicKeyRam();
}
+char *GetCurrentAccountPath(ChainType chain)
+{
+ return g_chainTable[chain].path;
+}
+
char *GetCurrentAccountPublicKey(ChainType chain)
{
uint8_t accountIndex;
diff --git a/src/crypto/account_public_info.h b/src/crypto/account_public_info.h
index 54fbb1a..431a797 100644
--- a/src/crypto/account_public_info.h
+++ b/src/crypto/account_public_info.h
@@ -47,7 +47,16 @@ typedef enum {
XPUB_TYPE_XRP,
XPUB_TYPE_THOR,
XPUB_TYPE_AVAX_BIP44_STANDARD,
- XPUB_TYPE_AVAX_X_P,
+ XPUB_TYPE_AVAX_X_P_0,
+ XPUB_TYPE_AVAX_X_P_1,
+ XPUB_TYPE_AVAX_X_P_2,
+ XPUB_TYPE_AVAX_X_P_3,
+ XPUB_TYPE_AVAX_X_P_4,
+ XPUB_TYPE_AVAX_X_P_5,
+ XPUB_TYPE_AVAX_X_P_6,
+ XPUB_TYPE_AVAX_X_P_7,
+ XPUB_TYPE_AVAX_X_P_8,
+ XPUB_TYPE_AVAX_X_P_9,
XPUB_TYPE_IOTA_0,
XPUB_TYPE_IOTA_1,
XPUB_TYPE_IOTA_2,
@@ -266,6 +275,7 @@ int32_t TempAccountPublicInfo(uint8_t accountIndex, const char *password, bool s
void DeleteAccountPublicInfo(uint8_t accountIndex);
char *GetCurrentAccountPublicKey(ChainType chain);
uint8_t SpecifiedXPubExist(const char *xPub);
+char *GetCurrentAccountPath(ChainType chain);
void AccountPublicInfoTest(int argc, char *argv[]);
bool GetFirstReceive(const char* chainName);
void SetFirstReceive(const char* chainName, bool isFirst);
diff --git a/src/ui/gui_chain/multi/web3/gui_avax.c b/src/ui/gui_chain/multi/web3/gui_avax.c
index 290bd23..a2cdcc6 100644
--- a/src/ui/gui_chain/multi/web3/gui_avax.c
+++ b/src/ui/gui_chain/multi/web3/gui_avax.c
@@ -20,10 +20,6 @@ static void *g_parseResult = NULL;
static bool g_isMulti = false;
static ViewType g_viewType = ViewTypeUnKnown;
-static lv_obj_t *CreateOverviewAmountView(lv_obj_t *parent, DisplayAvaxTx *data, lv_obj_t *lastView);
-static lv_obj_t *CreateOverviewActionView(lv_obj_t *parent, DisplayAvaxTx *data, lv_obj_t *lastView);
-static lv_obj_t *CreateOverviewDestinationView(lv_obj_t *parent, DisplayAvaxTx *data, lv_obj_t *lastView);
-static lv_obj_t *CreateOverviewContractDataView(lv_obj_t *parent, DisplayAvaxTx *data, lv_obj_t *lastView);
UREncodeResult *GetAvaxSignDataDynamic(bool isUnlimited);
void GuiSetAvaxUrData(URParseResult *urResult, URParseMultiResult *urMultiResult, bool multi)
@@ -62,13 +58,15 @@ void *GuiGetAvaxGUIData(void)
uint8_t mfp[4] = {0};
GetMasterFingerPrint(mfp);
PtrT_CSliceFFI_ExtendedPublicKey public_keys = SRAM_MALLOC(sizeof(CSliceFFI_ExtendedPublicKey));
- ExtendedPublicKey keys[2];
+ ExtendedPublicKey keys[11];
public_keys->data = keys;
public_keys->size = NUMBER_OF_ARRAYS(keys);
keys[0].path = "m/44'/60'/0'";
keys[0].xpub = GetCurrentAccountPublicKey(XPUB_TYPE_AVAX_BIP44_STANDARD);
- keys[1].path = "m/44'/9000'/0'";
- keys[1].xpub = GetCurrentAccountPublicKey(XPUB_TYPE_AVAX_X_P);
+ for (int i = 0; i < 10; i++) {
+ keys[1 + i].path = GetCurrentAccountPath(XPUB_TYPE_AVAX_X_P_0 + i);
+ keys[1 + i].xpub = GetCurrentAccountPublicKey(XPUB_TYPE_AVAX_X_P_0 + i);
+ }
PtrT_TransactionParseResult_DisplayTonTransaction parseResult = avax_parse_transaction(data, mfp, sizeof(mfp), public_keys);
SRAM_FREE(public_keys);
CHECK_CHAIN_BREAK(parseResult);
diff --git a/src/ui/gui_wallet/multi/web3/gui_wallet.c b/src/ui/gui_wallet/multi/web3/gui_wallet.c
index 7038f80..dc9e533 100644
--- a/src/ui/gui_wallet/multi/web3/gui_wallet.c
+++ b/src/ui/gui_wallet/multi/web3/gui_wallet.c
@@ -252,7 +252,16 @@ UREncodeResult *GuiGetCoreWalletData(void)
{
ChainPath_t chainPaths[] = {
{.path = "m/44'/60'/0'", .chainType = XPUB_TYPE_ETH_BIP44_STANDARD},
- {.path = "m/44'/9000'/0'", .chainType = XPUB_TYPE_AVAX_X_P},
+ {.path = "m/44'/9000'/0'", .chainType = XPUB_TYPE_AVAX_X_P_0},
+ {.path = "m/44'/9000'/1'", .chainType = XPUB_TYPE_AVAX_X_P_1},
+ {.path = "m/44'/9000'/2'", .chainType = XPUB_TYPE_AVAX_X_P_2},
+ {.path = "m/44'/9000'/3'", .chainType = XPUB_TYPE_AVAX_X_P_3},
+ {.path = "m/44'/9000'/4'", .chainType = XPUB_TYPE_AVAX_X_P_4},
+ {.path = "m/44'/9000'/5'", .chainType = XPUB_TYPE_AVAX_X_P_5},
+ {.path = "m/44'/9000'/6'", .chainType = XPUB_TYPE_AVAX_X_P_6},
+ {.path = "m/44'/9000'/7'", .chainType = XPUB_TYPE_AVAX_X_P_7},
+ {.path = "m/44'/9000'/8'", .chainType = XPUB_TYPE_AVAX_X_P_8},
+ {.path = "m/44'/9000'/9'", .chainType = XPUB_TYPE_AVAX_X_P_9},
};
ExtendedPublicKey keys[NUMBER_OF_ARRAYS(chainPaths)];
uint8_t mfp[4] = {0};
diff --git a/src/ui/gui_widgets/multi/web3/gui_multi_path_coin_receive_widgets.c b/src/ui/gui_widgets/multi/web3/gui_multi_path_coin_receive_widgets.c
index a209359..76c6305 100644
--- a/src/ui/gui_widgets/multi/web3/gui_multi_path_coin_receive_widgets.c
+++ b/src/ui/gui_widgets/multi/web3/gui_multi_path_coin_receive_widgets.c
@@ -1138,7 +1138,7 @@ static void GetAvaxPathItemSubTittle(char* subTitle, int index, uint32_t maxLen)
strcpy_s(subTitle, maxLen, "m/44'/60'/0'/0/#F5870A X#");
break;
case 1:
- strcpy_s(subTitle, maxLen, "m/44'/9000'/0'/0/#F5870A X#");
+ strcpy_s(subTitle, maxLen, "m/44'/9000'/#F5870A X#/0/0");
break;
default:
break;
@@ -1406,10 +1406,10 @@ static void ModelGetAvaxAddress(uint32_t index, AddressDataItem_t *item)
free_simple_response_c_char(result);
} else {
// x p chain address
- xPub = GetCurrentAccountPublicKey(XPUB_TYPE_AVAX_X_P);
+ xPub = GetCurrentAccountPublicKey(XPUB_TYPE_AVAX_X_P_0 + index);
ASSERT(xPub);
- snprintf_s(hdPath, sizeof(hdPath), "%s/0/%u", "m/44'/9000'/0'", index);
- strcpy_s(rootPath, sizeof(rootPath), "m/44'/9000'/0'");
+ snprintf_s(hdPath, sizeof(hdPath), "m/44'/9000'/%u'/0/0", index);
+ snprintf_s(rootPath, sizeof(rootPath), "m/44'/9000'/%u'", index);
SimpleResponse_c_char *result = avalanche_get_x_p_address(hdPath, xPub, rootPath);
if (result->error_code == 0) {
item->index = index;
Why this scored 26/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.