diff --git a/Cargo.lock b/Cargo.lock index b0d0d773..ad3526d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -564,6 +564,7 @@ name = "coin-bitcoin" version = "0.1.0" dependencies = [ "anyhow", + "bech32 0.9.1", "bitcoin", "bitcoin_hashes", "bytes", diff --git a/imkey-core/ikc-common/src/applet.rs b/imkey-core/ikc-common/src/applet.rs index ac741b35..74a2ff30 100644 --- a/imkey-core/ikc-common/src/applet.rs +++ b/imkey-core/ikc-common/src/applet.rs @@ -1,6 +1,6 @@ use crate::constants::{ - BCH_AID, BTC_AID, COSMOS_AID, EOS_AID, ETH_AID, FILECOIN_AID, IMK_AID, KUSAMA_AID, LTC_AID, - NERVOS_AID, POLKADOT_AID, TEZOS_AID, TRON_AID, + BCH_AID, BTC_AID, COSMOS_AID, DOGECOIN_AID, EOS_AID, ETH_AID, FILECOIN_AID, IMK_AID, + KUSAMA_AID, LTC_AID, NERVOS_AID, POLKADOT_AID, TEZOS_AID, TRON_AID, }; // type __appletName = 'IMK' | 'Ethereum' | 'Bitcoin' | 'EOS' | 'Cosmos' | 'Filecoin' | 'Kusama' | 'Tezos' | 'Polkadot' | 'TRON' | 'Bitcoin Cash' | 'Litecoin' | 'Nervos' pub fn get_appname_by_instid(instid: &str) -> Option<&str> { @@ -18,6 +18,7 @@ pub fn get_appname_by_instid(instid: &str) -> Option<&str> { IMK_AID => Some("IMK"), NERVOS_AID => Some("Nervos"), TEZOS_AID => Some("Tezos"), + DOGECOIN_AID => Some("Dogecoin"), _ => None, } } @@ -36,6 +37,7 @@ pub fn get_instid_by_appname(appname: &str) -> Option<&str> { "Bitcoin Cash" => Some(BCH_AID), "Litecoin" => Some(LTC_AID), "IMK" => Some(IMK_AID), + "Dogecoin" => Some(DOGECOIN_AID), _ => None, } } @@ -56,6 +58,7 @@ mod tests { assert_eq!(get_appname_by_instid("695F626368").unwrap(), "Bitcoin Cash"); assert_eq!(get_appname_by_instid("695F6C7463").unwrap(), "Litecoin"); assert_eq!(get_appname_by_instid("695F696D6B").unwrap(), "IMK"); + assert_eq!(get_appname_by_instid("695F646F6765636F696E").unwrap(), "Dogecoin"); assert!(get_appname_by_instid("1111111111").is_none()); } @@ -72,6 +75,7 @@ mod tests { ); assert_eq!(get_instid_by_appname("Bitcoin Cash").unwrap(), "695F626368"); assert_eq!(get_instid_by_appname("Litecoin").unwrap(), "695F6C7463"); + assert_eq!(get_instid_by_appname("Dogecoin").unwrap(), "695F646F6765636F696E"); assert!(get_instid_by_appname("APPLET").is_none()); } } diff --git a/imkey-core/ikc-common/src/coin_info.rs b/imkey-core/ikc-common/src/coin_info.rs index d4786fa3..1d6015d4 100644 --- a/imkey-core/ikc-common/src/coin_info.rs +++ b/imkey-core/ikc-common/src/coin_info.rs @@ -235,6 +235,62 @@ lazy_static! { network: "TESTNET".to_string(), seg_wit: "".to_string(), }, + CoinInfo { + coin: "DOGECOIN".to_string(), + derivation_path: "m/44'/3'/0'/0/0".to_string(), + curve: CurveType::SECP256k1, + network: "MAINNET".to_string(), + seg_wit: "NONE".to_string(), + }, + CoinInfo { + coin: "DOGECOIN".to_string(), + derivation_path: "m/44'/1'/0'/0/0".to_string(), + curve: CurveType::SECP256k1, + network: "TESTNET".to_string(), + seg_wit: "NONE".to_string(), + }, + CoinInfo { + coin: "DOGECOIN".to_string(), + derivation_path: "m/49'/3'/0'/0/0".to_string(), + curve: CurveType::SECP256k1, + network: "MAINNET".to_string(), + seg_wit: "P2WPKH".to_string(), + }, + CoinInfo { + coin: "DOGECOIN".to_string(), + derivation_path: "m/49'/1'/0'/0/0".to_string(), + curve: CurveType::SECP256k1, + network: "TESTNET".to_string(), + seg_wit: "P2WPKH".to_string(), + }, + CoinInfo { + coin: "DOGECOIN".to_string(), + derivation_path: "m/84'/3'/0'/0/0".to_string(), + curve: CurveType::SECP256k1, + network: "MAINNET".to_string(), + seg_wit: "VERSION_0".to_string(), + }, + CoinInfo { + coin: "DOGECOIN".to_string(), + derivation_path: "m/84'/1'/0'/0/0".to_string(), + curve: CurveType::SECP256k1, + network: "TESTNET".to_string(), + seg_wit: "VERSION_0".to_string(), + }, + CoinInfo { + coin: "DOGECOIN".to_string(), + derivation_path: "m/86'/3'/0'/0/0".to_string(), + curve: CurveType::SECP256k1, + network: "MAINNET".to_string(), + seg_wit: "VERSION_1".to_string(), + }, + CoinInfo { + coin: "DOGECOIN".to_string(), + derivation_path: "m/86'/1'/0'/0/0".to_string(), + curve: CurveType::SECP256k1, + network: "TESTNET".to_string(), + seg_wit: "VERSION_1".to_string(), + }, ]; RwLock::new(coin_infos) }; diff --git a/imkey-core/ikc-common/src/constants.rs b/imkey-core/ikc-common/src/constants.rs index b45761c0..a431563e 100644 --- a/imkey-core/ikc-common/src/constants.rs +++ b/imkey-core/ikc-common/src/constants.rs @@ -29,6 +29,7 @@ pub const NERVOS_AID: &str = "695F6B315F636B62"; pub const TEZOS_AID: &str = "695F65645F78747A"; pub const BCH_AID: &str = "695F626368"; pub const LTC_AID: &str = "695F6C7463"; +pub const DOGECOIN_AID: &str = "695F646F6765636F696E"; pub const BL_AID: &str = "D0426F6F746C6F61646572"; diff --git a/imkey-core/ikc-common/src/error.rs b/imkey-core/ikc-common/src/error.rs index 8ca00f62..7015e742 100644 --- a/imkey-core/ikc-common/src/error.rs +++ b/imkey-core/ikc-common/src/error.rs @@ -8,6 +8,8 @@ pub enum CommonError { InvalidKeyIvLength, #[error("invalid_base58")] InvalidBase58, + #[error("missing_network")] + MissingNetwork, } #[derive(Error, Debug, PartialOrd, PartialEq)] diff --git a/imkey-core/ikc-wallet/coin-bch/src/common.rs b/imkey-core/ikc-wallet/coin-bch/src/common.rs index 716682ec..7383bffe 100644 --- a/imkey-core/ikc-wallet/coin-bch/src/common.rs +++ b/imkey-core/ikc-wallet/coin-bch/src/common.rs @@ -2,7 +2,6 @@ use crate::address::BchAddress; use crate::transaction::Utxo; use crate::Result; use bitcoin::network::constants::Network; -use bitcoin::secp256k1::Secp256k1 as BitcoinSecp256k1; use bitcoin::util::base58; use bitcoin::util::bip32::{ChainCode, ChildNumber, ExtendedPubKey}; use bitcoin::{Address, PublicKey}; diff --git a/imkey-core/ikc-wallet/coin-bitcoin/Cargo.toml b/imkey-core/ikc-wallet/coin-bitcoin/Cargo.toml index 208aafa0..b101a4d8 100644 --- a/imkey-core/ikc-wallet/coin-bitcoin/Cargo.toml +++ b/imkey-core/ikc-wallet/coin-bitcoin/Cargo.toml @@ -18,4 +18,5 @@ num-bigint = "=0.4.3" anyhow = "=1.0.79" bytes = "=1.4.0" prost = "=0.11.2" -prost-types = "=0.11.2" \ No newline at end of file +prost-types = "=0.11.2" +bech32 = "=0.9.1" \ No newline at end of file diff --git a/imkey-core/ikc-wallet/coin-bitcoin/src/address.rs b/imkey-core/ikc-wallet/coin-bitcoin/src/address.rs index f576e458..9ec4ceaa 100644 --- a/imkey-core/ikc-wallet/coin-bitcoin/src/address.rs +++ b/imkey-core/ikc-wallet/coin-bitcoin/src/address.rs @@ -4,7 +4,6 @@ use bitcoin::network::constants::Network; use bitcoin::schnorr::UntweakedPublicKey; use bitcoin::util::bip32::{ChainCode, ChildNumber, DerivationPath, ExtendedPubKey, Fingerprint}; use bitcoin::{Address, PublicKey}; -use bitcoin_hashes::{hash160, Hash}; use ikc_common::apdu::{ApduCheck, BtcApdu, CoinCommonApdu}; use ikc_common::constants; use ikc_common::error::CommonError; diff --git a/imkey-core/ikc-wallet/coin-bitcoin/src/btc_kin_address.rs b/imkey-core/ikc-wallet/coin-bitcoin/src/btc_kin_address.rs new file mode 100644 index 00000000..ae3ff515 --- /dev/null +++ b/imkey-core/ikc-wallet/coin-bitcoin/src/btc_kin_address.rs @@ -0,0 +1,524 @@ +use crate::common::get_xpub_data; +use crate::network::BtcKinNetwork; +use crate::Result; +use bitcoin::hash_types::PubkeyHash as PubkeyHashType; +use bitcoin::hash_types::ScriptHash as ScriptHashType; +use bitcoin::network::constants::Network; +use bitcoin::schnorr::UntweakedPublicKey; +use bitcoin::util::address::Payload; +use bitcoin::util::address::{Error as LibAddressError, WitnessVersion}; +use bitcoin::util::base58; +use bitcoin::util::bip32::{ChainCode, ChildNumber, DerivationPath, ExtendedPubKey, Fingerprint}; +use bitcoin::{Address as LibAddress, PublicKey, Script}; +use bitcoin_hashes::Hash; +use core::result; +use ikc_common::apdu::{ApduCheck, BtcApdu, CoinCommonApdu}; +use ikc_common::coin_info::CoinInfo; +use ikc_common::constants; +use ikc_common::error::CoinError; +use ikc_common::path::check_path_validity; +use ikc_common::path::get_parent_path; +use ikc_common::utility::hex_to_bytes; +use ikc_transport::message::send_apdu; +use secp256k1::{PublicKey as Secp256k1PublicKey, Secp256k1}; +use std::convert::TryFrom; +use std::fmt::{Display, Formatter}; +use std::str::FromStr; + +pub trait AddressTrait { + fn from_public_key( + public_key: &str, + network: &BtcKinNetwork, + seg_wit: &str, + ) -> Result; + + fn is_valid(address: &str, coin: &CoinInfo) -> bool; +} + +pub trait ScriptPubkey { + fn script_pubkey(&self) -> Script; +} + +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct BtcKinAddress { + pub network: BtcKinNetwork, + pub payload: Payload, +} + +impl AddressTrait for BtcKinAddress { + fn from_public_key( + public_key: &str, + network: &BtcKinNetwork, + seg_wit: &str, + ) -> Result { + let mut pub_key_obj = PublicKey::from_str(public_key)?; + pub_key_obj.compressed = true; + + let address = match seg_wit { + constants::BTC_SEG_WIT_TYPE_P2WPKH => { + LibAddress::p2shwpkh(&pub_key_obj, Network::Bitcoin)? + } + constants::BTC_SEG_WIT_TYPE_VERSION_0 => { + LibAddress::p2wpkh(&pub_key_obj, Network::Bitcoin)? + } + constants::BTC_SEG_WIT_TYPE_VERSION_1 => { + let untweak_pub_key = UntweakedPublicKey::from(secp256k1::PublicKey::from_slice( + &hex_to_bytes(&public_key)?, + )?); + let secp256k1 = Secp256k1::new(); + LibAddress::p2tr(&secp256k1, untweak_pub_key, None, Network::Bitcoin) + } + _ => LibAddress::p2pkh(&pub_key_obj, Network::Bitcoin), + }; + + Ok(BtcKinAddress { + payload: address.payload, + network: network.clone(), + }) + } + + fn is_valid(address: &str, coin: &CoinInfo) -> bool { + let ret = BtcKinAddress::from_str(address); + if let Ok(btc_kin_addr) = ret { + btc_kin_addr.network.network == coin.network + } else { + false + } + } +} + +impl BtcKinAddress { + pub fn p2pkh(network: &BtcKinNetwork, path: &str) -> Result { + check_path_validity(path)?; + + let pub_key = &get_xpub_data(path, true)?[..130]; + + let mut pub_key_obj = PublicKey::from_str(pub_key)?; + pub_key_obj.compressed = true; + let addr = LibAddress::p2pkh(&pub_key_obj, Network::Bitcoin); + + Ok(BtcKinAddress { + payload: addr.payload, + network: network.clone(), + }) + } + + pub fn p2shwpkh(network: &BtcKinNetwork, path: &str) -> Result { + check_path_validity(path)?; + + let pub_key = &get_xpub_data(path, true)?[..130]; + + let mut pub_key_obj = PublicKey::from_str(pub_key)?; + pub_key_obj.compressed = true; + + let address = LibAddress::p2shwpkh(&pub_key_obj, Network::Bitcoin)?; + + Ok(BtcKinAddress { + payload: address.payload, + network: network.clone(), + }) + } + + pub fn p2wpkh(network: &BtcKinNetwork, path: &str) -> Result { + check_path_validity(path)?; + + let pub_key = &get_xpub_data(path, true)?[..130]; + + let mut pub_key_obj = PublicKey::from_str(pub_key)?; + pub_key_obj.compressed = true; + let addr = LibAddress::p2wpkh(&pub_key_obj, Network::Bitcoin)?; + Ok(BtcKinAddress { + payload: addr.payload, + network: network.clone(), + }) + } + + pub fn p2tr(network: &BtcKinNetwork, path: &str) -> Result { + check_path_validity(path)?; + + let pub_key = &get_xpub_data(path, true)?[..130]; + let untweak_pub_key = + UntweakedPublicKey::from(secp256k1::PublicKey::from_slice(&hex_to_bytes(&pub_key)?)?); + + let secp256k1 = Secp256k1::new(); + let addr = LibAddress::p2tr(&secp256k1, untweak_pub_key, None, Network::Bitcoin); + Ok(BtcKinAddress { + payload: addr.payload, + network: network.clone(), + }) + } + + pub fn display_address(network: &BtcKinNetwork, path: &str, seg_wit: &str) -> Result { + check_path_validity(path)?; + + let address = match seg_wit { + constants::BTC_SEG_WIT_TYPE_P2WPKH => Self::p2shwpkh(network, path)?, + constants::BTC_SEG_WIT_TYPE_VERSION_0 => Self::p2wpkh(network, path)?, + constants::BTC_SEG_WIT_TYPE_VERSION_1 => Self::p2tr(network, path)?, + _ => Self::p2pkh(network, path)?, + }; + + let apdu_res = send_apdu(BtcApdu::register_address(&address.to_string().as_bytes()))?; + ApduCheck::check_response(apdu_res.as_str())?; + Ok(address.to_string()) + } + + pub fn script_pubkey(&self) -> Script { + self.payload.script_pubkey() + } +} + +impl ScriptPubkey for BtcKinAddress { + fn script_pubkey(&self) -> Script { + self.script_pubkey() + } +} + +impl FromStr for BtcKinAddress { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + // try bech32 + let bech32_network = bech32_network(s); + if let Some(network) = bech32_network { + // decode as bech32 + let (_, payload, _) = bech32::decode(s)?; + if payload.is_empty() { + return Err(LibAddressError::EmptyBech32Payload.into()); + } + + // Get the script version and program (converted from 5-bit to 8-bit) + let (version, program): (bech32::u5, Vec) = { + let (v, p5) = payload.split_at(1); + (v[0], bech32::FromBase32::from_base32(p5)?) + }; + + // Generic segwit checks. + if version.to_u8() > 16 { + return Err(LibAddressError::InvalidWitnessVersion(version.to_u8()).into()); + } + if program.len() < 2 || program.len() > 40 { + return Err(LibAddressError::InvalidWitnessProgramLength(program.len()).into()); + } + + // Specific segwit v0 check. + if version.to_u8() == 0 && (program.len() != 20 && program.len() != 32) { + return Err(LibAddressError::InvalidSegwitV0ProgramLength(program.len()).into()); + } + let payload = Payload::WitnessProgram { + version: WitnessVersion::try_from(version.to_u8())?, + program, + }; + return Ok(BtcKinAddress { + payload, + network: network.clone(), + }); + } + + let data = decode_base58(s).map_err(|_| CoinError::InvalidAddress)?; + if let Some(network) = BtcKinNetwork::find_by_prefix(data[0]) { + if network.p2pkh_prefix == data[0] { + return Ok(BtcKinAddress { + network: network.clone(), + payload: Payload::PubkeyHash(PubkeyHashType::from_slice(&data[1..]).unwrap()), + }); + } else if network.p2sh_prefix == data[0] { + return Ok(BtcKinAddress { + network: network.clone(), + payload: Payload::ScriptHash(ScriptHashType::from_slice(&data[1..]).unwrap()), + }); + } + } + + Err(LibAddressError::UnrecognizedScript.into()) + } +} + +impl Display for BtcKinAddress { + fn fmt(&self, fmt: &mut Formatter) -> core::fmt::Result { + match self.payload { + Payload::PubkeyHash(ref hash) => { + let mut prefixed = [0; 21]; + prefixed[0] = self.network.p2pkh_prefix; + prefixed[1..].copy_from_slice(&hash[..]); + base58::check_encode_slice_to_fmt(fmt, &prefixed[..]) + } + Payload::ScriptHash(ref hash) => { + let mut prefixed = [0; 21]; + prefixed[0] = self.network.p2sh_prefix; + prefixed[1..].copy_from_slice(&hash[..]); + base58::check_encode_slice_to_fmt(fmt, &prefixed[..]) + } + Payload::WitnessProgram { + version, + program: ref prog, + } => { + let mut bech32_writer = bech32::Bech32Writer::new( + self.network.bech32_hrp, + version.bech32_variant(), + fmt, + )?; + bech32::WriteBase32::write_u5(&mut bech32_writer, version.into())?; + bech32::ToBase32::write_base32(&prog, &mut bech32_writer) + } + } + } +} + +fn bech32_network(bech32: &str) -> Option<&BtcKinNetwork> { + let bech32_prefix = bech32.rfind('1').map(|sep| bech32.split_at(sep).0); + + if bech32_prefix.is_some() { + let prefix = bech32_prefix.unwrap(); + if !prefix.is_empty() { + return BtcKinNetwork::find_by_hrp(prefix); + } + } + return None; +} + +fn decode_base58(addr: &str) -> result::Result, LibAddressError> { + // Base58 + if addr.len() > 50 { + return Err(LibAddressError::Base58(base58::Error::InvalidLength( + addr.len() * 11 / 15, + ))); + } + let data = base58::from_check(addr)?; + if data.len() != 21 { + Err(LibAddressError::Base58(base58::Error::InvalidLength( + data.len(), + ))) + } else { + Ok(data) + } +} + +pub struct ImkeyPublicKey(); + +impl ImkeyPublicKey { + pub fn get_xpub(network: Network, path: &str) -> Result { + check_path_validity(path)?; + + let xpub_data = get_xpub_data(path, true)?; + let xpub_data = &xpub_data[..194].to_string(); + + let pub_key = &xpub_data[..130]; + let chain_code = &xpub_data[130..]; + + let parent_xpub = get_xpub_data(get_parent_path(path)?, true)?; + let parent_xpub = &parent_xpub[..130].to_string(); + let parent_pub_key_obj = Secp256k1PublicKey::from_str(parent_xpub)?; + + let pub_key_obj = Secp256k1PublicKey::from_str(pub_key)?; + + let chain_code_obj = ChainCode::from(hex::decode(chain_code).unwrap().as_slice()); + let parent_ext_pub_key = ExtendedPubKey { + network, + depth: 0u8, + parent_fingerprint: Fingerprint::default(), + child_number: ChildNumber::from_normal_idx(0).unwrap(), + public_key: parent_pub_key_obj, + chain_code: chain_code_obj, + }; + let fingerprint_obj = parent_ext_pub_key.fingerprint(); + + //build extend public key obj + let chain_code_obj = ChainCode::from(hex::decode(chain_code).unwrap().as_slice()); + let chain_number_vec: Vec = DerivationPath::from_str(path)?.into(); + let extend_public_key = ExtendedPubKey { + network, + depth: chain_number_vec.len() as u8, + parent_fingerprint: fingerprint_obj, + child_number: *chain_number_vec.get(chain_number_vec.len() - 1).unwrap(), + public_key: pub_key_obj, + chain_code: chain_code_obj, + }; + Ok(extend_public_key.to_string()) + } + + pub fn get_pub_key(path: &str) -> Result { + check_path_validity(path)?; + + let xpub_data = get_xpub_data(path, true)?; + let pub_key = &xpub_data[..130]; + + Ok(pub_key.to_string()) + } +} + +#[cfg(test)] +mod test { + use crate::{ + btc_kin_address::{BtcKinAddress, ImkeyPublicKey}, + network::BtcKinNetwork, + }; + use bitcoin::Network; + use ikc_device::device_binding::bind_test; + + #[test] + fn get_xpub_test() { + bind_test(); + + let version: Network = Network::Bitcoin; + let path: &str = "m/44'/0'/0'/0/0"; + let get_xpub_result = ImkeyPublicKey::get_xpub(version, path); + assert!(get_xpub_result.is_ok()); + let xpub = get_xpub_result.ok().unwrap(); + assert_eq!("xpub6FuzpGNBc46EfvmcvECyqXjrzGcKErQgpQcpvhw1tiC5yXvi1jUkzudMpdg5AaguiFstdVR5ASDbSceBswKRy6cAhpTgozmgxMUayPDrLLX", xpub); + } + + #[test] + fn get_xpub_path_error_test() { + bind_test(); + + let version: Network = Network::Bitcoin; + let path: &str = "m/44'"; + let get_xpub_result = ImkeyPublicKey::get_xpub(version, path); + assert!(get_xpub_result.is_err()); + } + + #[test] + fn get_xpub_path_is_null_test() { + bind_test(); + + let version: Network = Network::Bitcoin; + let path: &str = ""; + let get_xpub_result = ImkeyPublicKey::get_xpub(version, path); + assert!(get_xpub_result.is_err()); + } + + #[test] + fn p2pkh_test() { + bind_test(); + + let network = BtcKinNetwork::find_by_coin("BITCOIN", "MAINNET").unwrap(); + let path: &str = "m/44'/0'/0'/0/0"; + let btc_address = BtcKinAddress::p2pkh(network, path).unwrap().to_string(); + + assert_eq!("12z6UzsA3tjpaeuvA2Zr9jwx19Azz74D6g", btc_address); + } + + #[test] + fn p2shwpkh_address_test() { + bind_test(); + + let network = BtcKinNetwork::find_by_coin("BITCOIN", "MAINNET").unwrap(); + let path: &str = "m/49'/0'/0'/0/22"; + let segwit_address = BtcKinAddress::p2shwpkh(network, path).unwrap().to_string(); + + assert_eq!("37E2J9ViM4QFiewo7aw5L3drF2QKB99F9e", segwit_address); + } + #[test] + fn p2wpkh_address_test() { + bind_test(); + + let network = BtcKinNetwork::find_by_coin("BITCOIN", "MAINNET").unwrap(); + let path: &str = "m/49'/0'/0'/0/22"; + let address = BtcKinAddress::p2wpkh(network, path).unwrap().to_string(); + + assert_eq!("bc1qe74h3vkdcj94uph4wdpk48nlqjdy42y87mdm7q", address); + } + + #[test] + fn p2tr_address_test() { + bind_test(); + + let network = BtcKinNetwork::find_by_coin("BITCOIN", "MAINNET").unwrap(); + let path: &str = "m/49'/0'/0'/0/22"; + let address = BtcKinAddress::p2tr(network, path).unwrap().to_string(); + + assert_eq!( + "bc1ph40wj9vl3kwhxq747wxkcr63e4r3uaryagpetnkey4zqhucmjfzse24jrd", + address + ); + } + + #[test] + fn display_address_test() { + bind_test(); + let network = BtcKinNetwork::find_by_coin("BITCOIN", "MAINNET").unwrap(); + let path: &str = "m/44'/0'/0'/0/0"; + let address = BtcKinAddress::display_address(network, path, "NONE").unwrap(); + + assert_eq!("12z6UzsA3tjpaeuvA2Zr9jwx19Azz74D6g", address); + } + + #[test] + fn display_segwit_address_test() { + bind_test(); + let network = BtcKinNetwork::find_by_coin("BITCOIN", "MAINNET").unwrap(); + let path: &str = "m/49'/0'/0'/0/22"; + let address = BtcKinAddress::display_address(network, path, "P2WPKH").unwrap(); + + assert_eq!("37E2J9ViM4QFiewo7aw5L3drF2QKB99F9e", address); + } + + #[test] + fn display_native_segwit_address_test() { + bind_test(); + let network = BtcKinNetwork::find_by_coin("BITCOIN", "MAINNET").unwrap(); + let path: &str = "m/84'/0'/0'"; + let address = BtcKinAddress::display_address(network, path, "VERSION_0").unwrap(); + + assert_eq!("bc1qhuwav68m49d8epty9ztg8yag7ku27jfccyz3hp", address); + } + + #[test] + fn display_taproot_address_test() { + bind_test(); + let network = BtcKinNetwork::find_by_coin("BITCOIN", "MAINNET").unwrap(); + let path: &str = "m/86'/0'/0'"; + let address = BtcKinAddress::display_address(network, path, "VERSION_1").unwrap(); + + assert_eq!( + "bc1p26r56upnktz0qm4vxw3228v956rxsc4sevasswxdvh9ysnq509fqctph3w", + address + ); + } + + #[test] + fn test_dogecoin_address() { + bind_test(); + + let network = BtcKinNetwork::find_by_coin("DOGECOIN", "MAINNET").unwrap(); + let path: &str = "m/44'/3'/0'/0/0"; + let address = BtcKinAddress::p2pkh(network, path).unwrap().to_string(); + assert_eq!("DQ4tVEqdPWHc1aVBm4Sfwft8XyNRPMEchR", address); + + let path: &str = "m/44'/1'/0'/0/0"; + let address = BtcKinAddress::p2shwpkh(network, path).unwrap().to_string(); + assert_eq!("A6tT5rU6MZBzArAVVei5PqqocfxBqJhSqg", address); + + let path: &str = "m/44'/1'/0'/0/0"; + let address = BtcKinAddress::p2wpkh(network, path).unwrap().to_string(); + assert_eq!("1q8qlms89s5078yj67pr8ch02qgvmdwy0k24vwhn", address); + + let path: &str = "m/44'/1'/0'/0/0"; + let address = BtcKinAddress::p2tr(network, path).unwrap().to_string(); + assert_eq!( + "1pd2gajgcpr7c5ajgl377sgmqexw5jxqvl305zw2a7aeujf8pun7ksh45tuj", + address + ); + + let network = BtcKinNetwork::find_by_coin("DOGECOIN", "TESTNET").unwrap(); + let path: &str = "m/44'/3'/0'/0/0"; + let address = BtcKinAddress::p2pkh(network, path).unwrap().to_string(); + assert_eq!("no7xDFaYKUkKtZ4Nnt68C5URmqkiMUTRTE", address); + + let path: &str = "m/44'/1'/0'/0/0"; + let address = BtcKinAddress::p2shwpkh(network, path).unwrap().to_string(); + assert_eq!("2N7hQQkLDtwpSUGRZkefXmfCh8SnKabUcC5", address); + + let path: &str = "m/44'/1'/0'/0/0"; + let address = BtcKinAddress::p2wpkh(network, path).unwrap().to_string(); + assert_eq!("1q8qlms89s5078yj67pr8ch02qgvmdwy0k24vwhn", address); + + let path: &str = "m/44'/1'/0'/0/0"; + let address = BtcKinAddress::p2tr(network, path).unwrap().to_string(); + assert_eq!( + "1pd2gajgcpr7c5ajgl377sgmqexw5jxqvl305zw2a7aeujf8pun7ksh45tuj", + address + ); + } +} diff --git a/imkey-core/ikc-wallet/coin-bitcoin/src/common.rs b/imkey-core/ikc-wallet/coin-bitcoin/src/common.rs index 8a07e4b2..01ca8ece 100644 --- a/imkey-core/ikc-wallet/coin-bitcoin/src/common.rs +++ b/imkey-core/ikc-wallet/coin-bitcoin/src/common.rs @@ -1,12 +1,10 @@ use crate::transaction::Utxo; use crate::Result; -use bitcoin::schnorr::UntweakedPublicKey; use bitcoin::util::base58; -use bitcoin::util::bip32::{ChainCode, ChildNumber, ExtendedPubKey}; -use bitcoin::{Address, AddressType, Network, PublicKey}; +use bitcoin::{Network, PublicKey}; use ikc_common::apdu::{ApduCheck, BtcApdu, CoinCommonApdu}; use ikc_common::error::CoinError; -use ikc_common::utility::{hex_to_bytes, sha256_hash}; +use ikc_common::utility::sha256_hash; use ikc_transport::message::send_apdu; use secp256k1::{ecdsa::Signature, Message, PublicKey as Secp256k1PublicKey, Secp256k1}; use std::str::FromStr; @@ -74,7 +72,11 @@ get address version pub fn get_address_version(network: Network, address: &str) -> Result { let version = match network { Network::Bitcoin => { - if address.starts_with('1') || address.starts_with('3') { + if address.starts_with('1') + || address.starts_with('3') + || address.starts_with('D') + || address.starts_with('A') + { let address_bytes = base58::from(address)?; address_bytes.as_slice()[0] } else if address.starts_with("bc1") { diff --git a/imkey-core/ikc-wallet/coin-bitcoin/src/lib.rs b/imkey-core/ikc-wallet/coin-bitcoin/src/lib.rs index 49980670..9ccbbac7 100644 --- a/imkey-core/ikc-wallet/coin-bitcoin/src/lib.rs +++ b/imkey-core/ikc-wallet/coin-bitcoin/src/lib.rs @@ -1,7 +1,9 @@ pub mod address; +pub mod btc_kin_address; pub mod btcapi; pub mod common; pub mod message; +pub mod network; pub mod psbt; pub mod transaction; pub mod usdt_transaction; diff --git a/imkey-core/ikc-wallet/coin-bitcoin/src/network.rs b/imkey-core/ikc-wallet/coin-bitcoin/src/network.rs new file mode 100644 index 00000000..fc1978af --- /dev/null +++ b/imkey-core/ikc-wallet/coin-bitcoin/src/network.rs @@ -0,0 +1,113 @@ +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct BtcKinNetwork { + pub coin: &'static str, + pub network: &'static str, + + pub bech32_hrp: &'static str, + pub p2pkh_prefix: u8, + pub p2sh_prefix: u8, + pub private_prefix: u8, + + pub xpub_prefix: [u8; 4], + pub xprv_prefix: [u8; 4], +} +const BTC_KIN_NETWORKS: [BtcKinNetwork; 8] = [ + BtcKinNetwork { + coin: "BITCOIN", + network: "MAINNET", + bech32_hrp: "bc", + p2pkh_prefix: 0x0, + p2sh_prefix: 0x05, + private_prefix: 0x80, + xpub_prefix: [0x04, 0x88, 0xb2, 0x1e], + xprv_prefix: [0x04, 0x88, 0xad, 0xe4], + }, + BtcKinNetwork { + coin: "BITCOIN", + network: "TESTNET", + bech32_hrp: "tb", + p2pkh_prefix: 0x6f, + p2sh_prefix: 0xc4, + private_prefix: 0xef, + xpub_prefix: [0x04, 0x35, 0x87, 0xcf], + xprv_prefix: [0x04, 0x35, 0x83, 0x94], + }, + BtcKinNetwork { + coin: "LITECOIN", + network: "MAINNET", + bech32_hrp: "ltc", + p2pkh_prefix: 0x30, + p2sh_prefix: 0x32, + private_prefix: 0xb0, + xpub_prefix: [0x04, 0x88, 0xb2, 0x1e], + xprv_prefix: [0x04, 0x88, 0xad, 0xe4], + }, + BtcKinNetwork { + coin: "LITECOIN", + network: "TESTNET", + bech32_hrp: "tltc", + p2pkh_prefix: 0x6f, + p2sh_prefix: 0x3a, + private_prefix: 0xef, + xpub_prefix: [0x04, 0x35, 0x87, 0xcf], + xprv_prefix: [0x04, 0x35, 0x83, 0x94], + }, + BtcKinNetwork { + coin: "BITCOINCASH", + network: "MAINNET", + bech32_hrp: "bitcoincash", + p2pkh_prefix: 0x0, + p2sh_prefix: 0x05, + private_prefix: 0x80, + xpub_prefix: [0x04, 0x88, 0xb2, 0x1e], + xprv_prefix: [0x04, 0x88, 0xad, 0xe4], + }, + BtcKinNetwork { + coin: "BITCOINCASH", + network: "TESTNET", + bech32_hrp: "bchtest", + p2pkh_prefix: 0x6f, + p2sh_prefix: 0xc4, + private_prefix: 0xef, + xpub_prefix: [0x04, 0x35, 0x87, 0xcf], + xprv_prefix: [0x04, 0x35, 0x83, 0x94], + }, + BtcKinNetwork { + coin: "DOGECOIN", + network: "TESTNET", + bech32_hrp: "", + p2pkh_prefix: 0x71, + p2sh_prefix: 0xc4, + private_prefix: 0xf1, + xpub_prefix: [0x04, 0x35, 0x87, 0xcf], + xprv_prefix: [0x04, 0x35, 0x83, 0x94], + }, + BtcKinNetwork { + coin: "DOGECOIN", + network: "MAINNET", + bech32_hrp: "", + p2pkh_prefix: 0x1e, + p2sh_prefix: 0x16, + private_prefix: 0x9e, + xpub_prefix: [0x02, 0xfa, 0xca, 0xfd], + xprv_prefix: [0x02, 0xfa, 0xc3, 0x98], + }, +]; + +impl BtcKinNetwork { + pub fn find_by_coin<'a>(coin: &str, network: &str) -> Option<&'a BtcKinNetwork> { + BTC_KIN_NETWORKS + .iter() + .find(|net| net.coin == coin && net.network == network) + } + + pub fn find_by_hrp<'a>(hrp: &str) -> Option<&'a BtcKinNetwork> { + BTC_KIN_NETWORKS.iter().find(|net| net.bech32_hrp == hrp) + } + + pub fn find_by_prefix<'a>(prefix: u8) -> Option<&'a BtcKinNetwork> { + BTC_KIN_NETWORKS + .iter() + .find(|net| net.p2pkh_prefix == prefix || net.p2sh_prefix == prefix) + } +} diff --git a/imkey-core/ikc-wallet/coin-bitcoin/src/psbt.rs b/imkey-core/ikc-wallet/coin-bitcoin/src/psbt.rs index 4fd610f4..e46e1ba1 100644 --- a/imkey-core/ikc-wallet/coin-bitcoin/src/psbt.rs +++ b/imkey-core/ikc-wallet/coin-bitcoin/src/psbt.rs @@ -5,13 +5,12 @@ use crate::Result; use anyhow::anyhow; use bitcoin::blockdata::script::Builder; use bitcoin::consensus::{serialize, Decodable, Encodable}; -use bitcoin::psbt::serialize::Serialize; use bitcoin::psbt::Psbt; -use bitcoin::schnorr::{TapTweak, UntweakedPublicKey}; +use bitcoin::schnorr::UntweakedPublicKey; use bitcoin::util::taproot::{TapLeafHash, TapTweakHash}; use bitcoin::{ - Address, EcdsaSig, EcdsaSighashType, Network, PackedLockTime, PublicKey, SchnorrSig, - SchnorrSighashType, Script, Transaction, TxOut, WPubkeyHash, Witness, + Address, EcdsaSig, EcdsaSighashType, Network, PublicKey, SchnorrSig, SchnorrSighashType, + Script, TxOut, WPubkeyHash, Witness, }; use bitcoin_hashes::hex::ToHex; use bitcoin_hashes::{hash160, Hash}; @@ -221,7 +220,7 @@ impl<'a> PsbtSigner<'a> { } fn sign_p2sh_nested_p2wpkh(&mut self, idx: usize, pub_key: &str) -> Result<()> { - let mut temp_serialize_txin = self + let temp_serialize_txin = self .psbt .unsigned_tx .input @@ -283,7 +282,7 @@ impl<'a> PsbtSigner<'a> { } fn sign_p2wpkh(&mut self, idx: usize, pub_key: &str) -> Result<()> { - let mut temp_serialize_txin = self + let temp_serialize_txin = self .psbt .unsigned_tx .input @@ -473,7 +472,7 @@ impl<'a> PsbtSigner<'a> { } pub fn tx_preview(&self, network: Network) -> Result<()> { - let (total_amount, fee, outputs) = self.get_preview_info()?; + let (total_amount, fee, _outputs) = self.get_preview_info()?; let mut preview_data = vec![]; preview_data.extend(&serialize(&self.psbt.unsigned_tx.version)); //version let input_number = self.psbt.unsigned_tx.input.len(); @@ -545,7 +544,7 @@ impl<'a> PsbtSigner<'a> { } fn finalize_p2pkh(&mut self, index: usize) { - let mut input = &mut self.psbt.inputs[index]; + let input = &mut self.psbt.inputs[index]; if !input.partial_sigs.is_empty() { let sig = input.partial_sigs.first_key_value().unwrap(); @@ -560,7 +559,7 @@ impl<'a> PsbtSigner<'a> { } fn finalize_p2sh_nested_p2wpkh(&mut self, index: usize) { - let mut input = &mut self.psbt.inputs[index]; + let input = &mut self.psbt.inputs[index]; if !input.partial_sigs.is_empty() { let sig = input.partial_sigs.first_key_value().unwrap(); @@ -579,7 +578,7 @@ impl<'a> PsbtSigner<'a> { } fn finalize_p2wpkh(&mut self, index: usize) { - let mut input = &mut self.psbt.inputs[index]; + let input = &mut self.psbt.inputs[index]; if !input.partial_sigs.is_empty() { let sig = input.partial_sigs.first_key_value().unwrap(); @@ -593,7 +592,7 @@ impl<'a> PsbtSigner<'a> { } fn finalize_p2tr(&mut self, index: usize) { - let mut input = &mut self.psbt.inputs[index]; + let input = &mut self.psbt.inputs[index]; if input.tap_key_sig.is_some() { let mut witness = Witness::new(); @@ -612,7 +611,7 @@ impl<'a> PsbtSigner<'a> { } fn clear_finalized_input(&mut self, index: usize) { - let mut input = &mut self.psbt.inputs[index]; + let input = &mut self.psbt.inputs[index]; input.tap_key_sig = None; input.tap_scripts = BTreeMap::new(); input.tap_internal_key = None; @@ -691,7 +690,7 @@ impl<'a> PsbtSigner<'a> { } pub fn get_preview_info(&self) -> Result<(u64, u64, Vec)> { - let mut outputs = &self.preview_output; + let outputs = &self.preview_output; let payment_total_amount = outputs.iter().map(|tx_out| tx_out.value).sum(); let input_total_amount: u64 = self.prevouts.iter().map(|tx_out| tx_out.value).sum(); let output_total_amount: u64 = self diff --git a/imkey-core/ikc-wallet/coin-bitcoin/src/transaction.rs b/imkey-core/ikc-wallet/coin-bitcoin/src/transaction.rs index 26122373..9d93fe62 100644 --- a/imkey-core/ikc-wallet/coin-bitcoin/src/transaction.rs +++ b/imkey-core/ikc-wallet/coin-bitcoin/src/transaction.rs @@ -1,15 +1,15 @@ -use crate::address::BtcAddress; +use crate::btc_kin_address::{AddressTrait, BtcKinAddress, ImkeyPublicKey}; use crate::common::{get_address_version, get_utxo_pub_key, TxSignResult}; +use crate::network::BtcKinNetwork; use crate::Result; use bitcoin::blockdata::{opcodes, script::Builder}; use bitcoin::consensus::{serialize, Encodable}; use bitcoin::hashes::hex::FromHex; -use bitcoin::psbt::serialize::Serialize; -use bitcoin::schnorr::{TapTweak, UntweakedPublicKey}; +use bitcoin::schnorr::UntweakedPublicKey; use bitcoin::util::taproot::TapTweakHash; use bitcoin::{ - Address, EcdsaSighashType, Network, OutPoint, PackedLockTime, SchnorrSighashType, Script, - Sequence, Transaction, TxIn, TxOut, WPubkeyHash, Witness, + Address, EcdsaSighashType, OutPoint, PackedLockTime, SchnorrSighashType, Script, Sequence, + Transaction, TxIn, TxOut, WPubkeyHash, Witness, }; use bitcoin_hashes::hash160; use bitcoin_hashes::hex::ToHex; @@ -19,39 +19,42 @@ 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::error::{CoinError, CommonError}; 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_common::utility::{bigint_to_byte_vec, hex_to_bytes, network_convert, 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; -use std::borrow::Borrow; use std::str::FromStr; +//The address version set supported by bitcoin applet versions lower than 1.6.00 +const VALID_ADDRESS_VERSIONS: [u8; 7] = [0, 111, 5, 196, 113, 30, 22]; + #[derive(Clone)] pub struct Utxo { pub txhash: String, pub vout: u32, pub amount: u64, - pub address: Address, + pub address: String, pub script_pubkey: String, pub derive_path: String, pub sequence: i64, } pub struct BtcTransaction { - pub to: Address, + pub to: String, pub amount: u64, pub unspents: Vec, pub fee: u64, + pub chain_type: String, } impl BtcTransaction { - pub fn sign_Transaction( + pub fn sign_transaction( &self, - network: Network, + network: &str, path: &str, change_idx: Option, extra_data: Option<&str>, @@ -120,26 +123,24 @@ impl BtcTransaction { 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)?; + let address_version = + get_address_version(network_convert(network), &self.to.to_string())?; + if VALID_ADDRESS_VERSIONS.contains(&address_version) { + 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()); - } + } else { + return Err(CoinError::InvalidAddress.into()); } } @@ -379,8 +380,8 @@ impl BtcTransaction { let txhash_data = serialize(&txin.previous_output); data.extend(txhash_data.iter()); //lock script - let script = unspent - .address + let address = Address::from_str(&unspent.address)?; + let script = address .script_pubkey() .p2wpkh_script_code() .expect("must be v0_p2wpkh"); @@ -499,7 +500,7 @@ impl BtcTransaction { pub fn build_send_to_output(&self) -> TxOut { TxOut { value: self.amount as u64, - script_pubkey: self.to.script_pubkey(), + script_pubkey: BtcKinAddress::from_str(&self.to).unwrap().script_pubkey() } } @@ -532,7 +533,7 @@ impl BtcTransaction { &self, change_idx: Option, change_path: &str, - network: Network, + network: &str, seg_wit: &str, extra_data: Option<&str>, ) -> Result> { @@ -548,12 +549,19 @@ impl BtcTransaction { "/1/", change_address_index ); - let pub_key = BtcAddress::get_pub_key(&change_path)?; - let change_address = BtcAddress::from_public_key(&pub_key, network, seg_wit)?; - let change_address = Address::from_str(&change_address)?; + let pub_key = ImkeyPublicKey::get_pub_key(&change_path)?; + let network = BtcKinNetwork::find_by_coin(&self.chain_type, network); + if network.is_none() { + return Err(CommonError::MissingNetwork.into()); + } + + let change_address = + BtcKinAddress::from_public_key(&pub_key, network.unwrap(), seg_wit)?; change_address.script_pubkey() + // let change_address = Address::from_str(&change_address)?; + // change_address.script_pubkey() } else { - Address::from_str(&self.unspents[0].address.to_string())?.script_pubkey() + BtcKinAddress::from_str(&self.unspents[0].address.to_string())?.script_pubkey() }; outputs.push(TxOut { value: self.get_change_amount(), @@ -582,7 +590,8 @@ impl BtcTransaction { let mut amount_vec = vec![]; let mut script_pubkeys_vec = vec![]; for unspent in self.unspents.iter() { - if !unspent.address.script_pubkey().is_p2pkh() { + let address = BtcKinAddress::from_str(&unspent.address)?; + if !address.script_pubkey().is_p2pkh() { transaction.version = 2i32; } @@ -599,7 +608,7 @@ impl BtcTransaction { txhash_vout_vec.extend(serialize(&tx_in.previous_output)); sequence_vec.extend(serialize(&tx_in.sequence)); amount_vec.extend(serialize(&unspent.amount)); - script_pubkeys_vec.extend(serialize(&unspent.address.script_pubkey())); + script_pubkeys_vec.extend(serialize(&address.script_pubkey())); } if transaction.version == 2 { let mut calc_hash_apdu = vec![]; @@ -616,7 +625,7 @@ impl BtcTransaction { Ok(()) } - pub fn tx_preview(&self, transaction: &Transaction, network: Network) -> Result<()> { + pub fn tx_preview(&self, transaction: &Transaction, network: &str) -> Result<()> { let mut output_serialize_data = serialize(&transaction); output_serialize_data.remove(5); @@ -638,6 +647,7 @@ impl BtcTransaction { output_serialize_data.extend(bigint_to_byte_vec(self.fee)); //add address version + let network = network_convert(network); let address_version = get_address_version(network, self.to.to_string().as_str())?; output_serialize_data.push(address_version); @@ -664,7 +674,7 @@ impl BtcTransaction { /** *original Nested Segwit transaction preview **/ - pub fn original_tx_preview(&self, transaction: &Transaction, network: Network) -> Result<()> { + pub fn original_tx_preview(&self, transaction: &Transaction, network: &str) -> Result<()> { let mut output_serialize_data = serialize(&transaction); output_serialize_data.remove(5); @@ -687,6 +697,7 @@ impl BtcTransaction { output_serialize_data.extend(bigint_to_byte_vec(self.fee)); //add address version + let network = network_convert(network); let address_version = get_address_version(network, self.to.to_string().as_str())?; output_serialize_data.push(address_version); @@ -735,7 +746,7 @@ mod tests { .to_string(), vout: 0, amount: 200000000, - address: Address::from_str("mh7jj2ELSQUvRQELbn9qyA4q5nADhmJmUC").unwrap(), + address: "mh7jj2ELSQUvRQELbn9qyA4q5nADhmJmUC".to_string(), script_pubkey: "76a914118c3123196e030a8a607c22bafc1577af61497d88ac".to_string(), derive_path: "m/44'/1'/0'/0/22".to_string(), sequence: 4294967295, @@ -745,7 +756,7 @@ mod tests { .to_string(), vout: 1, amount: 200000000, - address: Address::from_str("mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN").unwrap(), + address: "mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN".to_string(), script_pubkey: "76a914383fb81cb0a3fc724b5e08cf8bbd404336d711f688ac".to_string(), derive_path: "m/44'/1'/0'/0/0".to_string(), sequence: 4294967295, @@ -755,7 +766,7 @@ mod tests { .to_string(), vout: 0, amount: 200000000, - address: Address::from_str("mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN").unwrap(), + address: "mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN".to_string(), script_pubkey: "76a914383fb81cb0a3fc724b5e08cf8bbd404336d711f688ac".to_string(), derive_path: "m/44'/1'/0'/0/0".to_string(), sequence: 4294967295, @@ -765,7 +776,7 @@ mod tests { .to_string(), vout: 1, amount: 200000000, - address: Address::from_str("mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN").unwrap(), + address: "mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN".to_string(), script_pubkey: "76a914383fb81cb0a3fc724b5e08cf8bbd404336d711f688ac".to_string(), derive_path: "m/44'/1'/0'/0/0".to_string(), sequence: 4294967295, @@ -773,13 +784,14 @@ mod tests { ]; let transaction = BtcTransaction { - to: Address::from_str("moLK3tBG86ifpDDTqAQzs4a9cUoNjVLRE3").unwrap(), + to: "moLK3tBG86ifpDDTqAQzs4a9cUoNjVLRE3".to_string(), amount: 799988000, unspents: utxos, fee: 10000, + chain_type: "DOGECOIN".to_string(), }; - let sign_result = transaction.sign_Transaction( - Network::Testnet, + let sign_result = transaction.sign_transaction( + "TESTNET", &"m/44'/1'/0'".to_string(), Some(53), Some("0200000080a10bc28928f4c17a287318125115c3f098ed20a8237d1e8e4125bc25d1be99752adad0a7b9ceca853768aebb6965eca126a62965f698a0c1bc43d83db632ad7f717276057e6012afa99385"), @@ -810,7 +822,7 @@ mod tests { .to_string(), vout: 0, amount: 50000, - address: Address::from_str("2MwN441dq8qudMvtM5eLVwC3u4zfKuGSQAB").unwrap(), + address: "2MwN441dq8qudMvtM5eLVwC3u4zfKuGSQAB".to_string(), script_pubkey: "a9142d2b1ef5ee4cf6c3ebc8cf66a602783798f7875987".to_string(), derive_path: "m/49'/1'/0'/0/0".to_string(), sequence: 0, @@ -820,7 +832,7 @@ mod tests { .to_string(), vout: 0, amount: 50000, - address: Address::from_str("2N54wJxopnWTvBfqgAPVWqXVEdaqoH7Suvf").unwrap(), + address: "2N54wJxopnWTvBfqgAPVWqXVEdaqoH7Suvf".to_string(), script_pubkey: "a91481af6d803fdc6dca1f3a1d03f5ffe8124cd1b44787".to_string(), derive_path: "m/49'/1'/0'/0/1".to_string(), sequence: 0, @@ -828,13 +840,14 @@ mod tests { ]; let transaction = BtcTransaction { - to: Address::from_str("2N9wBy6f1KTUF5h2UUeqRdKnBT6oSMh4Whp").unwrap(), + to: "2N9wBy6f1KTUF5h2UUeqRdKnBT6oSMh4Whp".to_string(), amount: 88000, unspents: utxos, fee: 10000, + chain_type: "BITCOIN".to_string(), }; - let sign_result = transaction.sign_Transaction( - Network::Testnet, + let sign_result = transaction.sign_transaction( + "TESTNET", &"m/49'/1'/0'".to_string(), Some(0), Some("1234"), @@ -859,20 +872,21 @@ mod tests { txhash: "d7c2e585d5eaa185808addb3ef703f2a8fe09288b4f40b757a812d6d63b7c9c4".to_string(), vout: 1, amount: 100000, - address: Address::from_str("tb1qv48mkzpx0u74p4c44rc6hd2e0xckph2muvy76k").unwrap(), + address: "tb1qv48mkzpx0u74p4c44rc6hd2e0xckph2muvy76k".to_string(), script_pubkey: "0014654fbb08267f3d50d715a8f1abb55979b160dd5b".to_string(), derive_path: "m/49'/1'/0'/0/0".to_string(), sequence: 0, }]; let transaction = BtcTransaction { - to: Address::from_str("tb1qv48mkzpx0u74p4c44rc6hd2e0xckph2muvy76k").unwrap(), + to: "tb1qv48mkzpx0u74p4c44rc6hd2e0xckph2muvy76k".to_string(), amount: 88000, unspents: utxos, fee: 10000, + chain_type: "BITCOIN".to_string(), }; - let sign_result = transaction.sign_Transaction( - Network::Testnet, + let sign_result = transaction.sign_transaction( + "TESTNET", &"m/49'/1'/0'".to_string(), Some(0), None, @@ -900,20 +914,21 @@ mod tests { txhash: "7c99f906e291d453b2c039939598eefd182dafb20d53bd0eebc2a1aa635ff60f".to_string(), vout: 0, amount: 88000, - address: Address::from_str("tb1qv48mkzpx0u74p4c44rc6hd2e0xckph2muvy76k").unwrap(), + address: "tb1qv48mkzpx0u74p4c44rc6hd2e0xckph2muvy76k".to_string(), script_pubkey: "0014654fbb08267f3d50d715a8f1abb55979b160dd5b".to_string(), derive_path: "m/49'/1'/0'/0/0".to_string(), sequence: 0, }]; let transaction = BtcTransaction { - to: Address::from_str("tb1qv48mkzpx0u74p4c44rc6hd2e0xckph2muvy76k").unwrap(), + to: "tb1qv48mkzpx0u74p4c44rc6hd2e0xckph2muvy76k".to_string(), amount: 50000, unspents: utxos, fee: 10000, + chain_type: "BITCOIN".to_string(), }; - let sign_result = transaction.sign_Transaction( - Network::Testnet, + let sign_result = transaction.sign_transaction( + "TESTNET", &"m/49'/1'/0'".to_string(), Some(0), None, @@ -941,20 +956,21 @@ mod tests { txhash: "64381306678c6a868e8778adee1ee9d1746e5e8dd3535fcbaa1a25baab49f015".to_string(), vout: 1, amount: 100000, - address: Address::from_str("tb1qv48mkzpx0u74p4c44rc6hd2e0xckph2muvy76k").unwrap(), + address: "tb1qv48mkzpx0u74p4c44rc6hd2e0xckph2muvy76k".to_string(), script_pubkey: "0014654fbb08267f3d50d715a8f1abb55979b160dd5b".to_string(), derive_path: "m/49'/1'/0'/0/0".to_string(), sequence: 0, }]; let transaction = BtcTransaction { - to: Address::from_str("mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN").unwrap(), + to: "mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN".to_string(), amount: 30000, unspents: utxos, fee: 8000, + chain_type: "BITCOIN".to_string(), }; - let sign_result = transaction.sign_Transaction( - Network::Testnet, + let sign_result = transaction.sign_transaction( + "TESTNET", &"m/49'/1'/0'".to_string(), Some(0), None, @@ -982,20 +998,21 @@ mod tests { txhash: "fcc622970fd80c14b111ee7950bcc309469b575194072209598176123fd06598".to_string(), vout: 0, amount: 50000, - address: Address::from_str("tb1qv48mkzpx0u74p4c44rc6hd2e0xckph2muvy76k").unwrap(), + address: "tb1qv48mkzpx0u74p4c44rc6hd2e0xckph2muvy76k".to_string(), script_pubkey: "0014654fbb08267f3d50d715a8f1abb55979b160dd5b".to_string(), derive_path: "m/49'/1'/0'/0/0".to_string(), sequence: 0, }]; let transaction = BtcTransaction { - to: Address::from_str("2MwN441dq8qudMvtM5eLVwC3u4zfKuGSQAB").unwrap(), + to: "2MwN441dq8qudMvtM5eLVwC3u4zfKuGSQAB".to_string(), amount: 30000, unspents: utxos, fee: 7000, + chain_type: "BITCOIN".to_string(), }; - let sign_result = transaction.sign_Transaction( - Network::Testnet, + let sign_result = transaction.sign_transaction( + "TESTNET", &"m/49'/1'/0'".to_string(), Some(0), None, @@ -1023,19 +1040,20 @@ mod tests { txhash: "eb3ea0d4b360a304849b90baf49197eb449ca746febd60f8f29cd279c966a3ea".to_string(), vout: 0, amount: 30000, - address: Address::from_str("mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN").unwrap(), + address: "mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN".to_string(), script_pubkey: "76a914383fb81cb0a3fc724b5e08cf8bbd404336d711f688ac".to_string(), derive_path: "m/44'/1'/0'/0/0".to_string(), sequence: 4294967295, }]; let transaction = BtcTransaction { - to: Address::from_str("tb1qv48mkzpx0u74p4c44rc6hd2e0xckph2muvy76k").unwrap(), + to: "tb1qv48mkzpx0u74p4c44rc6hd2e0xckph2muvy76k".to_string(), amount: 25000, unspents: utxos, fee: 5000, + chain_type: "BITCOIN".to_string(), }; - let sign_result = transaction.sign_Transaction( - Network::Testnet, + let sign_result = transaction.sign_transaction( + "TESTNET", &"m/44'/1'/0'".to_string(), Some(0), None, @@ -1059,20 +1077,21 @@ mod tests { txhash: "e5add8950cb37b1d80ff18cb2ba775e185e1843b845e18b532dc4b5d8ffec7a9".to_string(), vout: 0, amount: 30000, - address: Address::from_str("2MwN441dq8qudMvtM5eLVwC3u4zfKuGSQAB").unwrap(), + address: "2MwN441dq8qudMvtM5eLVwC3u4zfKuGSQAB".to_string(), script_pubkey: "a9142d2b1ef5ee4cf6c3ebc8cf66a602783798f7875987".to_string(), derive_path: "m/49'/1'/0'/0/0".to_string(), sequence: 0, }]; let transaction = BtcTransaction { - to: Address::from_str("tb1qv48mkzpx0u74p4c44rc6hd2e0xckph2muvy76k").unwrap(), + to: "tb1qv48mkzpx0u74p4c44rc6hd2e0xckph2muvy76k".to_string(), amount: 26000, unspents: utxos, fee: 4000, + chain_type: "BITCOIN".to_string(), }; - let sign_result = transaction.sign_Transaction( - Network::Testnet, + let sign_result = transaction.sign_transaction( + "TESTNET", &"m/49'/1'/0'".to_string(), Some(0), Some("1234"), @@ -1102,7 +1121,7 @@ mod tests { .to_string(), vout: 0, amount: 26000, - address: Address::from_str("tb1qv48mkzpx0u74p4c44rc6hd2e0xckph2muvy76k").unwrap(), + address: "tb1qv48mkzpx0u74p4c44rc6hd2e0xckph2muvy76k".to_string(), script_pubkey: "0014654fbb08267f3d50d715a8f1abb55979b160dd5b".to_string(), derive_path: "m/49'/1'/0'/0/0".to_string(), sequence: 0, @@ -1112,7 +1131,7 @@ mod tests { .to_string(), vout: 1, amount: 13000, - address: Address::from_str("tb1qvg35wefk2h2ha68g7fvc8ajxhn0ec5pjekus6j").unwrap(), + address: "tb1qvg35wefk2h2ha68g7fvc8ajxhn0ec5pjekus6j".to_string(), script_pubkey: "0014622347653655d57ee8e8f25983f646bcdf9c5032".to_string(), derive_path: "m/49'/1'/0'/1/0".to_string(), sequence: 0, @@ -1120,13 +1139,14 @@ mod tests { ]; let transaction = BtcTransaction { - to: Address::from_str("tb1qv48mkzpx0u74p4c44rc6hd2e0xckph2muvy76k").unwrap(), + to: "tb1qv48mkzpx0u74p4c44rc6hd2e0xckph2muvy76k".to_string(), amount: 31000, unspents: utxos, fee: 5000, + chain_type: "BITCOIN".to_string(), }; - let sign_result = transaction.sign_Transaction( - Network::Testnet, + let sign_result = transaction.sign_transaction( + "TESTNET", &"m/49'/1'/0'".to_string(), Some(0), None, @@ -1154,20 +1174,21 @@ mod tests { txhash: "32f734241b2dee423ee736ddfd26ea341d56a0ded67f4e1c658d0119977c1b3a".to_string(), vout: 0, amount: 100000, - address: Address::from_str("2MwN441dq8qudMvtM5eLVwC3u4zfKuGSQAB").unwrap(), + address: "2MwN441dq8qudMvtM5eLVwC3u4zfKuGSQAB".to_string(), script_pubkey: "a9142d2b1ef5ee4cf6c3ebc8cf66a602783798f7875987".to_string(), derive_path: "m/49'/1'/0'/0/0".to_string(), sequence: 0, }]; let transaction = BtcTransaction { - to: Address::from_str("2N5z4KZBCQNULTegkETDftMiNHWEFjrH3m2").unwrap(), + to: "2N5z4KZBCQNULTegkETDftMiNHWEFjrH3m2".to_string(), amount: 90000, unspents: utxos, fee: 10000, + chain_type: "BITCOIN".to_string(), }; let sign_result = - transaction.sign_Transaction(Network::Testnet, "m/49'/1'/0'", Some(0), None, "P2WPKH"); + transaction.sign_transaction("TESTNET", "m/49'/1'/0'", Some(0), None, "P2WPKH"); assert_eq!( "020000000001013a1b7c9719018d651c4e7fd6dea0561d34ea26fddd36e73e42ee2d1b2434f7320000000017160014654fbb08267f3d50d715a8f1abb55979b160dd5bffffffff01905f01000000000017a9148bbb53570df9656926ea0ef029cd2ee84dbc7d0f870247304402202931423820466e0554d99eb93d6c9b6a1b7270c21e1ed7279f98152247103ab602201df7809aa81b66bace7131a260fb1de661c9da9d6ddbb82ceac3c6bbb043122f0121031aee5e20399d68cf0035d1a21564868f22bc448ab205292b4279136b15ecaebc00000000", @@ -1193,7 +1214,7 @@ mod tests { .to_string(), vout: 1, amount: 100000, - address: Address::from_str("mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN").unwrap(), + address: "mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN".to_string(), script_pubkey: "76a914383fb81cb0a3fc724b5e08cf8bbd404336d711f688ac".to_string(), derive_path: "m/44'/1'/0'/0/0".to_string(), sequence: 0, @@ -1203,7 +1224,7 @@ mod tests { .to_string(), vout: 1, amount: 1418852, - address: Address::from_str("2MwN441dq8qudMvtM5eLVwC3u4zfKuGSQAB").unwrap(), + address: "2MwN441dq8qudMvtM5eLVwC3u4zfKuGSQAB".to_string(), script_pubkey: "a9142d2b1ef5ee4cf6c3ebc8cf66a602783798f7875987".to_string(), derive_path: "m/49'/1'/0'/0/0".to_string(), sequence: 0, @@ -1211,13 +1232,14 @@ mod tests { ]; let transaction = BtcTransaction { - to: Address::from_str("2N5z4KZBCQNULTegkETDftMiNHWEFjrH3m2").unwrap(), + to: "2N5z4KZBCQNULTegkETDftMiNHWEFjrH3m2".to_string(), amount: 1508852, unspents: utxos, fee: 10000, + chain_type: "BITCOIN".to_string(), }; - let sign_result = transaction.sign_Transaction( - Network::Testnet, + let sign_result = transaction.sign_transaction( + "TESTNET", &"m/49'/1'/0'/0/0".to_string(), Some(53), None, @@ -1246,20 +1268,21 @@ mod tests { txhash: "41eb7058313847d1f1b0cfee964a436d55eab5ca29fdbb42dbb5107a85afdda7".to_string(), vout: 1, amount: 100000, - address: Address::from_str("tb1qrfaf3g4elgykshfgahktyaqj2r593qkrae5v95").unwrap(), + address: "tb1qrfaf3g4elgykshfgahktyaqj2r593qkrae5v95".to_string(), script_pubkey: "00141a7a98a2b9fa09685d28edecb2741250e85882c3".to_string(), derive_path: "m/84'/1'/0'/0/0".to_string(), sequence: 0, }]; let transaction = BtcTransaction { - to: Address::from_str("tb1qpma75pm648xmd9tfzah029edarqn4xtndqhp99").unwrap(), + to: "tb1qpma75pm648xmd9tfzah029edarqn4xtndqhp99".to_string(), amount: 30000, unspents: utxos, fee: 10000, + chain_type: "BITCOIN".to_string(), }; - let sign_result = transaction.sign_Transaction( - Network::Testnet, + let sign_result = transaction.sign_transaction( + "TESTNET", &"m/84'/1'/0'".to_string(), Some(53), None, @@ -1290,7 +1313,7 @@ mod tests { .to_string(), vout: 1, amount: 100000, - address: Address::from_str("tb1qrfaf3g4elgykshfgahktyaqj2r593qkrae5v95").unwrap(), + address: "tb1qrfaf3g4elgykshfgahktyaqj2r593qkrae5v95".to_string(), script_pubkey: "00141a7a98a2b9fa09685d28edecb2741250e85882c3".to_string(), derive_path: "m/84'/1'/0'/0/0".to_string(), sequence: 0, @@ -1300,7 +1323,7 @@ mod tests { .to_string(), vout: 1, amount: 100000, - address: Address::from_str("tb1qrfaf3g4elgykshfgahktyaqj2r593qkrae5v95").unwrap(), + address: "tb1qrfaf3g4elgykshfgahktyaqj2r593qkrae5v95".to_string(), script_pubkey: "00141a7a98a2b9fa09685d28edecb2741250e85882c3".to_string(), derive_path: "m/84'/1'/0'/0/0".to_string(), sequence: 0, @@ -1308,13 +1331,14 @@ mod tests { ]; let transaction_req_data = BtcTransaction { - to: Address::from_str("tb1qpma75pm648xmd9tfzah029edarqn4xtndqhp99").unwrap(), + to: "tb1qpma75pm648xmd9tfzah029edarqn4xtndqhp99".to_string(), amount: 110000, unspents: utxos, fee: 20000, + chain_type: "BITCOIN".to_string(), }; - let sign_result = transaction_req_data.sign_Transaction( - Network::Testnet, + let sign_result = transaction_req_data.sign_transaction( + "TESTNET", &"m/84'/1'/0'".to_string(), Some(53), None, @@ -1346,7 +1370,7 @@ mod tests { .to_string(), vout: 0, amount: 100000, - address: Address::from_str("2MwN441dq8qudMvtM5eLVwC3u4zfKuGSQAB").unwrap(), + address: "2MwN441dq8qudMvtM5eLVwC3u4zfKuGSQAB".to_string(), script_pubkey: "a9142d2b1ef5ee4cf6c3ebc8cf66a602783798f7875987".to_string(), derive_path: "m/49'/1'/0'/0/0".to_string(), sequence: 0, @@ -1356,7 +1380,7 @@ mod tests { .to_string(), vout: 0, amount: 100000, - address: Address::from_str("tb1qrfaf3g4elgykshfgahktyaqj2r593qkrae5v95").unwrap(), + address: "tb1qrfaf3g4elgykshfgahktyaqj2r593qkrae5v95".to_string(), script_pubkey: "00141a7a98a2b9fa09685d28edecb2741250e85882c3".to_string(), derive_path: "m/84'/1'/0'/0/0".to_string(), sequence: 0, @@ -1364,18 +1388,14 @@ mod tests { ]; let transaction = BtcTransaction { - to: Address::from_str("tb1qpma75pm648xmd9tfzah029edarqn4xtndqhp99").unwrap(), + to: "tb1qpma75pm648xmd9tfzah029edarqn4xtndqhp99".to_string(), amount: 90000, unspents: utxos, fee: 10000, + chain_type: "BITCOIN".to_string(), }; - let sign_result = transaction.sign_Transaction( - Network::Testnet, - "m/84'/1'/0'", - Some(53), - None, - "VERSION_0", - ); + let sign_result = + transaction.sign_transaction("TESTNET", "m/84'/1'/0'", Some(53), None, "VERSION_0"); assert_eq!( "02000000000102d7577189012a74ad94f3f5d18ec7f1b2d35fcaf3d9ce5a83fb036d1cfe37790a0000000017160014654fbb08267f3d50d715a8f1abb55979b160dd5bffffffffdd4ec3a05e81576ab1ef90fe6efbf3fafbf090b4121368e7a1c6344b62ccfb940000000000ffffffff02905f0100000000001600140efbea077aa9cdb69569176ef5172de8c13a9973a0860100000000001600147805a6361d2532deac1b62c93288aa159308dcc002483045022100e44a802d1a9f70e4087541808b39f4ba4b455f6371b471fa0cc122e2e8a163500220423a35e6c79cbe6287cde4b771b37966d6627defac5d0ed57b53e6c9ffa57c1f0121031aee5e20399d68cf0035d1a21564868f22bc448ab205292b4279136b15ecaebc02473044022018b55722a8c933fcb75309aedb4269d55d2e32549b431822f09019013785b8aa02205980d4f9233bae825cad9cf37b59aeac8fded55c450c59ce02f2bc4bb62352a3012102e24f625a31c9a8bae42239f2bf945a306c01a450a03fd123316db0e837a660c000000000", @@ -1402,7 +1422,7 @@ mod tests { .to_string(), vout: 1, amount: 100000, - address: Address::from_str("mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN").unwrap(), + address: "mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN".to_string(), script_pubkey: "76a914383fb81cb0a3fc724b5e08cf8bbd404336d711f688ac".to_string(), derive_path: "m/44'/1'/0'/0/0".to_string(), sequence: 0, @@ -1412,7 +1432,7 @@ mod tests { .to_string(), vout: 0, amount: 100000, - address: Address::from_str("2MwN441dq8qudMvtM5eLVwC3u4zfKuGSQAB").unwrap(), + address: "2MwN441dq8qudMvtM5eLVwC3u4zfKuGSQAB".to_string(), script_pubkey: "a9142d2b1ef5ee4cf6c3ebc8cf66a602783798f7875987".to_string(), derive_path: "m/49'/1'/0'/0/0".to_string(), sequence: 0, @@ -1422,7 +1442,7 @@ mod tests { .to_string(), vout: 1, amount: 100000, - address: Address::from_str("tb1qrfaf3g4elgykshfgahktyaqj2r593qkrae5v95").unwrap(), + address: "tb1qrfaf3g4elgykshfgahktyaqj2r593qkrae5v95".to_string(), script_pubkey: "00141a7a98a2b9fa09685d28edecb2741250e85882c3".to_string(), derive_path: "m/84'/1'/0'/0/0".to_string(), sequence: 0, @@ -1432,7 +1452,7 @@ mod tests { .to_string(), vout: 1, amount: 70000, - address: Address::from_str("tb1q0qz6vdsay5edatqmvtyn9z92zkfs3hxqvk8k8k").unwrap(), + address: "tb1q0qz6vdsay5edatqmvtyn9z92zkfs3hxqvk8k8k".to_string(), script_pubkey: "00141a7a98a2b9fa09685d28edecb2741250e85882c3".to_string(), derive_path: "m/84'/1'/0'/1/53".to_string(), sequence: 0, @@ -1440,18 +1460,14 @@ mod tests { ]; let transaction = BtcTransaction { - to: Address::from_str("tb1qpma75pm648xmd9tfzah029edarqn4xtndqhp99").unwrap(), + to: "tb1qpma75pm648xmd9tfzah029edarqn4xtndqhp99".to_string(), amount: 310000, unspents: utxos, fee: 10000, + chain_type: "BITCOIN".to_string(), }; - let sign_result = transaction.sign_Transaction( - Network::Testnet, - "m/84'/1'/0'", - Some(53), - None, - "VERSION_0", - ); + let sign_result = + transaction.sign_transaction("TESTNET", "m/84'/1'/0'", Some(53), None, "VERSION_0"); assert_eq!( "02000000000104601e02999f6427896801174bf5a89a95ed4029f045757f8d03ad4dcd825eb0b7010000006a47304402205363ea34883d551c35c2338e1809566e424489167e69a404120c6684827443bf02200fbdcb4ff821c5aa28c1633e36406d3597f729b61dd631175d52964397138a7f0121033d710ab45bb54ac99618ad23b3c1da661631aa25f23bfe9d22b41876f1d46e4effffffffa011a8b84393834b2010a7287d1733b2379811258770b9e92a54728f4b1b67360000000017160014654fbb08267f3d50d715a8f1abb55979b160dd5bffffffff3a565653f6d376f0be94d9f09232d7dbf54ae2232f9f09c950c2e1ae5b9459640100000000ffffffffacff40c69ca5d9894882840a4e75980d9fb5085d0de289e49de93ff9168f1d6d0100000000ffffffff02f0ba0400000000001600140efbea077aa9cdb69569176ef5172de8c13a997350c30000000000001600147805a6361d2532deac1b62c93288aa159308dcc00002473044022073a93e5bc5f739d9f54198f2d4da1dfc8f79f23a62a8fada6f5edd54f6a1f358022028b3c86a2683cfed9128b2bea71de30e2e3e29e48996a383ec030403c1b716360121031aee5e20399d68cf0035d1a21564868f22bc448ab205292b4279136b15ecaebc02483045022100b4608108057f49a58ef4a9e49107232e140cb6729a69b0ac48c0bfeb237bf75e02206b6336c759ef83cb20b073545ca4227b280ebcb2aab932928a967e74bc6e4d42012102e24f625a31c9a8bae42239f2bf945a306c01a450a03fd123316db0e837a660c00247304402205155631ba66e009c677cc7e4f67183922eaff389719e604d1ff72fe7fbd1b27d0220523baa8575da69b150da6ecf56814bc34820ed1950ec59943b36c0d5451b3ffe01210383f26c44bf1607224237a93e8735ff69a23655878ddb22c46fcdd850417097a400000000", @@ -1471,24 +1487,25 @@ mod tests { fn test_sign_mixed_single_legacy_utxotransaction() { bind_test(); - let mut utxos = vec![Utxo { + let utxos = vec![Utxo { txhash: "1cd9bfa2cabf071aca138e38e7ba281fa0aa26dd554d3518a2f3f74d33e9d3f5".to_string(), vout: 0, amount: 100000, - address: Address::from_str("mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN").unwrap(), + address: "mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN".to_string(), script_pubkey: "76a914383fb81cb0a3fc724b5e08cf8bbd404336d711f688ac".to_string(), derive_path: "m/44'/1'/0'/0/0".to_string(), sequence: 0, }]; let transaction = BtcTransaction { - to: Address::from_str("2N5z4KZBCQNULTegkETDftMiNHWEFjrH3m2").unwrap(), + to: "2N5z4KZBCQNULTegkETDftMiNHWEFjrH3m2".to_string(), amount: 30000, unspents: utxos, fee: 10000, + chain_type: "BITCOIN".to_string(), }; let sign_result = - transaction.sign_Transaction(Network::Testnet, "m/49'/1'/0'", Some(53), None, "P2WPKH"); + transaction.sign_transaction("TESTNET", "m/49'/1'/0'", Some(53), None, "P2WPKH"); assert_eq!( "0100000001f5d3e9334df7f3a218354d55dd26aaa01f28bae7388e13ca1a07bfcaa2bfd91c000000006b48304502210091a1232f0c63dd72dcbf07092b92fe360ebb76425c57cb0281e12addfd92940d022055b500ccd12861bad5d48785a369157fca3a633df09e5cb800053e6e3b3d691c0121033d710ab45bb54ac99618ad23b3c1da661631aa25f23bfe9d22b41876f1d46e4effffffff02307500000000000017a9148bbb53570df9656926ea0ef029cd2ee84dbc7d0f8760ea00000000000017a914a906137d79fc84a9685de5e6185bf397c2249bcd8700000000", @@ -1514,7 +1531,7 @@ mod tests { .to_string(), vout: 0, amount: 200000000, - address: Address::from_str("mh7jj2ELSQUvRQELbn9qyA4q5nADhmJmUC").unwrap(), + address: "mh7jj2ELSQUvRQELbn9qyA4q5nADhmJmUC".to_string(), script_pubkey: "76a914118c3123196e030a8a607c22bafc1577af61497d88ac".to_string(), derive_path: "m/44'/1'/0'/0/22".to_string(), sequence: 0, @@ -1524,7 +1541,7 @@ mod tests { .to_string(), vout: 1, amount: 200000000, - address: Address::from_str("mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN").unwrap(), + address: "mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN".to_string(), script_pubkey: "76a914383fb81cb0a3fc724b5e08cf8bbd404336d711f688ac".to_string(), derive_path: "m/44'/1'/0'/0/0".to_string(), sequence: 0, @@ -1534,7 +1551,7 @@ mod tests { .to_string(), vout: 0, amount: 200000000, - address: Address::from_str("mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN").unwrap(), + address: "mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN".to_string(), script_pubkey: "76a914383fb81cb0a3fc724b5e08cf8bbd404336d711f688ac".to_string(), derive_path: "m/44'/1'/0'/0/0".to_string(), sequence: 0, @@ -1544,7 +1561,7 @@ mod tests { .to_string(), vout: 1, amount: 200000000, - address: Address::from_str("mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN").unwrap(), + address: "mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN".to_string(), script_pubkey: "76a914383fb81cb0a3fc724b5e08cf8bbd404336d711f688ac".to_string(), derive_path: "m/44'/1'/0'/0/0".to_string(), sequence: 0, @@ -1552,18 +1569,14 @@ mod tests { ]; let transaction = BtcTransaction { - to: Address::from_str("moLK3tBG86ifpDDTqAQzs4a9cUoNjVLRE3").unwrap(), + to: "moLK3tBG86ifpDDTqAQzs4a9cUoNjVLRE3".to_string(), amount: 799988000, unspents: utxos.clone(), fee: 12000, + chain_type: "BITCOIN".to_string(), }; - let sign_result = transaction.sign_Transaction( - Network::Testnet, - "m/44'/1'/0'/0/0", - Some(53), - None, - "NONE", - ); + let sign_result = + transaction.sign_transaction("TESTNET", "m/44'/1'/0'/0/0", Some(53), None, "NONE"); assert_eq!( "01000000047a222fb053b6e5339a9b6f9649f88a9481606cf3c64c4557802b3a819ddf3a98000000006b483045022100c610f77f71cc8afcfbd46df8e3d564fb8fb0f2c041bdf0869512c461901a8ad802206b92460cccbcb2a525877db1b4b7530d9b85e135ce88424d1f5f345dc65b881401210312a0cb31ff52c480c049da26d0aaa600f47e9deee53d02fc2b0e9acf3c20fbdfffffffff31b5a9794dcaf82af1738745afe1ecf402ea4a93e71ae75c7d3d8bf7c78aef45010000006b483045022100dce4a4c3d79bf9392832f68da3cd2daf85ac7fa851402ecc9aaac69b8761941d02201e1fd6601812ea9e39c6df0030cb754d4c578ff48bc9db6072ba5207a4ebc2b60121033d710ab45bb54ac99618ad23b3c1da661631aa25f23bfe9d22b41876f1d46e4effffffffa92c40dfd195a188d87110557fb7f46dbbfb68c4bb8718f33dc31d61927ec614000000006b483045022100e1802d80d72f5f3be624df3ab668692777188a9255c58067840e4b73a5a61a99022025b23942deb21f5d1959aae85421299ecc9efefb250dbacb46a4130abd538d730121033d710ab45bb54ac99618ad23b3c1da661631aa25f23bfe9d22b41876f1d46e4effffffffb99a3e8884b14f330d2a444a4bc2a03af16804fb99b5e37ee892ed5db8b67f11010000006a47304402207b82a62ed0d35c9878e6a7946d04679c8a17a8dd0a856b5cc14928fe1e9b554a0220411dd1a61f8ac2a8d7564de84e2c8a2c2583986bd71ac316ade480b8d0b4fffd0121033d710ab45bb54ac99618ad23b3c1da661631aa25f23bfe9d22b41876f1d46e4effffffff0120d9ae2f000000001976a91455bdc1b42e3bed851959846ddf600e96125423e088ac00000000", @@ -1581,7 +1594,7 @@ mod tests { .to_string(), vout: 0, amount: 50000, - address: Address::from_str("2MwN441dq8qudMvtM5eLVwC3u4zfKuGSQAB").unwrap(), + address: "2MwN441dq8qudMvtM5eLVwC3u4zfKuGSQAB".to_string(), script_pubkey: "a9142d2b1ef5ee4cf6c3ebc8cf66a602783798f7875987".to_string(), derive_path: "m/49'/1'/0'/0/0".to_string(), sequence: 0, @@ -1591,7 +1604,7 @@ mod tests { .to_string(), vout: 0, amount: 50000, - address: Address::from_str("2N54wJxopnWTvBfqgAPVWqXVEdaqoH7Suvf").unwrap(), + address: "2N54wJxopnWTvBfqgAPVWqXVEdaqoH7Suvf".to_string(), script_pubkey: "a91481af6d803fdc6dca1f3a1d03f5ffe8124cd1b44787".to_string(), derive_path: "m/49'/1'/0'/0/1".to_string(), sequence: 0, @@ -1599,18 +1612,14 @@ mod tests { ]; let transaction = BtcTransaction { - to: Address::from_str("2N9wBy6f1KTUF5h2UUeqRdKnBT6oSMh4Whp").unwrap(), + to: "2N9wBy6f1KTUF5h2UUeqRdKnBT6oSMh4Whp".to_string(), amount: 88000, unspents: utxos.clone(), fee: 12000, + chain_type: "BITCOIN".to_string(), }; - let sign_result = transaction.sign_Transaction( - Network::Testnet, - "m/49'/1'/0'/0/0", - Some(0), - None, - "P2WPKH", - ); + let sign_result = + transaction.sign_transaction("TESTNET", "m/49'/1'/0'/0/0", Some(0), None, "P2WPKH"); assert_eq!( "020000000001027f717276057e6012afa99385c18cc692397a666560520577679bf38c08b5cec20000000017160014654fbb08267f3d50d715a8f1abb55979b160dd5bffffffff74cdd54bc48333e1d2f108460284d137c39b6c417d9ff55a572a9550d428d69a00000000171600149d66aa6399de69d5c5ae19f9098047760251a854ffffffff01c05701000000000017a914b710f6e5049eaf0404c2f02f091dd5bb79fa135e870247304402205fd9dea5df0db5cc7b1d4b969f63b4526fb00fd5563ab91012cb511744a53d570220784abfe099a2b063b1cfc1f145fef2ffcb100b0891514fa164d357f0ef7ca6bb0121031aee5e20399d68cf0035d1a21564868f22bc448ab205292b4279136b15ecaebc02483045022100b0246c12428dbf863fcc9060ab6fc46dc2135adaa6cf8117de49f9acecaccf6c022059377d05c9cab24b7dec14242ea3206cc1f464d5ff9904dca515fc71766507cd012103a241c8d13dd5c92475652c43bf56580fbf9f1e8bc0aa0132ddc8443c03062bb900000000", @@ -1618,13 +1627,14 @@ mod tests { ); let transaction_req_data = BtcTransaction { - to: Address::from_str("2N9wBy6f1KTUF5h2UUeqRdKnBT6oSMh4Whp").unwrap(), + to: "2N9wBy6f1KTUF5h2UUeqRdKnBT6oSMh4Whp".to_string(), amount: 80000, unspents: utxos.clone(), fee: 10000, + chain_type: "BITCOIN".to_string(), }; - let sign_result = transaction_req_data.sign_Transaction( - Network::Testnet, + let sign_result = transaction_req_data.sign_transaction( + "TESTNET", "m/49'/1'/0'/0/0", Some(0), None, @@ -1647,7 +1657,7 @@ mod tests { .to_string(), vout: 0, amount: 50000, - address: Address::from_str("2MwN441dq8qudMvtM5eLVwC3u4zfKuGSQAB").unwrap(), + address: "2MwN441dq8qudMvtM5eLVwC3u4zfKuGSQAB".to_string(), script_pubkey: "a9142d2b1ef5ee4cf6c3ebc8cf66a602783798f7875987".to_string(), derive_path: "m/49'/1'/0'/0/0".to_string(), sequence: 0, @@ -1657,7 +1667,7 @@ mod tests { .to_string(), vout: 0, amount: 50000, - address: Address::from_str("2N54wJxopnWTvBfqgAPVWqXVEdaqoH7Suvf").unwrap(), + address: "2N54wJxopnWTvBfqgAPVWqXVEdaqoH7Suvf".to_string(), script_pubkey: "a91481af6d803fdc6dca1f3a1d03f5ffe8124cd1b44787".to_string(), derive_path: "m/49'/1'/0'/0/1".to_string(), sequence: 0, @@ -1665,13 +1675,14 @@ mod tests { ]; let transaction = BtcTransaction { - to: Address::from_str("2N9wBy6f1KTUF5h2UUeqRdKnBT6oSMh4Whp").unwrap(), + to: "2N9wBy6f1KTUF5h2UUeqRdKnBT6oSMh4Whp".to_string(), amount: 88000, unspents: utxos.clone(), fee: 12000, + chain_type: "BITCOIN".to_string(), }; - let sign_result = transaction.sign_Transaction( - Network::Testnet, + let sign_result = transaction.sign_transaction( + "TESTNET", "m/49'/1'/0'/0/0", Some(0), Some("1234"), @@ -1692,10 +1703,7 @@ mod tests { txhash: "2bdcfa88d5f48954e98018da33aaf11a4951b4167ba8121bc787880890dee5f0".to_string(), vout: 1, amount: 523000, - address: Address::from_str( - "tb1pjvp6z9shfhfpafrnwen9j452cf8tdwpgc0hfnzvz62aqwr4qv92sg7qj9r", - ) - .unwrap(), + address: "tb1pjvp6z9shfhfpafrnwen9j452cf8tdwpgc0hfnzvz62aqwr4qv92sg7qj9r".to_string(), script_pubkey: "51208f4ca6a7384f50a1fe00cba593d5a834b480c65692a76ae6202e1ce46cb1c233" .to_string(), derive_path: "m/86'/1'/0'/1/53".to_string(), @@ -1703,19 +1711,14 @@ mod tests { }]; let transaction = BtcTransaction { - to: Address::from_str("tb1p3ax2dfecfag2rlsqewje84dgxj6gp3jkj2nk4e3q9cwwgm93cgesa0zwj4") - .unwrap(), + to: "tb1p3ax2dfecfag2rlsqewje84dgxj6gp3jkj2nk4e3q9cwwgm93cgesa0zwj4".to_string(), amount: 40000, unspents: utxos.clone(), fee: 1000, + chain_type: "BITCOIN".to_string(), }; - let sign_result = transaction.sign_Transaction( - Network::Testnet, - "m/86'/1'/0'/0/0", - Some(53), - None, - "VERSION_1", - ); + let sign_result = + transaction.sign_transaction("TESTNET", "m/86'/1'/0'/0/0", Some(53), None, "VERSION_1"); assert_eq!( "0fb223cd2cd90830827ab235b752de841153d69a75649d8f92ffa2198d645852", sign_result.as_ref().unwrap().tx_hash @@ -1730,26 +1733,21 @@ mod tests { txhash: "cebc5c2b4f5533428ad0cca94e9bfefa6410a270ed1d7116e2ee8592494c66bd".to_string(), vout: 1, amount: 100000, - address: Address::from_str("tb1qrfaf3g4elgykshfgahktyaqj2r593qkrae5v95").unwrap(), + address: "tb1qrfaf3g4elgykshfgahktyaqj2r593qkrae5v95".to_string(), script_pubkey: "00141a7a98a2b9fa09685d28edecb2741250e85882c3".to_string(), derive_path: "m/84'/1'/0'/0/0".to_string(), sequence: 0, }]; let transaction = BtcTransaction { - to: Address::from_str("tb1p3ax2dfecfag2rlsqewje84dgxj6gp3jkj2nk4e3q9cwwgm93cgesa0zwj4") - .unwrap(), + to: "tb1p3ax2dfecfag2rlsqewje84dgxj6gp3jkj2nk4e3q9cwwgm93cgesa0zwj4".to_string(), amount: 50000, unspents: utxos, fee: 20000, + chain_type: "BITCOIN".to_string(), }; - let sign_result = transaction.sign_Transaction( - Network::Testnet, - "m/86'/1'/0'/0/0", - Some(53), - None, - "VERSION_1", - ); + let sign_result = + transaction.sign_transaction("TESTNET", "m/86'/1'/0'/0/0", Some(53), None, "VERSION_1"); assert_eq!( "02000000000101bd664c499285eee216711ded70a21064fafe9b4ea9ccd08a4233554f2b5cbcce0100000000ffffffff0250c30000000000002251208f4ca6a7384f50a1fe00cba593d5a834b480c65692a76ae6202e1ce46cb1c23330750000000000002251209303a116174dd21ea473766659568ac24eb6b828c3ee998982d2ba070ea0615502483045022100bed2bc8b4bf2beb4dacda077b47f96b4070af659ca241c343eccfe3ebc4a6f600220379c51f6456adff08a7605496a88653689af9e44f5d324e2ad2e1eae330b434f012102e24f625a31c9a8bae42239f2bf945a306c01a450a03fd123316db0e837a660c000000000", sign_result.as_ref().unwrap().signature @@ -1770,10 +1768,8 @@ mod tests { .to_string(), vout: 0, amount: 20000, - address: Address::from_str( - "tb1p3ax2dfecfag2rlsqewje84dgxj6gp3jkj2nk4e3q9cwwgm93cgesa0zwj4", - ) - .unwrap(), + address: "tb1p3ax2dfecfag2rlsqewje84dgxj6gp3jkj2nk4e3q9cwwgm93cgesa0zwj4" + .to_string(), script_pubkey: "51208f4ca6a7384f50a1fe00cba593d5a834b480c65692a76ae6202e1ce46cb1c233" .to_string(), @@ -1785,10 +1781,8 @@ mod tests { .to_string(), vout: 1, amount: 283000, - address: Address::from_str( - "tb1pjvp6z9shfhfpafrnwen9j452cf8tdwpgc0hfnzvz62aqwr4qv92sg7qj9r", - ) - .unwrap(), + address: "tb1pjvp6z9shfhfpafrnwen9j452cf8tdwpgc0hfnzvz62aqwr4qv92sg7qj9r" + .to_string(), script_pubkey: "51209303a116174dd21ea473766659568ac24eb6b828c3ee998982d2ba070ea06155" .to_string(), @@ -1800,7 +1794,7 @@ mod tests { .to_string(), vout: 0, amount: 100000, - address: Address::from_str("tb1qrfaf3g4elgykshfgahktyaqj2r593qkrae5v95").unwrap(), + address: "tb1qrfaf3g4elgykshfgahktyaqj2r593qkrae5v95".to_string(), script_pubkey: "00141a7a98a2b9fa09685d28edecb2741250e85882c3".to_string(), derive_path: "m/84'/1'/0'/0/0".to_string(), sequence: 0, @@ -1810,7 +1804,7 @@ mod tests { .to_string(), vout: 1, amount: 100000, - address: Address::from_str("2MwN441dq8qudMvtM5eLVwC3u4zfKuGSQAB").unwrap(), + address: "2MwN441dq8qudMvtM5eLVwC3u4zfKuGSQAB".to_string(), script_pubkey: "a9142d2b1ef5ee4cf6c3ebc8cf66a602783798f7875987".to_string(), derive_path: "m/49'/1'/0'/0/0".to_string(), sequence: 0, @@ -1820,7 +1814,7 @@ mod tests { .to_string(), vout: 1, amount: 100000, - address: Address::from_str("mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN").unwrap(), + address: "mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN".to_string(), script_pubkey: "76a914383fb81cb0a3fc724b5e08cf8bbd404336d711f688ac".to_string(), derive_path: "m/44'/1'/0'/0/0".to_string(), sequence: 0, @@ -1828,19 +1822,14 @@ mod tests { ]; let transaction = BtcTransaction { - to: Address::from_str("tb1pxcrec4q8tzj34phw470pwe5dfkz58k93kljklck6pxpv8yx9v40q66tmr7") - .unwrap(), + to: "tb1pxcrec4q8tzj34phw470pwe5dfkz58k93kljklck6pxpv8yx9v40q66tmr7".to_string(), amount: 40000, unspents: utxos, fee: 40000, + chain_type: "BITCOIN".to_string(), }; - let sign_result = transaction.sign_Transaction( - Network::Testnet, - "m/86'/1'/0'/0/0", - Some(53), - None, - "VERSION_1", - ); + let sign_result = + transaction.sign_transaction("TESTNET", "m/86'/1'/0'/0/0", Some(53), None, "VERSION_1"); let tx_sign_result = &sign_result.unwrap(); let tx = Transaction::deserialize(&hex_to_bytes(&tx_sign_result.signature).unwrap()).unwrap(); @@ -1904,7 +1893,7 @@ mod tests { .to_string(), vout: 0, amount: 200000000, - address: Address::from_str("mh7jj2ELSQUvRQELbn9qyA4q5nADhmJmUC").unwrap(), + address: "mh7jj2ELSQUvRQELbn9qyA4q5nADhmJmUC".to_string(), script_pubkey: "76a914118c3123196e030a8a607c22bafc1577af61497d88ac".to_string(), derive_path: "m/44'/1'/0'/0/22".to_string(), sequence: 0, @@ -1914,7 +1903,7 @@ mod tests { .to_string(), vout: 1, amount: 200000000, - address: Address::from_str("mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN").unwrap(), + address: "mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN".to_string(), script_pubkey: "76a914383fb81cb0a3fc724b5e08cf8bbd404336d711f688ac".to_string(), derive_path: "m/44'/1'/0'/0/0".to_string(), sequence: 0, @@ -1924,7 +1913,7 @@ mod tests { .to_string(), vout: 0, amount: 200000000, - address: Address::from_str("mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN").unwrap(), + address: "mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN".to_string(), script_pubkey: "76a914383fb81cb0a3fc724b5e08cf8bbd404336d711f688ac".to_string(), derive_path: "m/44'/1'/0'/0/0".to_string(), sequence: 0, @@ -1934,7 +1923,7 @@ mod tests { .to_string(), vout: 1, amount: 200000000, - address: Address::from_str("mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN").unwrap(), + address: "mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN".to_string(), script_pubkey: "76a914383fb81cb0a3fc724b5e08cf8bbd404336d711f688ac".to_string(), derive_path: "m/44'/1'/0'/0/0".to_string(), sequence: 0, @@ -1942,25 +1931,201 @@ mod tests { ]; let transaction = BtcTransaction { - to: Address::from_str("moLK3tBG86ifpDDTqAQzs4a9cUoNjVLRE3").unwrap(), + to: "moLK3tBG86ifpDDTqAQzs4a9cUoNjVLRE3".to_string(), amount: 750000000, unspents: utxos, fee: 502130, + chain_type: "BITCOIN".to_string(), + }; + let sign_result = + transaction.sign_transaction("TESTNET", "m/44'/1'/0'/0/0", Some(53), None, "NONE"); + assert_eq!( + "01000000047a222fb053b6e5339a9b6f9649f88a9481606cf3c64c4557802b3a819ddf3a98000000006b483045022100c4f39ce7f2448ab8e7154a7b7ce82edd034e3f33e1f917ca43e4aff822ba804c02206dd146d1772a45bb5e51abb081d066114e78bcb504671f61c5a301a647a494ac01210312a0cb31ff52c480c049da26d0aaa600f47e9deee53d02fc2b0e9acf3c20fbdfffffffff31b5a9794dcaf82af1738745afe1ecf402ea4a93e71ae75c7d3d8bf7c78aef45010000006b483045022100d235afda9a56aaa4cbe05df712202e6b1a45aab7a0c83540d3053133f15acc5602201b0e144bec3a02a5c556596040b0be81b0202c19b163bb537b8d965afd61403a0121033d710ab45bb54ac99618ad23b3c1da661631aa25f23bfe9d22b41876f1d46e4effffffffa92c40dfd195a188d87110557fb7f46dbbfb68c4bb8718f33dc31d61927ec614000000006b483045022100dd8f1e20116f96a3400f55e0c637a0ad21ae47ff92d83ffb0c3d324c684a54be0220064b0a6d316154ef07a69bd82de3a052e43c3c6bb0e55e4de4de939b093e1a3a0121033d710ab45bb54ac99618ad23b3c1da661631aa25f23bfe9d22b41876f1d46e4effffffffb99a3e8884b14f330d2a444a4bc2a03af16804fb99b5e37ee892ed5db8b67f11010000006a473044022048d8cb0f1480174b3b9186cc6fe410db765f1f9d3ce036b0d4dee0eb19aa3641022073de4bb2b00a0533e9c8f3e074c655e0695c8b223233ddecf3c99a84351d50a60121033d710ab45bb54ac99618ad23b3c1da661631aa25f23bfe9d22b41876f1d46e4effffffff028017b42c000000001976a91455bdc1b42e3bed851959846ddf600e96125423e088ac0e47f302000000001976a91412967cdd9ceb72bbdbb7e5db85e2dbc6d6c3ab1a88ac00000000", + sign_result.as_ref().unwrap().signature + ); + assert_eq!( + "3aa6ed94e29c01b96fe3a20c30825d161f421d5e2358eb1ceade43de533e1977", + sign_result.as_ref().unwrap().tx_hash + ); + } + + #[test] + fn test_sign_dogecoin_p2pkh_testnet() { + bind_test(); + + let utxos = vec![ + Utxo { + txhash: "983adf9d813a2b8057454cc6f36c6081948af849966f9b9a33e5b653b02f227a" + .to_string(), + vout: 0, + amount: 200000000, + address: "nVnwhEpurmQg4GWWecpUwQcQLng798GRai".to_string(), + script_pubkey: "76a914118c3123196e030a8a607c22bafc1577af61497d88ac".to_string(), + derive_path: "m/44'/1'/0'/0/22".to_string(), + sequence: 4294967295, + }, + Utxo { + txhash: "45ef8ac7f78b3d7d5ce71ae7934aea02f4ece1af458773f12af8ca4d79a9b531" + .to_string(), + vout: 1, + amount: 200000000, + address: "nZKaSJP5DAv4MSSNG4zyB833s92rHdzyqW".to_string(), + script_pubkey: "76a914383fb81cb0a3fc724b5e08cf8bbd404336d711f688ac".to_string(), + derive_path: "m/44'/1'/0'/0/0".to_string(), + sequence: 4294967295, + }, + Utxo { + txhash: "14c67e92611dc33df31887bbc468fbbb6df4b77f551071d888a195d1df402ca9" + .to_string(), + vout: 0, + amount: 200000000, + address: "nZKaSJP5DAv4MSSNG4zyB833s92rHdzyqW".to_string(), + script_pubkey: "76a914383fb81cb0a3fc724b5e08cf8bbd404336d711f688ac".to_string(), + derive_path: "m/44'/1'/0'/0/0".to_string(), + sequence: 4294967295, + }, + Utxo { + txhash: "117fb6b85ded92e87ee3b599fb0468f13aa0c24b4a442a0d334fb184883e9ab9" + .to_string(), + vout: 1, + amount: 200000000, + address: "nZKaSJP5DAv4MSSNG4zyB833s92rHdzyqW".to_string(), + script_pubkey: "76a914383fb81cb0a3fc724b5e08cf8bbd404336d711f688ac".to_string(), + derive_path: "m/44'/1'/0'/0/0".to_string(), + sequence: 4294967295, + }, + ]; + + let transaction = BtcTransaction { + to: "nZKaSJP5DAv4MSSNG4zyB833s92rHdzyqW".to_string(), + amount: 799988000, + unspents: utxos, + fee: 10000, + chain_type: "DOGECOIN".to_string(), }; - let sign_result = transaction.sign_Transaction( - Network::Testnet, - "m/44'/1'/0'/0/0", + let sign_result = transaction.sign_transaction( + "TESTNET", + &"m/44'/1'/0'".to_string(), Some(53), None, "NONE", ); + assert_eq!( - "01000000047a222fb053b6e5339a9b6f9649f88a9481606cf3c64c4557802b3a819ddf3a98000000006b483045022100c4f39ce7f2448ab8e7154a7b7ce82edd034e3f33e1f917ca43e4aff822ba804c02206dd146d1772a45bb5e51abb081d066114e78bcb504671f61c5a301a647a494ac01210312a0cb31ff52c480c049da26d0aaa600f47e9deee53d02fc2b0e9acf3c20fbdfffffffff31b5a9794dcaf82af1738745afe1ecf402ea4a93e71ae75c7d3d8bf7c78aef45010000006b483045022100d235afda9a56aaa4cbe05df712202e6b1a45aab7a0c83540d3053133f15acc5602201b0e144bec3a02a5c556596040b0be81b0202c19b163bb537b8d965afd61403a0121033d710ab45bb54ac99618ad23b3c1da661631aa25f23bfe9d22b41876f1d46e4effffffffa92c40dfd195a188d87110557fb7f46dbbfb68c4bb8718f33dc31d61927ec614000000006b483045022100dd8f1e20116f96a3400f55e0c637a0ad21ae47ff92d83ffb0c3d324c684a54be0220064b0a6d316154ef07a69bd82de3a052e43c3c6bb0e55e4de4de939b093e1a3a0121033d710ab45bb54ac99618ad23b3c1da661631aa25f23bfe9d22b41876f1d46e4effffffffb99a3e8884b14f330d2a444a4bc2a03af16804fb99b5e37ee892ed5db8b67f11010000006a473044022048d8cb0f1480174b3b9186cc6fe410db765f1f9d3ce036b0d4dee0eb19aa3641022073de4bb2b00a0533e9c8f3e074c655e0695c8b223233ddecf3c99a84351d50a60121033d710ab45bb54ac99618ad23b3c1da661631aa25f23bfe9d22b41876f1d46e4effffffff028017b42c000000001976a91455bdc1b42e3bed851959846ddf600e96125423e088ac0e47f302000000001976a91412967cdd9ceb72bbdbb7e5db85e2dbc6d6c3ab1a88ac00000000", + "01000000047a222fb053b6e5339a9b6f9649f88a9481606cf3c64c4557802b3a819ddf3a98000000006a47304402200eb094ab218f492e15bcae19c61c980111fdca403108ed8502a6c4ada5ffe8b802204afb0b58b770a4523148895ffdb1a154c0642584223306c267ee67e6d2f35fba01210312a0cb31ff52c480c049da26d0aaa600f47e9deee53d02fc2b0e9acf3c20fbdfffffffff31b5a9794dcaf82af1738745afe1ecf402ea4a93e71ae75c7d3d8bf7c78aef45010000006a47304402203b7317f8443f49a8c2930b679181feb0640e88447bca3f2a94600cc0078e4ab90220624ec6e2ba25f3bde477df048d84a9aad736087332ffced65377a66b1ea4d3c10121033d710ab45bb54ac99618ad23b3c1da661631aa25f23bfe9d22b41876f1d46e4effffffffa92c40dfd195a188d87110557fb7f46dbbfb68c4bb8718f33dc31d61927ec614000000006b4830450221008541d1e27f76450b4b78a89af8e6707a042a30151d1e59d271f335720a3a4a590220152b688827df0fff697cb8ba47a6293a4785d651e5da426854bebce7ea4d34010121033d710ab45bb54ac99618ad23b3c1da661631aa25f23bfe9d22b41876f1d46e4effffffffb99a3e8884b14f330d2a444a4bc2a03af16804fb99b5e37ee892ed5db8b67f11010000006a473044022042b27e6639c575f0acf4ebb4b43ee52ff9fef2888caf5d88d38a6c6b062cbbae022048446492f4c315cc5cd37d6dac041d67ffbf1bc199311bfc5f29fbdb102279a60121033d710ab45bb54ac99618ad23b3c1da661631aa25f23bfe9d22b41876f1d46e4effffffff0220d9ae2f000000001976a914383fb81cb0a3fc724b5e08cf8bbd404336d711f688acd0070000000000001976a91412967cdd9ceb72bbdbb7e5db85e2dbc6d6c3ab1a88ac00000000", sign_result.as_ref().unwrap().signature ); assert_eq!( - "3aa6ed94e29c01b96fe3a20c30825d161f421d5e2358eb1ceade43de533e1977", + "365d003e2bb2213e6754e787a576007cab51a46a146bad1c722a59622ff26535", sign_result.as_ref().unwrap().tx_hash ); + assert_eq!( + "365d003e2bb2213e6754e787a576007cab51a46a146bad1c722a59622ff26535", + sign_result.as_ref().unwrap().wtx_id + ); + } + + #[test] + fn test_sign_dogecoin_p2wpkh_testnet() { + bind_test(); + + let utxos = vec![ + Utxo { + txhash: "c2ceb5088cf39b677705526065667a3992c68cc18593a9af12607e057672717f" + .to_string(), + vout: 0, + amount: 50000, + address: "2MwN441dq8qudMvtM5eLVwC3u4zfKuGSQAB".to_string(), + script_pubkey: "a9142d2b1ef5ee4cf6c3ebc8cf66a602783798f7875987".to_string(), + derive_path: "m/49'/1'/0'/0/0".to_string(), + sequence: 0, + }, + Utxo { + txhash: "9ad628d450952a575af59f7d416c9bc337d184024608f1d2e13383c44bd5cd74" + .to_string(), + vout: 0, + amount: 50000, + address: "2N54wJxopnWTvBfqgAPVWqXVEdaqoH7Suvf".to_string(), + script_pubkey: "a91481af6d803fdc6dca1f3a1d03f5ffe8124cd1b44787".to_string(), + derive_path: "m/49'/1'/0'/0/1".to_string(), + sequence: 0, + }, + ]; + + let transaction = BtcTransaction { + to: "2N9wBy6f1KTUF5h2UUeqRdKnBT6oSMh4Whp".to_string(), + amount: 88000, + unspents: utxos, + fee: 10000, + chain_type: "DOGECOIN".to_string(), + }; + let sign_result = transaction.sign_transaction( + "TESTNET", + &"m/49'/1'/0'".to_string(), + Some(0), + Some("1234"), + "P2WPKH", + ); + assert_eq!( + "dc021850ca46b2fdc3f278020ac4e27ee18d9753dd07cbd97b84a2a0a2af3940", + sign_result.as_ref().unwrap().tx_hash + ); + assert_eq!( + "4eede542b9da11500d12f38b81c3728ae6cd094b866bc9629cbb2c6ab0810914", + sign_result.as_ref().unwrap().wtx_id + ); + assert_eq!(sign_result.as_ref().unwrap().signature, "020000000001027f717276057e6012afa99385c18cc692397a666560520577679bf38c08b5cec20000000017160014654fbb08267f3d50d715a8f1abb55979b160dd5bffffffff74cdd54bc48333e1d2f108460284d137c39b6c417d9ff55a572a9550d428d69a00000000171600149d66aa6399de69d5c5ae19f9098047760251a854ffffffff03c05701000000000017a914b710f6e5049eaf0404c2f02f091dd5bb79fa135e87d00700000000000017a914755fba51b5c443b9f16b1f86665dec10dd7a25c5870000000000000000046a02123402483045022100c5c33638f7a93094f4c5f30e384ed619f1818ee5095f6c892909b1fde0ec3d45022078d4c458e05d7ffee8dc7807d4b1b576c2ba1311b05d1e6f4c41775da77deb4d0121031aee5e20399d68cf0035d1a21564868f22bc448ab205292b4279136b15ecaebc0247304402201d0b9fd415cbe3af809709fea17dfab49291d5f9e42c2ec916dc547b8819df8d02203281c5a742093d46d6b681afc837022ae33c6ff3839ac502bb6bf443782f8010012103a241c8d13dd5c92475652c43bf56580fbf9f1e8bc0aa0132ddc8443c03062bb900000000"); + } + + #[test] + fn test_sign_dogecoin_p2wpkh_mainnet() { + bind_test(); + + let utxos = vec![ + Utxo { + txhash: "c2ceb5088cf39b677705526065667a3992c68cc18593a9af12607e057672717f" + .to_string(), + vout: 0, + amount: 50000, + address: "9vZ6j7mhbTHB4WdGpeP3ZNh1ZDqCBLfmjQ".to_string(), + script_pubkey: "a9142d2b1ef5ee4cf6c3ebc8cf66a602783798f7875987".to_string(), + derive_path: "m/49'/1'/0'/0/0".to_string(), + sequence: 0, + }, + Utxo { + txhash: "9ad628d450952a575af59f7d416c9bc337d184024608f1d2e13383c44bd5cd74" + .to_string(), + vout: 0, + amount: 50000, + address: "A4Fyz4whF7qTtFabuPY4Ti8M7p1faxWKsQ".to_string(), + script_pubkey: "a91481af6d803fdc6dca1f3a1d03f5ffe8124cd1b44787".to_string(), + derive_path: "m/49'/1'/0'/0/1".to_string(), + sequence: 0, + }, + ]; + + let transaction = BtcTransaction { + to: "A4Fyz4whF7qTtFabuPY4Ti8M7p1faxWKsQ".to_string(), + amount: 88000, + unspents: utxos, + fee: 10000, + chain_type: "DOGECOIN".to_string(), + }; + let sign_result = transaction.sign_transaction( + "MAINNET", + &"m/49'/1'/0'".to_string(), + Some(0), + None, + "P2WPKH", + ); + assert_eq!( + "330df579f9432661cd295cd6317c9f6f0af4356e7e78c258dfd3e40fd4e8ca47", + sign_result.as_ref().unwrap().tx_hash + ); + assert_eq!( + "6e6fe4bf6396e5255f28fb956edc0d6b1968898f7a62b92cf87c71a851899a3f", + sign_result.as_ref().unwrap().wtx_id + ); + assert_eq!(sign_result.as_ref().unwrap().signature, "020000000001027f717276057e6012afa99385c18cc692397a666560520577679bf38c08b5cec20000000017160014654fbb08267f3d50d715a8f1abb55979b160dd5bffffffff74cdd54bc48333e1d2f108460284d137c39b6c417d9ff55a572a9550d428d69a00000000171600149d66aa6399de69d5c5ae19f9098047760251a854ffffffff02c05701000000000017a91481af6d803fdc6dca1f3a1d03f5ffe8124cd1b44787d00700000000000017a914755fba51b5c443b9f16b1f86665dec10dd7a25c58702483045022100aa512e5b38e828bc2219ea58c8f8c432f5f1ad5e13a7972e01d1b43740e08cf302201ae50dfd52cd1d37d285a085064afe18ec822d7f05187441614cb0865ce8db530121031aee5e20399d68cf0035d1a21564868f22bc448ab205292b4279136b15ecaebc0247304402202da72ad7f19306c6aabcf7ee2ce48bcf4c3b508027ce1c572f6cd0ea95bf0a5202204331ddced75e1eb67e2ead1ee328942e4f3b7c6eb2b785a1caa6b7fe2abf7542012103a241c8d13dd5c92475652c43bf56580fbf9f1e8bc0aa0132ddc8443c03062bb900000000"); } } diff --git a/imkey-core/ikc-wallet/coin-bitcoin/src/usdt_transaction.rs b/imkey-core/ikc-wallet/coin-bitcoin/src/usdt_transaction.rs index f7cf0573..ffe9d331 100644 --- a/imkey-core/ikc-wallet/coin-bitcoin/src/usdt_transaction.rs +++ b/imkey-core/ikc-wallet/coin-bitcoin/src/usdt_transaction.rs @@ -4,6 +4,7 @@ use crate::Result; use bitcoin::blockdata::{opcodes, script::Builder}; use bitcoin::consensus::{serialize, Encodable}; use bitcoin::hashes::hex::FromHex; +use bitcoin::Address; use bitcoin::{ EcdsaSighashType, Network, OutPoint, PackedLockTime, Script, Sequence, Transaction, TxIn, TxOut, Witness, @@ -19,6 +20,7 @@ use ikc_common::utility::{bigint_to_byte_vec, hex_to_bytes, secp256k1_sign}; use ikc_device::device_binding::KEY_MANAGER; use ikc_transport::message::{send_apdu, send_apdu_timeout}; use secp256k1::ecdsa::Signature; +use std::str::FromStr; impl BtcTransaction { pub fn sign_omni_transaction( @@ -29,10 +31,7 @@ impl BtcTransaction { ) -> Result { //path check check_path_validity(path)?; - let mut path_str = path.to_string(); - if !path.ends_with("/") { - path_str = format!("{}{}", path_str, "/"); - } + //check uxto number if &self.unspents.len() > &MAX_UTXO_NUMBER { return Err(CoinError::ImkeyExceededMaxUtxoNumber.into()); @@ -49,17 +48,17 @@ impl BtcTransaction { //add change output let mut txouts: Vec = Vec::new(); let change_amount = self.get_total_amount() - MIN_NONDUST_OUTPUT - self.fee; - let receiver_address = &self.unspents.get(0).unwrap().address; let txout_send_output = TxOut { value: change_amount as u64, - script_pubkey: receiver_address.script_pubkey(), + script_pubkey: Address::from_str(&self.unspents.get(0).unwrap().address)? + .script_pubkey(), }; txouts.push(txout_send_output); //add send to output let txout_change_output = TxOut { value: MIN_NONDUST_OUTPUT as u64, - script_pubkey: self.to.script_pubkey(), + script_pubkey: Address::from_str(&self.to)?.script_pubkey(), }; txouts.push(txout_change_output); @@ -190,10 +189,7 @@ impl BtcTransaction { ) -> Result { //path check check_path_validity(path)?; - let mut path_str = path.to_string(); - if !path.ends_with("/") { - path_str = format!("{}{}", path_str, "/"); - } + //check uxto number if &self.unspents.len() > &MAX_UTXO_NUMBER { return Err(CoinError::ImkeyExceededMaxUtxoNumber.into()); @@ -210,16 +206,17 @@ impl BtcTransaction { //5.add change output let mut txouts: Vec = vec![]; let receiver_address = &self.unspents.get(0).unwrap().address; + let script_pubkey = Address::from_str(receiver_address)?.script_pubkey(); let txout_send_output = TxOut { value: change_amount as u64, - script_pubkey: receiver_address.script_pubkey(), + script_pubkey, }; txouts.push(txout_send_output); //6.add send to output let txout_change_output = TxOut { value: MIN_NONDUST_OUTPUT as u64, - script_pubkey: self.to.script_pubkey(), + script_pubkey: Address::from_str(&self.to)?.script_pubkey(), }; txouts.push(txout_change_output); @@ -432,7 +429,7 @@ mod tests { txhash: "0dd195c815c5086c5995f43a0c67d28344ae5fa130739a5e03ef40fea54f2031".to_string(), vout: 0, amount: 14824854, - address: Address::from_str("mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN").unwrap(), + address: "mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN".to_string(), script_pubkey: "76a914383fb81cb0a3fc724b5e08cf8bbd404336d711f688ac".to_string(), derive_path: "m/44'/1'/0'/0/0".to_string(), sequence: 4294967295, @@ -441,10 +438,11 @@ mod tests { utxos.push(utxo); let transaction_req_data = BtcTransaction { - to: Address::from_str("moLK3tBG86ifpDDTqAQzs4a9cUoNjVLRE3").unwrap(), + to: "moLK3tBG86ifpDDTqAQzs4a9cUoNjVLRE3".to_string(), amount: 10050000000, unspents: utxos, fee: 4000, + chain_type: "".to_string(), }; let sign_result = transaction_req_data.sign_omni_transaction( Network::Testnet, @@ -470,7 +468,7 @@ mod tests { txhash: "9baf6fd0e560f9f199f4879c23cb73b9c4affb54a1cfdbacb85687efa89f4c78".to_string(), vout: 1, amount: 21863396, - address: Address::from_str("2MwN441dq8qudMvtM5eLVwC3u4zfKuGSQAB").unwrap(), + address: "2MwN441dq8qudMvtM5eLVwC3u4zfKuGSQAB".to_string(), script_pubkey: "a9142d2b1ef5ee4cf6c3ebc8cf66a602783798f7875987".to_string(), derive_path: "m/49'/1'/0'/0/0".to_string(), sequence: 0, @@ -479,10 +477,11 @@ mod tests { let mut utxos = Vec::new(); utxos.push(utxo); let transaction_req_data = BtcTransaction { - to: Address::from_str("moLK3tBG86ifpDDTqAQzs4a9cUoNjVLRE3").unwrap(), + to: "moLK3tBG86ifpDDTqAQzs4a9cUoNjVLRE3".to_string(), amount: 10000000000, unspents: utxos, fee: 4000, + chain_type: "".to_string(), }; let sign_result = transaction_req_data.sign_omni_segwit_transaction( Network::Testnet, @@ -509,7 +508,7 @@ mod tests { txhash: "983adf9d813a2b8057454cc6f36c6081948af849966f9b9a33e5b653b02f227a".to_string(), vout: 0, amount: 200000000, - address: Address::from_str("37E2J9ViM4QFiewo7aw5L3drF2QKB99F9e").unwrap(), + address: "37E2J9ViM4QFiewo7aw5L3drF2QKB99F9e".to_string(), script_pubkey: "a9142d2b1ef5ee4cf6c3ebc8cf66a602783798f7875987".to_string(), derive_path: "m/49'/0'/0'/0/22".to_string(), sequence: 0, @@ -518,7 +517,7 @@ mod tests { txhash: "45ef8ac7f78b3d7d5ce71ae7934aea02f4ece1af458773f12af8ca4d79a9b531".to_string(), vout: 1, amount: 200000000, - address: Address::from_str("3JmreiUEKn8P3SyLYmZ7C1YCd4r2nFy3Dp").unwrap(), + address: "3JmreiUEKn8P3SyLYmZ7C1YCd4r2nFy3Dp".to_string(), script_pubkey: "a9142d2b1ef5ee4cf6c3ebc8cf66a602783798f7875987".to_string(), derive_path: "m/49'/0'/0'/0/0".to_string(), sequence: 0, @@ -527,7 +526,7 @@ mod tests { txhash: "14c67e92611dc33df31887bbc468fbbb6df4b77f551071d888a195d1df402ca9".to_string(), vout: 0, amount: 200000000, - address: Address::from_str("3JmreiUEKn8P3SyLYmZ7C1YCd4r2nFy3Dp").unwrap(), + address: "3JmreiUEKn8P3SyLYmZ7C1YCd4r2nFy3Dp".to_string(), script_pubkey: "a9142d2b1ef5ee4cf6c3ebc8cf66a602783798f7875987".to_string(), derive_path: "m/49'/0'/0'/0/0".to_string(), sequence: 0, @@ -536,7 +535,7 @@ mod tests { txhash: "117fb6b85ded92e87ee3b599fb0468f13aa0c24b4a442a0d334fb184883e9ab9".to_string(), vout: 1, amount: 200000000, - address: Address::from_str("3JmreiUEKn8P3SyLYmZ7C1YCd4r2nFy3Dp").unwrap(), + address: "3JmreiUEKn8P3SyLYmZ7C1YCd4r2nFy3Dp".to_string(), script_pubkey: "a9142d2b1ef5ee4cf6c3ebc8cf66a602783798f7875987".to_string(), derive_path: "m/49'/0'/0'/0/0".to_string(), sequence: 0, @@ -545,7 +544,7 @@ mod tests { txhash: "013adf9d813a2b8057454cc6f36c6081948af849966f9b9a33e5b653b02f227a".to_string(), vout: 0, amount: 200000000, - address: Address::from_str("37E2J9ViM4QFiewo7aw5L3drF2QKB99F9e").unwrap(), + address: "37E2J9ViM4QFiewo7aw5L3drF2QKB99F9e".to_string(), script_pubkey: "a9142d2b1ef5ee4cf6c3ebc8cf66a602783798f7875987".to_string(), derive_path: "m/49'/0'/0'/0/22".to_string(), sequence: 0, @@ -554,7 +553,7 @@ mod tests { txhash: "023adf9d813a2b8057454cc6f36c6081948af849966f9b9a33e5b653b02f227a".to_string(), vout: 0, amount: 200000000, - address: Address::from_str("37E2J9ViM4QFiewo7aw5L3drF2QKB99F9e").unwrap(), + address: "37E2J9ViM4QFiewo7aw5L3drF2QKB99F9e".to_string(), script_pubkey: "a9142d2b1ef5ee4cf6c3ebc8cf66a602783798f7875987".to_string(), derive_path: "m/49'/0'/0'/0/22".to_string(), sequence: 0, @@ -563,7 +562,7 @@ mod tests { txhash: "033adf9d813a2b8057454cc6f36c6081948af849966f9b9a33e5b653b02f227a".to_string(), vout: 0, amount: 200000000, - address: Address::from_str("37E2J9ViM4QFiewo7aw5L3drF2QKB99F9e").unwrap(), + address: "37E2J9ViM4QFiewo7aw5L3drF2QKB99F9e".to_string(), script_pubkey: "a9142d2b1ef5ee4cf6c3ebc8cf66a602783798f7875987".to_string(), derive_path: "m/49'/0'/0'/0/22".to_string(), sequence: 0, @@ -572,7 +571,7 @@ mod tests { txhash: "043adf9d813a2b8057454cc6f36c6081948af849966f9b9a33e5b653b02f227a".to_string(), vout: 0, amount: 200000000, - address: Address::from_str("37E2J9ViM4QFiewo7aw5L3drF2QKB99F9e").unwrap(), + address: "37E2J9ViM4QFiewo7aw5L3drF2QKB99F9e".to_string(), script_pubkey: "a9142d2b1ef5ee4cf6c3ebc8cf66a602783798f7875987".to_string(), derive_path: "m/49'/0'/0'/0/22".to_string(), sequence: 0, @@ -588,10 +587,11 @@ mod tests { utxos.push(utxo7); utxos.push(utxo8); let transaction_req_data = BtcTransaction { - to: Address::from_str("3PGEDofNu6aJ3KfgK9PHGt3EW3oZK5qY1a").unwrap(), + to: "3PGEDofNu6aJ3KfgK9PHGt3EW3oZK5qY1a".to_string(), amount: 750000000, unspents: utxos, fee: 502130, + chain_type: "".to_string(), }; let sign_result = transaction_req_data.sign_omni_segwit_transaction( Network::Bitcoin, @@ -617,7 +617,7 @@ mod tests { txhash: "983adf9d813a2b8057454cc6f36c6081948af849966f9b9a33e5b653b02f227a".to_string(), vout: 0, amount: 10000112345678, - address: Address::from_str("37E2J9ViM4QFiewo7aw5L3drF2QKB99F9e").unwrap(), + address: "37E2J9ViM4QFiewo7aw5L3drF2QKB99F9e".to_string(), script_pubkey: "a9142d2b1ef5ee4cf6c3ebc8cf66a602783798f7875987".to_string(), derive_path: "m/49'/0'/0'/0/22".to_string(), sequence: 0, @@ -625,10 +625,11 @@ mod tests { let mut utxos = Vec::new(); utxos.push(utxo); let transaction_req_data = BtcTransaction { - to: Address::from_str("3PGEDofNu6aJ3KfgK9PHGt3EW3oZK5qY1a").unwrap(), + to: "3PGEDofNu6aJ3KfgK9PHGt3EW3oZK5qY1a".to_string(), amount: 345678, unspents: utxos, fee: 502130, + chain_type: "".to_string(), }; let sign_result = transaction_req_data.sign_omni_segwit_transaction( Network::Bitcoin, @@ -651,7 +652,7 @@ mod tests { txhash: "983adf9d813a2b8057454cc6f36c6081948af849966f9b9a33e5b653b02f227a".to_string(), vout: 0, amount: 10000112345678, - address: Address::from_str("37E2J9ViM4QFiewo7aw5L3drF2QKB99F9e").unwrap(), + address: "37E2J9ViM4QFiewo7aw5L3drF2QKB99F9e".to_string(), script_pubkey: "a9142d2b1ef5ee4cf6c3ebc8cf66a602783798f7875987".to_string(), derive_path: "m/49'/0'/0'/0/22".to_string(), sequence: 0, @@ -661,10 +662,11 @@ mod tests { utxos.push(utxo.clone()); } let transaction_req_data = BtcTransaction { - to: Address::from_str("3PGEDofNu6aJ3KfgK9PHGt3EW3oZK5qY1a").unwrap(), + to: "3PGEDofNu6aJ3KfgK9PHGt3EW3oZK5qY1a".to_string(), amount: 345678, unspents: utxos, fee: 502130, + chain_type: "".to_string(), }; let sign_result = transaction_req_data.sign_omni_segwit_transaction( Network::Bitcoin, @@ -680,7 +682,7 @@ mod tests { txhash: "0dd195c815c5086c5995f43a0c67d28344ae5fa130739a5e03ef40fea54f2031".to_string(), vout: 0, amount: 14824854, - address: Address::from_str("mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN").unwrap(), + address: "mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN".to_string(), script_pubkey: "76a914383fb81cb0a3fc724b5e08cf8bbd404336d711f688ac".to_string(), derive_path: "m/44'/1'/0'/0/0".to_string(), sequence: 4294967295, @@ -691,10 +693,11 @@ mod tests { } let transaction_req_data = BtcTransaction { - to: Address::from_str("moLK3tBG86ifpDDTqAQzs4a9cUoNjVLRE3").unwrap(), + to: "moLK3tBG86ifpDDTqAQzs4a9cUoNjVLRE3".to_string(), amount: 10050000000, unspents: utxos, fee: 4000, + chain_type: "".to_string(), }; let sign_result = transaction_req_data.sign_omni_transaction( Network::Testnet, @@ -714,7 +717,7 @@ mod tests { txhash: "983adf9d813a2b8057454cc6f36c6081948af849966f9b9a33e5b653b02f227a".to_string(), vout: 0, amount: 1000, - address: Address::from_str("37E2J9ViM4QFiewo7aw5L3drF2QKB99F9e").unwrap(), + address: "37E2J9ViM4QFiewo7aw5L3drF2QKB99F9e".to_string(), script_pubkey: "a9142d2b1ef5ee4cf6c3ebc8cf66a602783798f7875987".to_string(), derive_path: "m/49'/0'/0'/0/22".to_string(), sequence: 0, @@ -723,10 +726,11 @@ mod tests { utxos.push(utxo); let transaction_req_data = BtcTransaction { - to: Address::from_str("3PGEDofNu6aJ3KfgK9PHGt3EW3oZK5qY1a").unwrap(), + to: "3PGEDofNu6aJ3KfgK9PHGt3EW3oZK5qY1a".to_string(), amount: 100, unspents: utxos, fee: 900, + chain_type: "".to_string(), }; let sign_result = transaction_req_data.sign_omni_segwit_transaction( Network::Bitcoin, @@ -742,7 +746,7 @@ mod tests { txhash: "0dd195c815c5086c5995f43a0c67d28344ae5fa130739a5e03ef40fea54f2031".to_string(), vout: 0, amount: 1000, - address: Address::from_str("mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN").unwrap(), + address: "mkeNU5nVnozJiaACDELLCsVUc8Wxoh1rQN".to_string(), script_pubkey: "76a914383fb81cb0a3fc724b5e08cf8bbd404336d711f688ac".to_string(), derive_path: "m/44'/1'/0'/0/0".to_string(), sequence: 4294967295, @@ -751,10 +755,11 @@ mod tests { utxos.push(utxo); let transaction_req_data = BtcTransaction { - to: Address::from_str("moLK3tBG86ifpDDTqAQzs4a9cUoNjVLRE3").unwrap(), + to: "moLK3tBG86ifpDDTqAQzs4a9cUoNjVLRE3".to_string(), amount: 100, unspents: utxos, fee: 900, + chain_type: "".to_string(), }; let sign_result = transaction_req_data.sign_omni_transaction( Network::Testnet, diff --git a/imkey-core/ikc-wallet/coin-btc-fork/src/common.rs b/imkey-core/ikc-wallet/coin-btc-fork/src/common.rs index 1bc283dd..eb940432 100644 --- a/imkey-core/ikc-wallet/coin-btc-fork/src/common.rs +++ b/imkey-core/ikc-wallet/coin-btc-fork/src/common.rs @@ -1,7 +1,6 @@ use crate::address::BtcForkAddress; use crate::btcforkapi::Utxo; use crate::Result; -use bitcoin::secp256k1::Secp256k1 as BitcoinSecp256k1; use bitcoin::util::base58; use bitcoin::util::bip32::{ChainCode, ChildNumber, ExtendedPubKey}; use bitcoin::Network; diff --git a/imkey-core/ikc/src/btc_address.rs b/imkey-core/ikc/src/btc_address.rs index 464e8ca0..380ba905 100644 --- a/imkey-core/ikc/src/btc_address.rs +++ b/imkey-core/ikc/src/btc_address.rs @@ -4,40 +4,56 @@ use crate::api::{ use crate::error_handling::Result; use crate::message_handler::encode_message; use bitcoin::network::constants::Network; -use bitcoin::schnorr::UntweakedPublicKey; -use bitcoin::secp256k1::Secp256k1; -use bitcoin::{secp256k1, Address}; use coin_bitcoin::address::BtcAddress; -use ikc_common::utility::{hex_to_bytes, network_convert}; -use prost::Message; +use coin_bitcoin::btc_kin_address::BtcKinAddress; +use coin_bitcoin::network::BtcKinNetwork; +use ikc_common::error::CommonError; +use ikc_common::utility::network_convert; pub fn get_address(param: &AddressParam) -> Result> { - let network = network_convert(param.network.as_ref()); let account_path = param.path.to_string(); let main_address: String; let receive_address: String; + let network = BtcKinNetwork::find_by_coin(¶m.chain_type, ¶m.network); + if network.is_none() { + return Err(CommonError::MissingNetwork.into()); + } + let network = network.unwrap(); + match param.seg_wit.as_str() { "P2WPKH" => { - main_address = BtcAddress::p2shwpkh(network, format!("{}/0/0", account_path).as_str())?; + main_address = + BtcKinAddress::p2shwpkh(network, format!("{}/0/0", account_path).as_str())? + .to_string(); receive_address = - BtcAddress::p2shwpkh(network, format!("{}/0/1", account_path).as_str())?; + BtcKinAddress::p2shwpkh(network, format!("{}/0/1", account_path).as_str())? + .to_string(); } "VERSION_0" => { - main_address = BtcAddress::p2wpkh(network, format!("{}/0/0", account_path).as_str())?; + main_address = + BtcKinAddress::p2wpkh(network, format!("{}/0/0", account_path).as_str())? + .to_string(); receive_address = - BtcAddress::p2wpkh(network, format!("{}/0/1", account_path).as_str())?; + BtcKinAddress::p2wpkh(network, format!("{}/0/1", account_path).as_str())? + .to_string(); } "VERSION_1" => { - main_address = BtcAddress::p2tr(network, format!("{}/0/0", account_path).as_str())?; - receive_address = BtcAddress::p2tr(network, format!("{}/0/1", account_path).as_str())?; + main_address = + BtcKinAddress::p2tr(network, format!("{}/0/0", account_path).as_str())?.to_string(); + receive_address = + BtcKinAddress::p2tr(network, format!("{}/0/1", account_path).as_str())?.to_string(); } _ => { - main_address = BtcAddress::p2pkh(network, format!("{}/0/0", account_path).as_str())?; - receive_address = BtcAddress::p2pkh(network, format!("{}/0/1", account_path).as_str())?; + main_address = BtcKinAddress::p2pkh(network, format!("{}/0/0", account_path).as_str())? + .to_string(); + receive_address = + BtcKinAddress::p2pkh(network, format!("{}/0/1", account_path).as_str())? + .to_string(); } } + let network = network_convert(param.network.as_ref()); let enc_xpub = get_enc_xpub(network, param.path.as_ref())?; let external_address = ExternalAddress { @@ -100,30 +116,6 @@ pub fn register_btc_address(param: &AddressParam) -> Result> { encode_message(address_message) } -// pub fn display_btc_legacy_address(param: &AddressParam) -> Result> { -// let network = network_convert(param.network.as_ref()); -// let address = BtcAddress::display_address(network, ¶m.path)?; -// -// let address_message = AddressResult { -// address, -// path: param.path.to_string(), -// chain_type: param.chain_type.to_string(), -// }; -// encode_message(address_message) -// } -// -// pub fn display_segwit_address(param: &AddressParam) -> Result> { -// let network = network_convert(param.network.as_ref()); -// let address = BtcAddress::display_segwit_address(network, ¶m.path)?; -// -// let address_message = AddressResult { -// path: param.path.to_string(), -// chain_type: param.chain_type.to_string(), -// address, -// }; -// encode_message(address_message) -// } - pub fn derive_account(param: &AddressParam) -> Result> { let network = network_convert(param.network.as_ref()); let path = format!("{}/0/0", param.path); diff --git a/imkey-core/ikc/src/btc_signer.rs b/imkey-core/ikc/src/btc_signer.rs index 19aa7776..517ca7d6 100644 --- a/imkey-core/ikc/src/btc_signer.rs +++ b/imkey-core/ikc/src/btc_signer.rs @@ -1,13 +1,12 @@ use crate::error_handling::Result; use crate::message_handler::encode_message; -use bitcoin::{Address, Network}; +use bitcoin::Network; use coin_bitcoin::btcapi::{BtcMessageInput, BtcTxInput, BtcTxOutput}; use coin_bitcoin::message::MessageSinger; use coin_bitcoin::transaction::{BtcTransaction, Utxo}; use ikc_common::path::get_account_path; use ikc_common::SignParam; use prost::Message; -use std::str::FromStr; pub fn sign_btc_transaction(data: &[u8], sign_param: &SignParam) -> Result> { let input: BtcTxInput = BtcTxInput::decode(data).expect("BtcTxInput"); @@ -30,7 +29,7 @@ pub fn btc_sign(param: &BtcTxInput, sign_param: &SignParam) -> Result> { txhash: utxo.tx_hash.to_string(), vout: utxo.vout, amount: utxo.amount, - address: Address::from_str(&utxo.address).unwrap(), + address: utxo.address.to_string(), script_pubkey: utxo.script_pub_key.to_string(), derive_path: utxo.derived_path.to_uppercase(), sequence: utxo.sequence, @@ -39,32 +38,20 @@ pub fn btc_sign(param: &BtcTxInput, sign_param: &SignParam) -> Result> { } let btc_tx = BtcTransaction { - to: Address::from_str(¶m.to).unwrap(), + to: param.to.to_string(), amount: param.amount, unspents, fee: param.fee, + chain_type: sign_param.chain_type.clone(), }; - let network = if sign_param.network == "TESTNET".to_string() { - Network::Testnet - } else { - Network::Bitcoin - }; - // let op_return: Option<&[u8]>; - // if let Some(extra) = param.extra.clone() { - // let data = hex_to_bytes(&extra.op_return.clone())?; - // op_return = Some(data.clone().as_slice()) - // // op_return = Some(hex::decode(extra.op_return.clone())?.as_slice()); - // } else { - // op_return = None; - // } let op_return = match ¶m.extra { Some(extra) => Some(extra.op_return.clone()), _ => None, }; - let signed = btc_tx.sign_Transaction( - network, + let signed = btc_tx.sign_transaction( + &sign_param.network, &sign_param.path, param.change_address_index, op_return.as_deref(), @@ -85,7 +72,7 @@ pub fn sign_usdt_transaction(input: &BtcTxInput, sign_param: &SignParam) -> Resu txhash: utxo.tx_hash.to_string(), vout: utxo.vout, amount: utxo.amount, - address: Address::from_str(&utxo.address).unwrap(), + address: utxo.address.to_string(), script_pubkey: utxo.script_pub_key.to_string(), derive_path: utxo.derived_path.to_string(), sequence: utxo.sequence, @@ -94,10 +81,11 @@ pub fn sign_usdt_transaction(input: &BtcTxInput, sign_param: &SignParam) -> Resu } let btc_tx = BtcTransaction { - to: Address::from_str(&input.to).unwrap(), + to: input.to.to_string(), amount: input.amount, unspents: unspents, fee: input.fee, + chain_type: sign_param.chain_type.clone(), }; let network = if sign_param.network == "TESTNET".to_string() { @@ -126,7 +114,7 @@ pub fn sign_usdt_segwit_transaction(input: &BtcTxInput, sign_param: &SignParam) txhash: utxo.tx_hash.to_string(), vout: utxo.vout, amount: utxo.amount, - address: Address::from_str(&utxo.address).unwrap(), + address: utxo.address.to_string(), script_pubkey: utxo.script_pub_key.to_string(), derive_path: utxo.derived_path.to_string(), sequence: utxo.sequence, @@ -135,11 +123,11 @@ pub fn sign_usdt_segwit_transaction(input: &BtcTxInput, sign_param: &SignParam) } let btc_tx = BtcTransaction { - to: Address::from_str(&input.to).unwrap(), - // change_idx: input.change_address_index as i32, + to: input.to.to_string(), amount: input.amount, unspents: unspents, fee: input.fee, + chain_type: sign_param.chain_type.clone(), }; let network = if sign_param.network == "TESTNET".to_string() { diff --git a/imkey-core/ikc/src/handler.rs b/imkey-core/ikc/src/handler.rs index 1dca94b4..d9c60d1f 100644 --- a/imkey-core/ikc/src/handler.rs +++ b/imkey-core/ikc/src/handler.rs @@ -11,7 +11,9 @@ use bitcoin::util::bip32::ExtendedPubKey; use bitcoin::Network; use coin_bch::address::BchAddress; use coin_bitcoin::address::BtcAddress; +use coin_bitcoin::btc_kin_address::{AddressTrait, BtcKinAddress, ImkeyPublicKey}; use coin_bitcoin::btcapi::PsbtInput; +use coin_bitcoin::network::BtcKinNetwork; use coin_btc_fork::address::BtcForkAddress; use coin_btc_fork::btc_fork_network::network_from_param; use coin_ckb::address::CkbAddress; @@ -22,6 +24,7 @@ use coin_filecoin::address::FilecoinAddress; use coin_substrate::address::{AddressType, SubstrateAddress}; use coin_tron::address::TronAddress; use ikc_common::curve::CurveType; +use ikc_common::error::CommonError; use ikc_common::path::get_account_path; use ikc_common::utility::{ encrypt_xpub, extended_pub_key_derive, from_ss58check_with_version, get_xpub_prefix, @@ -51,20 +54,29 @@ pub(crate) fn derive_accounts(data: &[u8]) -> Result> { }; let ext_public_key = match derivation.chain_type.as_str() { - "BITCOIN" => { - let network = network_convert(derivation.network.as_str()); - let public_key = BtcAddress::get_pub_key(&derivation.path)?; + "BITCOIN" | "DOGECOIN" => { + let network = BtcKinNetwork::find_by_coin( + &derivation.chain_type, + &derivation.network.to_uppercase(), + ); + if network.is_none() { + return Err(CommonError::MissingNetwork.into()); + } + let network = network.unwrap(); + + let public_key = ImkeyPublicKey::get_pub_key(&derivation.path)?; let public_key = uncompress_pubkey_2_compress(&public_key); account_rsp.public_key = format!("0x{}", public_key); let address = match derivation.seg_wit.as_str() { - "P2WPKH" => BtcAddress::p2shwpkh(network, &derivation.path)?, - "VERSION_0" => BtcAddress::p2wpkh(network, &derivation.path)?, - "VERSION_1" => BtcAddress::p2tr(network, &derivation.path)?, - _ => BtcAddress::p2pkh(network, &derivation.path)?, + "P2WPKH" => BtcKinAddress::p2shwpkh(&network, &derivation.path)?.to_string(), + "VERSION_0" => BtcKinAddress::p2wpkh(network, &derivation.path)?.to_string(), + "VERSION_1" => BtcKinAddress::p2tr(network, &derivation.path)?.to_string(), + _ => BtcKinAddress::p2pkh(network, &derivation.path)?.to_string(), }; account_rsp.address = address; - BtcAddress::get_xpub(network, &account_path)? + let network = network_convert(derivation.network.as_str()); + ImkeyPublicKey::get_xpub(network, &account_path)? } "LITECOIN" => { let network = network_convert(derivation.network.as_str()); @@ -192,13 +204,17 @@ pub(crate) fn derive_sub_accounts(data: &[u8]) -> Result> { account.path = relative_path; let address = match param.chain_type.as_str() { "ETHEREUM" => EthAddress::from_pub_key(pub_key_uncompressed)?, - "BITCOIN" => { - let network = network_convert(¶m.network); - BtcAddress::from_public_key( + "BITCOIN" | "DOGECOIN" => { + let network = BtcKinNetwork::find_by_coin(¶m.chain_type, ¶m.network); + if network.is_none() { + return Err(CommonError::MissingNetwork.into()); + } + BtcKinAddress::from_public_key( &hex::encode(pub_key_uncompressed), - network, + &network.unwrap(), ¶m.seg_wit, )? + .to_string() } "LITECOIN" => { let btc_fork_network = @@ -229,12 +245,11 @@ pub(crate) fn get_extended_public_keys(data: &[u8]) -> Result> { let param: GetExtendedPublicKeysParam = GetExtendedPublicKeysParam::decode(data)?; let mut extended_public_keys = vec![]; for public_key_derivation in param.derivations.iter() { - // if "".eq(&public_key_derivation.path) || &public_key_derivation.path.split("/") { } if !public_key_derivation.curve.eq("secp256k1") { return Err(anyhow!("unsupported_curve_type")); } let extended_public_key = match public_key_derivation.chain_type.as_str() { - "BITCOIN" | "LITECOIN" | "BITCOINCASH" => { + "BITCOIN" | "LITECOIN" | "BITCOINCASH" | "DOGECOIN" => { BtcAddress::get_xpub(Network::Bitcoin, public_key_derivation.path.as_str())? } "ETHEREUM" => EthAddress::get_xpub(public_key_derivation.path.as_str())?, @@ -261,7 +276,7 @@ pub(crate) fn get_public_keys(data: &[u8]) -> Result> { let public_key = match public_key_derivation.curve.as_str() { "secp256k1" => { let mut public_key = match public_key_derivation.chain_type.as_str() { - "BITCOIN" | "LITECOIN" | "BITCOINCASH" => { + "BITCOIN" | "LITECOIN" | "BITCOINCASH" | "DOGECOIN" => { BtcAddress::get_pub_key(&public_key_derivation.path)? } diff --git a/imkey-core/ikc/src/lib.rs b/imkey-core/ikc/src/lib.rs index d1f76fbf..429b18b2 100644 --- a/imkey-core/ikc/src/lib.rs +++ b/imkey-core/ikc/src/lib.rs @@ -138,7 +138,7 @@ pub unsafe extern "C" fn call_imkey_api(hex_str: *const c_char) -> *const c_char let param: AddressParam = AddressParam::decode(action.param.unwrap().value.as_slice()) .expect("imkey_illegal_param"); match param.chain_type.as_str() { - "BITCOIN" => btc_address::get_address(¶m), + "BITCOIN" | "DEGECOIN" => btc_address::get_address(¶m), "ETHEREUM" => ethereum_address::get_address(¶m), "COSMOS" => cosmos_address::get_address(¶m), "FILECOIN" => filecoin_address::get_address(¶m), @@ -529,6 +529,30 @@ mod tests { chain_id: "".to_string(), curve: "secp256k1".to_string(), }, + Derivation { + chain_type: "DOGECOIN".to_string(), + path: "m/44'/3'/0'/0/0".to_string(), + network: "MAINNET".to_string(), + seg_wit: "NONE".to_string(), + chain_id: "".to_string(), + curve: "secp256k1".to_string(), + }, + Derivation { + chain_type: "DOGECOIN".to_string(), + path: "m/44'/1'/0'/0/0".to_string(), + network: "MAINNET".to_string(), + seg_wit: "NONE".to_string(), + chain_id: "".to_string(), + curve: "secp256k1".to_string(), + }, + Derivation { + chain_type: "DOGECOIN".to_string(), + path: "m/44'/1'/0'/0/0".to_string(), + network: "MAINNET".to_string(), + seg_wit: "VERSION_1".to_string(), + chain_id: "".to_string(), + curve: "secp256k1".to_string(), + }, ]; let param = DeriveAccountsParam { derivations }; let action: ImkeyAction = ImkeyAction { @@ -805,6 +829,39 @@ mod tests { ); assert_eq!("tpubDCfbnFQU7fU3Rg3vefdeKXdAFw4V9wt3qoyFTDt6vR9fiswpcp9T7BUuBHtsDHv5JygbojgUUhEWBeUmzoA7Nz8Uzewr9aJQE9sBnsJkNxG", derived_accounts.accounts[23].extended_public_key); assert_eq!("XMpKliPCkntBO51gk2e9O2kNxYcIe64boiJ3yjdiYdGJjbM3GeEUIaJnrNR13mqKLphye+RbzfMnaITZG+qSTYxFT2p6SEoeBUxionjKb9gVJ/AbUkseruRVJ3I8pI0Gefd/KoJe76bFBZhGEXSD2Q==", derived_accounts.accounts[23].encrypted_extended_public_key); + + assert_eq!( + "DQ4tVEqdPWHc1aVBm4Sfwft8XyNRPMEchR", + derived_accounts.accounts[24].address + ); + assert_eq!( + "0x030940ee016f241f8355f8bbeac3e36ed95aaa0472e9e0d3eb3fa999c28512335f", + derived_accounts.accounts[24].public_key + ); + assert_eq!("xpub6CDSaXHQokkKmHHG2kNCFZeirJkcZgRZE97ZZUtViif3SFHSNVAvRpWC3CxeRt2VZetEGCcPTmWEFpKF4NDeeZrMNPQgfUaX5Hkw89kW8qE", derived_accounts.accounts[24].extended_public_key); + assert_eq!("xfp4vzRYZmLL7L/3HQZeGxbWkkU5Pr/2MPb2t3NvPiI+t+8SbOof9Vo3tW6B/5JO+Qt4Dud4d+OTS1rbcZ35F8ldMbClq/z/+vaN2PwkiLmF2WrKJofr9BdHbfyLbQeg6LJ02hZ7yVAjP7w5pn2S8w==", derived_accounts.accounts[24].encrypted_extended_public_key); + + assert_eq!( + "DAGWiHeAHCTLUTsBEFMWviSkdGeZGveASM", + derived_accounts.accounts[25].address + ); + assert_eq!( + "0x033d710ab45bb54ac99618ad23b3c1da661631aa25f23bfe9d22b41876f1d46e4e", + derived_accounts.accounts[25].public_key + ); + assert_eq!("xpub6CSt8ZdrPg6j9ECzMGGDaKJJDUe8Cfm4xHjVrvL7PyGkBGdog8asBznBaZQiYbRtCdWRUAKGpKcbyUYMUUwgmiNt7mPs1QCUMhyHB6rBURT", derived_accounts.accounts[25].extended_public_key); + assert_eq!("aZQFapKlNXVFODnqcTrkYcUdEBOJng0detiaBwO/7yNBWxxukf9/GJOn1dUh4oumFTtHoNNsBxYjYXpMdO7HMksOlOOJUCFNGRvVkiS5W83nAMTTDDbJGlC9ZB0lbm6wC4RYP3uGlg1anIl2BOW+mg==", derived_accounts.accounts[25].encrypted_extended_public_key); + + assert_eq!( + "1pd2gajgcpr7c5ajgl377sgmqexw5jxqvl305zw2a7aeujf8pun7ksh45tuj", + derived_accounts.accounts[26].address + ); + assert_eq!( + "0x033d710ab45bb54ac99618ad23b3c1da661631aa25f23bfe9d22b41876f1d46e4e", + derived_accounts.accounts[26].public_key + ); + assert_eq!("xpub6CSt8ZdrPg6j9ECzMGGDaKJJDUe8Cfm4xHjVrvL7PyGkBGdog8asBznBaZQiYbRtCdWRUAKGpKcbyUYMUUwgmiNt7mPs1QCUMhyHB6rBURT", derived_accounts.accounts[26].extended_public_key); + assert_eq!("aZQFapKlNXVFODnqcTrkYcUdEBOJng0detiaBwO/7yNBWxxukf9/GJOn1dUh4oumFTtHoNNsBxYjYXpMdO7HMksOlOOJUCFNGRvVkiS5W83nAMTTDDbJGlC9ZB0lbm6wC4RYP3uGlg1anIl2BOW+mg==", derived_accounts.accounts[26].encrypted_extended_public_key); } #[test] @@ -1521,6 +1578,141 @@ mod tests { ); } + #[test] + fn test_dogecoin_derive_sub_accounts() { + let mut derivation = Derivation { + chain_type: "DOGECOIN".to_string(), + path: "m/44'/0'/0'/0/0".to_string(), + network: "MAINNET".to_string(), + seg_wit: "NONE".to_string(), + chain_id: "".to_string(), + curve: "secp256k1".to_string(), + }; + let derived_accounts_result = derive_account(derivation.clone()); + let mut derive_sub_accounts_param = DeriveSubAccountsParam { + chain_type: "DOGECOIN".to_string(), + curve: "secp256k1".to_string(), + network: "MAINNET".to_string(), + seg_wit: "NONE".to_string(), + relative_paths: vec!["0/0".to_string(), "0/1".to_string()], + extended_public_key: derived_accounts_result.accounts[0] + .extended_public_key + .to_string(), + }; + let derived_sub_accounts = derive_sub_account(derive_sub_accounts_param.clone()); + assert_eq!( + "D78C2FooMJe77f6WtcZQhW7YtGuJNVLesB", + derived_sub_accounts.accounts[0].address + ); + assert_eq!( + "0x026b5b6a9d041bc5187e0b34f9e496436c7bff261c6c1b5f3c06b433c61394b868", + derived_sub_accounts.accounts[0].public_key + ); + assert_eq!( + "xpub6CqzLtyKdJN53jPY13W6GdyB8ZGWuFZuBPU4Xh9DXm6Q1cULVLtsyfXSjx4G77rNdCRBgi83LByaWxjtDaZfLAKT6vFUq3EhPtNwTpJigx8", + derived_sub_accounts.accounts[0].extended_public_key + ); + assert_eq!( + "BdgvWHN/Uh/K526q/+CdpGwEPZ41SvZHHGSgiSqhFesjErdbo6UnJMIoDOHV94qW8fd2KBW18UG3nTzDwS7a5oArqPtv+2aE9+1bNvCdtYoAx3979N3vbX4Xxn/najTABykXrJDjgpoaXxSo/xTktQ==", + derived_sub_accounts.accounts[0].encrypted_extended_public_key + ); + assert_eq!( + "DDE8E8VmhDHm5HyEzAkJkE2Wdkyenc7wCc", + derived_sub_accounts.accounts[1].address + ); + assert_eq!( + "0x024fb7df3961e08f01025e434ea19708a4317d2fe59775cddd38df6e8a2d30697d", + derived_sub_accounts.accounts[1].public_key + ); + + derivation.seg_wit = "P2WPKH".to_string(); + derivation.path = "m/84'/0'/0'/0/0".to_string(); + let derived_accounts_result = derive_account(derivation.clone()); + derive_sub_accounts_param.seg_wit = "P2WPKH".to_string(); + derive_sub_accounts_param.extended_public_key = derived_accounts_result.accounts[0] + .extended_public_key + .to_string(); + let derived_sub_accounts = derive_sub_account(derive_sub_accounts_param.clone()); + assert_eq!( + "9vz3rYvWamWSBpezGmxtnj1WRhTYttsDJa", + derived_sub_accounts.accounts[0].address + ); + assert_eq!( + "0x0324778f934a20a9ca06cec3fb7176ccbc054278b9d5d7f0a1077582367af92e75", + derived_sub_accounts.accounts[0].public_key + ); + assert_eq!( + "xpub6CKMszasQeidek6fYD7g5N1mwUK3ouX8YHWs47MZyXh62GxsEQsU57NuN6GTS3Mh3bwykHGa14617A6HQoYFDSM9deJvgjDeEJxBYsfJ1bs", + derived_sub_accounts.accounts[0].extended_public_key + ); + assert_eq!( + "lclQ6y+KR+TfgLnRs457Yq/cX8LWLG5vajoDbXK+2uCu8iP3ARtWpkTatT+DJSXTMWjOQX6wrZ/h9VeFFQSO7ki1HDjfBcRTRd8LKKyxuRJEDI+bLJ4ZNJqMDJTcPGJZ2n0pXZX3+wCzxe7PmS0cpQ==", + derived_sub_accounts.accounts[0].encrypted_extended_public_key + ); + assert_eq!( + "AEnAz82LcjUDw1iieSxUMofK84raXDRPcV", + derived_sub_accounts.accounts[1].address + ); + + derivation.seg_wit = "VERSION_0".to_string(); + derivation.path = "m/84'/0'/0'/0/0".to_string(); + let derived_accounts_result = derive_account(derivation.clone()); + derive_sub_accounts_param.seg_wit = "VERSION_0".to_string(); + derive_sub_accounts_param.extended_public_key = derived_accounts_result.accounts[0] + .extended_public_key + .to_string(); + let derived_sub_accounts = derive_sub_account(derive_sub_accounts_param.clone()); + assert_eq!( + "1q05ec6z8df2vlzkxjxfd2xr3veypzm93whtnyq3", + derived_sub_accounts.accounts[0].address + ); + assert_eq!( + "0x0324778f934a20a9ca06cec3fb7176ccbc054278b9d5d7f0a1077582367af92e75", + derived_sub_accounts.accounts[0].public_key + ); + assert_eq!( + "xpub6CKMszasQeidek6fYD7g5N1mwUK3ouX8YHWs47MZyXh62GxsEQsU57NuN6GTS3Mh3bwykHGa14617A6HQoYFDSM9deJvgjDeEJxBYsfJ1bs", + derived_sub_accounts.accounts[0].extended_public_key + ); + assert_eq!( + "lclQ6y+KR+TfgLnRs457Yq/cX8LWLG5vajoDbXK+2uCu8iP3ARtWpkTatT+DJSXTMWjOQX6wrZ/h9VeFFQSO7ki1HDjfBcRTRd8LKKyxuRJEDI+bLJ4ZNJqMDJTcPGJZ2n0pXZX3+wCzxe7PmS0cpQ==", + derived_sub_accounts.accounts[0].encrypted_extended_public_key + ); + assert_eq!( + "1qak0g6t8syjpq36t8z3768sfz7n0uf0lc4x75yt", + derived_sub_accounts.accounts[1].address + ); + + derivation.seg_wit = "VERSION_1".to_string(); + derivation.path = "m/84'/0'/0'/0/0".to_string(); + let derived_accounts_result = derive_account(derivation.clone()); + derive_sub_accounts_param.seg_wit = "VERSION_1".to_string(); + derive_sub_accounts_param.extended_public_key = derived_accounts_result.accounts[0] + .extended_public_key + .to_string(); + let derived_sub_accounts = derive_sub_account(derive_sub_accounts_param.clone()); + assert_eq!( + "1pgf72k7lhzaz9gmlaxtrnq4kcmhnwwk0t6xnecr9rgpvrspf2tn8qzsahaz", + derived_sub_accounts.accounts[0].address + ); + assert_eq!( + "0x0324778f934a20a9ca06cec3fb7176ccbc054278b9d5d7f0a1077582367af92e75", + derived_sub_accounts.accounts[0].public_key + ); + assert_eq!( + "xpub6CKMszasQeidek6fYD7g5N1mwUK3ouX8YHWs47MZyXh62GxsEQsU57NuN6GTS3Mh3bwykHGa14617A6HQoYFDSM9deJvgjDeEJxBYsfJ1bs", + derived_sub_accounts.accounts[0].extended_public_key + ); + assert_eq!( + "lclQ6y+KR+TfgLnRs457Yq/cX8LWLG5vajoDbXK+2uCu8iP3ARtWpkTatT+DJSXTMWjOQX6wrZ/h9VeFFQSO7ki1HDjfBcRTRd8LKKyxuRJEDI+bLJ4ZNJqMDJTcPGJZ2n0pXZX3+wCzxe7PmS0cpQ==", + derived_sub_accounts.accounts[0].encrypted_extended_public_key + ); + assert_eq!( + "1pxlnp964ka353cuws7m3l7eshf38lyh0m2rme2y69n57t4dhlfq2sks3dcl", + derived_sub_accounts.accounts[1].address + ); + } + #[test] fn test_get_extended_public_keys() { connect_and_bind(); @@ -1576,6 +1768,11 @@ mod tests { path: "m/44'/309'/0'".to_string(), curve: "secp256k1".to_string(), }, + PublicKeyDerivation { + chain_type: "DOGECOIN".to_string(), + path: "m/44'/145'/0'/0/0".to_string(), + curve: "secp256k1".to_string(), + }, ]; let param = GetExtendedPublicKeysParam { derivations }; let action: ImkeyAction = ImkeyAction { @@ -1600,6 +1797,7 @@ mod tests { assert_eq!(extended_public_key.extended_public_keys[7], "xpub6CaaaWKi9NRFAnRyDFZxWKWs7Sh8d9WiaCspHVpkDcaVwqQFRH2z5ygLbHZs8yWtwyR3QhJLDJzbrdSTZRC9PWaRfAMNCruoSJnWhKFFCWV"); assert_eq!(extended_public_key.extended_public_keys[8], "xpub6CUtvjXi3yjmhjaC2GxjiWE9FbQs1TrtqAgRDhB2gmDBsPzTfwqZ7MvGGYScKiVx8PBNFSmHm4mCnFDCaX23c1nJS4p8ynR2wnGne4qEEX9"); assert_eq!(extended_public_key.extended_public_keys[9], "xpub6CyvXfYwHJjJ9syYjG7qZMva1yMx93SUmqUcKHvoReUadCzqJA8mMXrrXQjRvzveuahgdQmCsdsuiCkMRsLec63DW83Wwu5UqKJQmsonKpo"); + assert_eq!(extended_public_key.extended_public_keys[10], "xpub6GZjFnyumLtEwC4KQkigvc3vXJdZvy71QxHTsFQQv1YtEUWNEwynKWsK2LBFZNLWdTk3w1Y9cRv4NN7V2pnDBoWgH3PkVE9r9Q2kSQL2zkH"); } #[test] @@ -1725,6 +1923,11 @@ mod tests { path: "m/0'/0'".to_string(), curve: "ed25519".to_string(), }, + PublicKeyDerivation { + chain_type: "DOGECOIN".to_string(), + path: "m/44'/145'/0'".to_string(), + curve: "secp256k1".to_string(), + }, ]; let param = GetPublicKeysParam { derivations }; let action: ImkeyAction = ImkeyAction { @@ -1791,6 +1994,10 @@ mod tests { result.public_keys[12], "0x4f3f24c064893a591d5a5b31990de9d12ed9da0c8650bcf98ede27e3da141401" ); + assert_eq!( + result.public_keys[13], + "0x0303f2f84851514bf2f40a46b5bb9dbf4e5913fbacde1a96968cda08f9fd882caa" + ); } #[test] diff --git a/imkey-core/test-data/keys00000113 b/imkey-core/test-data/keys00000113 new file mode 100644 index 00000000..2d5f00d8 --- /dev/null +++ b/imkey-core/test-data/keys00000113 @@ -0,0 +1 @@ +z4BU03OmJMtc4nQ1QOmc0ml+2VGwD2IQ0/q/sMxxZyptA459PvgSmaQz3AnkAl9imdEiiHKrD16uvGZkTBEuvPv2Vsk2jdLcNKEVZOkHVo4Eqn8Mzd2tu2uCuyfeJqMAxyZAtRBd7kbf7ImeDOlMyp2d9UJcZVEcBCno5dKIoNoBhmT96nspbUkIZ2KkM1pF4sRdfLY3fvheo0tWzO3n5L7+uHUXp15UDKwkCmxOW7NabgvhVNFqEspKjeYQ1ihj \ No newline at end of file