diff --git a/imkey-core/ikc-device/src/device_binding.rs b/imkey-core/ikc-device/src/device_binding.rs index 246e95a8..ca2df55d 100644 --- a/imkey-core/ikc-device/src/device_binding.rs +++ b/imkey-core/ikc-device/src/device_binding.rs @@ -237,7 +237,7 @@ pub fn bind_test() { // pub const TEST_KEY_PATH: &str = "/tmp/"; // pub const TEST_BIND_CODE: &str = "MCYNK5AH"; pub const TEST_KEY_PATH: &str = "/tmp/"; -pub const TEST_BIND_CODE: &str = "5PJT7223"; +pub const TEST_BIND_CODE: &str = "CM3SH5QE"; #[cfg(test)] mod test { diff --git a/imkey-core/ikc-device/src/device_manager.rs b/imkey-core/ikc-device/src/device_manager.rs index 344bd913..bbc0069f 100644 --- a/imkey-core/ikc-device/src/device_manager.rs +++ b/imkey-core/ikc-device/src/device_manager.rs @@ -224,10 +224,20 @@ pub fn is_bl_status() -> Result { Ok(true) } +pub fn get_btc_apple_version() -> Result { + select_isd()?; + let res = send_apdu("00a4040005695f62746300".to_string())?; + ApduCheck::check_response(res.as_str())?; + let btc_version = hex::decode(&res[0..(res.len() - 4)])?; + let btc_version = String::from_utf8(btc_version)?; + Ok(btc_version) +} + #[cfg(test)] mod test { use crate::device_manager::{ - active_device, app_delete, app_download, app_update, bind_check, is_bl_status, + active_device, app_delete, app_download, app_update, bind_check, get_btc_apple_version, + is_bl_status, }; use ikc_common::constants; use ikc_transport::hid_api::hid_connect; @@ -296,4 +306,11 @@ mod test { let result = active_device(); assert!(result.is_ok()); } + + #[test] + fn get_btc_version_test() { + assert!(hid_connect(constants::DEVICE_MODEL_NAME).is_ok()); + let result = get_btc_apple_version(); + assert!(result.is_ok()); + } } diff --git a/imkey-core/ikc-wallet/coin-bitcoin/src/transaction.rs b/imkey-core/ikc-wallet/coin-bitcoin/src/transaction.rs index 9d5088d3..26122373 100644 --- a/imkey-core/ikc-wallet/coin-bitcoin/src/transaction.rs +++ b/imkey-core/ikc-wallet/coin-bitcoin/src/transaction.rs @@ -15,11 +15,15 @@ use bitcoin_hashes::hash160; use bitcoin_hashes::hex::ToHex; use bitcoin_hashes::Hash; use ikc_common::apdu::{ApduCheck, BtcApdu}; -use ikc_common::constants::{MAX_OPRETURN_SIZE, MAX_UTXO_NUMBER, MIN_NONDUST_OUTPUT, TIMEOUT_LONG}; +use ikc_common::constants::{ + BTC_SEG_WIT_TYPE_P2WPKH, EACH_ROUND_NUMBER, MAX_OPRETURN_SIZE, MAX_UTXO_NUMBER, + MIN_NONDUST_OUTPUT, TIMEOUT_LONG, +}; use ikc_common::error::CoinError; use ikc_common::path::{check_path_validity, get_account_path}; use ikc_common::utility::{bigint_to_byte_vec, hex_to_bytes, secp256k1_sign}; use ikc_device::device_binding::KEY_MANAGER; +use ikc_device::device_manager::get_btc_apple_version; use ikc_transport::message::{send_apdu, send_apdu_timeout}; use secp256k1::ecdsa::Signature; use secp256k1::PublicKey; @@ -66,6 +70,9 @@ impl BtcTransaction { return Err(CoinError::ImkeyInsufficientFunds.into()); } + //get current btc applet version + let btc_version = get_btc_apple_version()?; + //utxo address verify let utxo_pub_key_vec = get_utxo_pub_key(&self.unspents)?; @@ -78,29 +85,62 @@ impl BtcTransaction { output, }; - self.calc_tx_hash(&mut tx_to_sign)?; - - self.tx_preview(&tx_to_sign, network)?; - - let input_with_sigs: Vec = vec![]; - for (idx, utxo) in self.unspents.iter().enumerate() { - let script = Script::from_str(&utxo.script_pubkey)?; - if script.is_p2pkh() { - self.sign_p2pkh_input(idx, &utxo_pub_key_vec[idx], &mut tx_to_sign)?; - } else if script.is_p2sh() { - self.sign_p2sh_nested_p2wpkh_input(idx, &utxo_pub_key_vec[idx], &mut tx_to_sign)?; - } else if script.is_v0_p2wpkh() { - self.sign_p2wpkh_input(idx, &utxo_pub_key_vec[idx], &mut tx_to_sign)?; - } else if script.is_v1_p2tr() { - self.sign_p2tr_input( - idx, - &utxo_pub_key_vec[idx], - &mut tx_to_sign, - SchnorrSighashType::Default, - )?; - } else { - return Err(CoinError::InvalidUtxo.into()); - }; + self.calc_tx_hash(&mut tx_to_sign, &btc_version)?; + + //Compatible with Legacy and Nested Segwit transactions without upgrading btc apples + if btc_version.as_str() >= "1.6.00" { + self.tx_preview(&tx_to_sign, network)?; + for (idx, utxo) in self.unspents.iter().enumerate() { + let script = Script::from_str(&utxo.script_pubkey)?; + if script.is_p2pkh() { + self.sign_p2pkh_input(idx, &utxo_pub_key_vec[idx], &mut tx_to_sign)?; + } else if script.is_p2sh() { + self.sign_p2sh_nested_p2wpkh_input( + idx, + &utxo_pub_key_vec[idx], + &mut tx_to_sign, + )?; + } else if script.is_v0_p2wpkh() { + self.sign_p2wpkh_input(idx, &utxo_pub_key_vec[idx], &mut tx_to_sign)?; + } else if script.is_v1_p2tr() { + self.sign_p2tr_input( + idx, + &utxo_pub_key_vec[idx], + &mut tx_to_sign, + SchnorrSighashType::Default, + )?; + } else { + return Err(CoinError::InvalidUtxo.into()); + }; + } + } else { + for utxo in self.unspents.iter() { + let script = Script::from_str(&utxo.script_pubkey)?; + if !script.is_p2pkh() && !script.is_p2sh() { + return Err(CoinError::InvalidUtxo.into()); + } + } + let address_version = get_address_version(network, &self.to.to_string())?; + match address_version { + 0 | 111 | 5 | 96 => { + if BTC_SEG_WIT_TYPE_P2WPKH.eq(&seg_wit.to_uppercase()) { + self.original_tx_preview(&tx_to_sign, network)?; + for (idx, _) in self.unspents.iter().enumerate() { + self.sign_p2sh_nested_p2wpkh_input( + idx, + &utxo_pub_key_vec[idx], + &mut tx_to_sign, + )?; + } + } else { + self.tx_preview(&tx_to_sign, network)?; + self.sign_p2pkh_inputs(&utxo_pub_key_vec, &mut tx_to_sign)?; + } + } + _ => { + return Err(CoinError::InvalidAddress.into()); + } + } } let tx_bytes = serialize(&tx_to_sign); @@ -112,6 +152,76 @@ impl BtcTransaction { }) } + pub fn sign_p2pkh_inputs( + &self, + utxo_pub_key_vec: &Vec, + transaction: &mut Transaction, + ) -> Result<()> { + let mut lock_script_ver: Vec