diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index b8db8c7c12..cef40380f0 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -1500,7 +1500,7 @@ impl SwapOps for EthCoin { _secret_hash: &[u8], spend_tx: &[u8], watcher_reward: bool, - ) -> Result, String> { + ) -> Result<[u8; 32], String> { let unverified: UnverifiedTransactionWrapper = try_s!(rlp::decode(spend_tx)); let function_name = get_function_name("receiverSpend", watcher_reward); let function = try_s!(SWAP_CONTRACT.function(&function_name)); @@ -1522,7 +1522,7 @@ impl SwapOps for EthCoin { return ERR!("Invalid arguments in 'receiverSpend' call: {:?}", tokens); } match &tokens[2] { - Token::FixedBytes(secret) => Ok(secret.to_vec()), + Token::FixedBytes(secret) => Ok(try_s!(secret.as_slice().try_into())), _ => ERR!( "Expected secret to be fixed bytes, decoded function data is {:?}", tokens @@ -1572,7 +1572,7 @@ impl SwapOps for EthCoin { | EthPrivKeyPolicy::HDWallet { activated_key: ref key_pair, .. - } => key_pair_from_secret(key_pair.secret().as_bytes()).expect("valid key"), + } => key_pair_from_secret(key_pair.secret().as_fixed_bytes()).expect("valid key"), EthPrivKeyPolicy::Trezor => todo!(), #[cfg(target_arch = "wasm32")] EthPrivKeyPolicy::Metamask(_) => todo!(), @@ -1580,19 +1580,20 @@ impl SwapOps for EthCoin { } #[inline] - fn derive_htlc_pubkey(&self, _swap_unique_data: &[u8]) -> Vec { + fn derive_htlc_pubkey(&self, _swap_unique_data: &[u8]) -> [u8; 33] { match self.priv_key_policy { EthPrivKeyPolicy::Iguana(ref key_pair) | EthPrivKeyPolicy::HDWallet { activated_key: ref key_pair, .. - } => key_pair_from_secret(key_pair.secret().as_bytes()) + } => key_pair_from_secret(&key_pair.secret().to_fixed_bytes()) .expect("valid key") .public_slice() - .to_vec(), + .try_into() + .expect("valid key length!"), EthPrivKeyPolicy::Trezor => todo!(), #[cfg(target_arch = "wasm32")] - EthPrivKeyPolicy::Metamask(ref metamask_policy) => metamask_policy.public_key.as_bytes().to_vec(), + EthPrivKeyPolicy::Metamask(ref metamask_policy) => metamask_policy.public_key.0, } } diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 67b27ba8f4..83e97b7706 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -73,7 +73,7 @@ use secp256k1v24::PublicKey; use serde::Deserialize; use serde_json::Value as Json; use std::collections::{HashMap, HashSet}; -use std::convert::TryInto; +use std::convert::{TryFrom, TryInto}; use std::fmt; use std::io::Cursor; use std::net::SocketAddr; @@ -793,13 +793,13 @@ impl SwapOps for LightningCoin { _secret_hash: &[u8], spend_tx: &[u8], _watcher_reward: bool, - ) -> Result, String> { + ) -> Result<[u8; 32], String> { let payment_hash = payment_hash_from_slice(spend_tx).map_err(|e| e.to_string())?; let payment_hex = hex::encode(payment_hash.0); match self.db.get_payment_from_db(payment_hash).await { Ok(Some(payment)) => match payment.preimage { - Some(preimage) => Ok(preimage.0.to_vec()), + Some(preimage) => Ok(preimage.0), None => ERR!("Preimage for payment {} should be found on the database", payment_hex), }, Ok(None) => ERR!("Payment {} is not in the database when it should be!", payment_hex), @@ -857,8 +857,8 @@ impl SwapOps for LightningCoin { } #[inline] - fn derive_htlc_pubkey(&self, _swap_unique_data: &[u8]) -> Vec { - self.channel_manager.get_our_node_id().serialize().to_vec() + fn derive_htlc_pubkey(&self, _swap_unique_data: &[u8]) -> [u8; 33] { + self.channel_manager.get_our_node_id().serialize() } #[inline] @@ -1046,7 +1046,8 @@ impl MarketCoinOps for LightningCoin { .map_err(|_| SignatureError::InternalError("Error accessing node keys".to_string()))?; let private = Private { prefix: 239, - secret: H256::from(*secret_key.as_ref()), + secret: H256::from_slice(secret_key.as_ref()) + .map_to_mm(|err| SignatureError::InvalidRequest(err.to_string()))?, compressed: true, checksum_type: ChecksumType::DSHA256, }; @@ -1058,10 +1059,11 @@ impl MarketCoinOps for LightningCoin { let message_hash = self .sign_message_hash(message) .ok_or(VerificationError::PrefixNotFound)?; - let signature = CompactSignature::from( + let signature = CompactSignature::try_from( zbase32::decode_full_bytes_str(signature) .map_err(|e| VerificationError::SignatureDecodingError(e.to_string()))?, - ); + ) + .map_to_mm(|err| VerificationError::SignatureDecodingError(err.to_string()))?; let recovered_pubkey = Public::recover_compact(&H256::from(message_hash), &signature)?; Ok(recovered_pubkey.to_string() == pubkey) } diff --git a/mm2src/coins/lightning/ln_platform.rs b/mm2src/coins/lightning/ln_platform.rs index a3a4c22776..cfdc2185c7 100644 --- a/mm2src/coins/lightning/ln_platform.rs +++ b/mm2src/coins/lightning/ln_platform.rs @@ -74,7 +74,7 @@ pub async fn update_best_block( ) }, ElectrumBlockHeader::V14(h) => { - let block_header = match deserialize(&h.hex.into_vec()) { + let block_header = match deserialize(&h.hex.0) { Ok(header) => header, Err(e) => { error!("Block header deserialization error: {}", e.to_string()); diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 16ade17a6b..380220bc40 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -1119,7 +1119,7 @@ pub trait SwapOps { secret_hash: &[u8], spend_tx: &[u8], watcher_reward: bool, - ) -> Result, String>; + ) -> Result<[u8; 32], String>; fn check_tx_signed_by_pub(&self, tx: &[u8], expected_pub: &[u8]) -> Result>; @@ -1151,7 +1151,7 @@ pub trait SwapOps { fn derive_htlc_key_pair(&self, swap_unique_data: &[u8]) -> KeyPair; /// Derives an HTLC key-pair and returns a public key corresponding to that key. - fn derive_htlc_pubkey(&self, swap_unique_data: &[u8]) -> Vec; + fn derive_htlc_pubkey(&self, swap_unique_data: &[u8]) -> [u8; 33]; fn validate_other_pubkey(&self, raw_pubkey: &[u8]) -> MmResult<(), ValidateOtherPubKeyErr>; diff --git a/mm2src/coins/qrc20.rs b/mm2src/coins/qrc20.rs index df14fea09a..ba0806c554 100644 --- a/mm2src/coins/qrc20.rs +++ b/mm2src/coins/qrc20.rs @@ -992,7 +992,7 @@ impl SwapOps for Qrc20Coin { secret_hash: &[u8], spend_tx: &[u8], _watcher_reward: bool, - ) -> Result, String> { + ) -> Result<[u8; 32], String> { self.extract_secret_impl(secret_hash, spend_tx) } @@ -1034,7 +1034,7 @@ impl SwapOps for Qrc20Coin { utxo_common::derive_htlc_key_pair(self.as_ref(), swap_unique_data) } - fn derive_htlc_pubkey(&self, swap_unique_data: &[u8]) -> Vec { + fn derive_htlc_pubkey(&self, swap_unique_data: &[u8]) -> [u8; 33] { utxo_common::derive_htlc_pubkey(self, swap_unique_data) } diff --git a/mm2src/coins/qrc20/history.rs b/mm2src/coins/qrc20/history.rs index af3c41f078..e6c6eb7085 100644 --- a/mm2src/coins/qrc20/history.rs +++ b/mm2src/coins/qrc20/history.rs @@ -39,7 +39,9 @@ impl TxInternalId { return ERR!("Incorrect bytes len {}, expected {}", bytes.len(), EXPECTED_LEN); } - let tx_hash: H256Json = bytes[0..32].into(); + let mut tx_hash = [0u8; 32]; + tx_hash.copy_from_slice(&bytes[0..32]); + let tx_hash = H256Json::from(tx_hash); let buf = bytes[32..].to_vec(); let mut cursor = Cursor::new(buf); @@ -192,7 +194,7 @@ impl Qrc20Coin { let receipts = try_s!(self.utxo.rpc_client.get_transaction_receipts(&tx_hash).compat().await); // request Qtum transaction details to get a tx_hex, timestamp, block_height and calculate a miner_fee let mut input_transactions = HistoryUtxoTxMap::new(); - let qtum_details = try_s!(utxo_common::tx_details_by_hash(self, &tx_hash.0, &mut input_transactions).await); + let qtum_details = try_s!(utxo_common::tx_details_by_hash(self, &tx_hash, &mut input_transactions).await); // Deserialize the UtxoTx to get a script pubkey let qtum_tx: UtxoTx = try_s!(deserialize( try_s!(qtum_details.tx.tx_hex().ok_or("unexpected tx type")).as_slice() @@ -823,17 +825,21 @@ fn is_transfer_event_log(log: &LogEntry) -> bool { mod tests { use super::*; use common::block_on; + use hex::FromHex; use mm2_metrics::{MetricType, MetricsJson, MetricsOps}; use mm2_test_helpers::for_tests::find_metrics_in_json; use qrc20_tests::qrc20_coin_for_test; #[test] fn test_tx_internal_id() { - let tx_hash = hex::decode("39104d29d77ba83c5c6c63ab7a0f096301c443b4538dc6b30140453a40caa80a").unwrap(); - let expected_id = TxInternalId::new(tx_hash.as_slice().into(), 13, 257); + let tx_hash: [u8; 32] = hex::decode("39104d29d77ba83c5c6c63ab7a0f096301c443b4538dc6b30140453a40caa80a") + .unwrap() + .try_into() + .unwrap(); + let expected_id = TxInternalId::new(tx_hash.into(), 13, 257); let actual_bytes: BytesJson = expected_id.clone().into(); - let mut expected_bytes = tx_hash; + let mut expected_bytes = Vec::from(tx_hash); expected_bytes.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 13]); expected_bytes.extend_from_slice(&[0, 0, 0, 0, 0, 0, 1, 1]); assert_eq!(actual_bytes, expected_bytes.into()); @@ -852,10 +858,10 @@ mod tests { let (ctx, coin) = qrc20_coin_for_test(priv_key, None); ctx.metrics.init(); - let tx_hash: H256Json = hex::decode("85ede12ccc12fb1709c4d9e403e96c0c394b0916f2f6098d41d8dfa00013fcdb") - .unwrap() - .as_slice() - .into(); + let tx_hash: H256Json = + <[u8; 32]>::from_hex("85ede12ccc12fb1709c4d9e403e96c0c394b0916f2f6098d41d8dfa00013fcdb") + .unwrap() + .into(); let tx_height = 699545; let transfer_map_expected = block_on(coin.transfer_details_by_hash(tx_hash)).unwrap(); @@ -884,10 +890,10 @@ mod tests { let (ctx, coin) = qrc20_coin_for_test(priv_key, None); ctx.metrics.init(); - let tx_hash: H256Json = hex::decode("85ede12ccc12fb1709c4d9e403e96c0c394b0916f2f6098d41d8dfa00013fcdb") - .unwrap() - .as_slice() - .into(); + let tx_hash: H256Json = + <[u8; 32]>::from_hex("85ede12ccc12fb1709c4d9e403e96c0c394b0916f2f6098d41d8dfa00013fcdb") + .unwrap() + .into(); let tx_height = 699545; let transfer_map_expected = block_on(coin.transfer_details_by_hash(tx_hash)).unwrap(); @@ -926,10 +932,10 @@ mod tests { let (ctx, coin) = qrc20_coin_for_test(priv_key, None); ctx.metrics.init(); - let tx_hash: H256Json = hex::decode("85ede12ccc12fb1709c4d9e403e96c0c394b0916f2f6098d41d8dfa00013fcdb") - .unwrap() - .as_slice() - .into(); + let tx_hash: H256Json = + <[u8; 32]>::from_hex("85ede12ccc12fb1709c4d9e403e96c0c394b0916f2f6098d41d8dfa00013fcdb") + .unwrap() + .into(); let tx_height = 699545; let transfer_map_expected = block_on(coin.transfer_details_by_hash(tx_hash)).unwrap(); @@ -937,9 +943,8 @@ mod tests { .into_iter() .map(|(mut id, tx)| { // just another tx_hash - id.tx_hash = hex::decode("8a7270110ab7b56142b3bac89999276beb70320a7fe7666f460a05aa615eb0a0") + id.tx_hash = <[u8; 32]>::from_hex("8a7270110ab7b56142b3bac89999276beb70320a7fe7666f460a05aa615eb0a0") .unwrap() - .as_slice() .into(); (id, tx) }) @@ -966,10 +971,10 @@ mod tests { ]; let (ctx, coin) = qrc20_coin_for_test(priv_key, None); - let tx_hash: H256Json = hex::decode("35e03bc529528a853ee75dde28f27eec8ed7b152b6af7ab6dfa5d55ea46f25ac") - .unwrap() - .as_slice() - .into(); + let tx_hash: H256Json = + <[u8; 32]>::from_hex("35e03bc529528a853ee75dde28f27eec8ed7b152b6af7ab6dfa5d55ea46f25ac") + .unwrap() + .into(); let tx_height = 681443; let transfer_map_expected = block_on(coin.transfer_details_by_hash(tx_hash)).unwrap(); let mut history_map_expected = HistoryMapByHash::new(); @@ -991,10 +996,10 @@ mod tests { ]; let (ctx, coin) = qrc20_coin_for_test(priv_key, None); - let tx_hash: H256Json = hex::decode("85ede12ccc12fb1709c4d9e403e96c0c394b0916f2f6098d41d8dfa00013fcdb") - .unwrap() - .as_slice() - .into(); + let tx_hash: H256Json = + <[u8; 32]>::from_hex("85ede12ccc12fb1709c4d9e403e96c0c394b0916f2f6098d41d8dfa00013fcdb") + .unwrap() + .into(); let tx_height = 699545; let transfer_map_expected = block_on(coin.transfer_details_by_hash(tx_hash)).unwrap(); let mut history_map_expected = HistoryMapByHash::new(); @@ -1019,14 +1024,14 @@ mod tests { let metrics = MetricsArc::new(); metrics.init(); - let tx_hash_invalid: H256Json = hex::decode("0000000000000000000000000000000000000000000000000000000000000000") - .unwrap() - .as_slice() - .into(); - let tx_hash: H256Json = hex::decode("85ede12ccc12fb1709c4d9e403e96c0c394b0916f2f6098d41d8dfa00013fcdb") - .unwrap() - .as_slice() - .into(); + let tx_hash_invalid: H256Json = + <[u8; 32]>::from_hex("0000000000000000000000000000000000000000000000000000000000000000") + .unwrap() + .into(); + let tx_hash: H256Json = + <[u8; 32]>::from_hex("85ede12ccc12fb1709c4d9e403e96c0c394b0916f2f6098d41d8dfa00013fcdb") + .unwrap() + .into(); let tx_height = 699545; let transfer_map_expected = block_on(coin.transfer_details_by_hash(tx_hash)).unwrap(); let mut history_map_expected = HistoryMapByHash::new(); diff --git a/mm2src/coins/qrc20/qrc20_tests.rs b/mm2src/coins/qrc20/qrc20_tests.rs index 3e6dbc94dd..6d5556fc4d 100644 --- a/mm2src/coins/qrc20/qrc20_tests.rs +++ b/mm2src/coins/qrc20/qrc20_tests.rs @@ -481,8 +481,8 @@ fn test_extract_secret() { ]; let (_ctx, coin) = qrc20_coin_for_test(priv_key, None); - let expected_secret = &[1; 32]; - let secret_hash = &*dhash160(expected_secret); + let expected_secret = [1; 32]; + let secret_hash = &*dhash160(&expected_secret); // taker spent maker payment - d3f5dab4d54c14b3d7ed8c7f5c8cc7f47ccf45ce589fdc7cd5140a3c1c3df6e1 let tx_hex = hex::decode("01000000033f56ecafafc8602fde083ba868d1192d6649b8433e42e1a2d79ba007ea4f7abb010000006b48304502210093404e90e40d22730013035d31c404c875646dcf2fad9aa298348558b6d65ba60220297d045eac5617c1a3eddb71d4bca9772841afa3c4c9d6c68d8d2d42ee6de3950121022b00078841f37b5d30a6a1defb82b3af4d4e2d24dd4204d41f0c9ce1e875de1affffffff9cac7fe90d597922a1d92e05306c2215628e7ea6d5b855bfb4289c2944f4c73a030000006b483045022100b987da58c2c0c40ce5b6ef2a59e8124ed4ef7a8b3e60c7fb631139280019bc93022069649bcde6fe4dd5df9462a1fcae40598488d6af8c324cd083f5c08afd9568be0121022b00078841f37b5d30a6a1defb82b3af4d4e2d24dd4204d41f0c9ce1e875de1affffffff70b9870f2b0c65d220a839acecebf80f5b44c3ca4c982fa2fdc5552c037f5610010000006a473044022071b34dd3ebb72d29ca24f3fa0fc96571c815668d3b185dd45cc46a7222b6843f02206c39c030e618d411d4124f7b3e7ca1dd5436775bd8083a85712d123d933a51300121022b00078841f37b5d30a6a1defb82b3af4d4e2d24dd4204d41f0c9ce1e875de1affffffff020000000000000000c35403a0860101284ca402ed292b806a1835a1b514ad643f2acdb5c8db6b6a9714accff3275ea0d79a3f23be8fd00000000000000000000000000000000000000000000000000000000001312d000101010101010101010101010101010101010101010101010101010101010101000000000000000000000000d362e096e873eb7907e205fadc6175c6fec7bc440000000000000000000000009e032d4b0090a11dc40fe6c47601499a35d55fbb14ba8b71f3544b93e2f681f996da519a98ace0107ac2c02288d4010000001976a914783cf0be521101942da509846ea476e683aad83288ac0f047f5f").unwrap(); @@ -505,10 +505,10 @@ fn test_extract_secret_malicious() { // 1 - with an invalid secret (this case should be processed correctly) // 2 - correct spend tx let spend_tx = hex::decode("01000000022bc8299981ec0cea664cdf9df4f8306396a02e2067d6ac2d3770b34646d2bc2a010000006b483045022100eb13ef2d99ac1cd9984045c2365654b115dd8a7815b7fbf8e2a257f0b93d1592022060d648e73118c843e97f75fafc94e5ff6da70ec8ba36ae255f8c96e2626af6260121022b00078841f37b5d30a6a1defb82b3af4d4e2d24dd4204d41f0c9ce1e875de1affffffffd92a0a10ac6d144b36033916f67ae79889f40f35096629a5cd87be1a08f40ee7010000006b48304502210080cdad5c4770dfbeb760e215494c63cc30da843b8505e75e7bf9e8dad18568000220234c0b11c41bfbcdd50046c69059976aedabe17657fe43d809af71e9635678e20121022b00078841f37b5d30a6a1defb82b3af4d4e2d24dd4204d41f0c9ce1e875de1affffffff030000000000000000c35403a0860101284ca402ed292b8620ad3b72361a5aeba5dffd333fb64750089d935a1ec974d6a91ef4f24ff6ba0000000000000000000000000000000000000000000000000000000001312d000202020202020202020202020202020202020202020202020202020202020202000000000000000000000000d362e096e873eb7907e205fadc6175c6fec7bc440000000000000000000000009e032d4b0090a11dc40fe6c47601499a35d55fbb14ba8b71f3544b93e2f681f996da519a98ace0107ac20000000000000000c35403a0860101284ca402ed292b8620ad3b72361a5aeba5dffd333fb64750089d935a1ec974d6a91ef4f24ff6ba0000000000000000000000000000000000000000000000000000000001312d000101010101010101010101010101010101010101010101010101010101010101000000000000000000000000d362e096e873eb7907e205fadc6175c6fec7bc440000000000000000000000009e032d4b0090a11dc40fe6c47601499a35d55fbb14ba8b71f3544b93e2f681f996da519a98ace0107ac2b8ea82d3010000001976a914783cf0be521101942da509846ea476e683aad83288ac735d855f").unwrap(); - let expected_secret = &[1; 32]; - let secret_hash = &*dhash160(expected_secret); + let expected_secret = [1; 32]; + let secret_hash = &*dhash160(&expected_secret); let actual = block_on(coin.extract_secret(secret_hash, &spend_tx, false)); - assert_eq!(actual, Ok(expected_secret.to_vec())); + assert_eq!(actual, Ok(expected_secret)); } #[test] @@ -569,10 +569,10 @@ fn test_transfer_details_by_hash() { ]; let (_ctx, coin) = qrc20_coin_for_test(priv_key, None); let tx_hash_bytes = hex::decode("85ede12ccc12fb1709c4d9e403e96c0c394b0916f2f6098d41d8dfa00013fcdb").unwrap(); - let tx_hash: H256Json = tx_hash_bytes.as_slice().into(); + let tx_hash: [u8; 32] = tx_hash_bytes.clone().try_into().unwrap(); let tx_hex:BytesJson = hex::decode("0100000001426d27fde82e12e1ce84e73ca41e2a30420f4c94aaa37b30d4c5b8b4f762c042040000006a473044022032665891693ee732571cefaa6d322ec5114c78259f2adbe03a0d7e6b65fbf40d022035c9319ca41e5423e09a8a613ac749a20b8f5ad6ba4ad6bb60e4a020b085d009012103693bff1b39e8b5a306810023c29b95397eb395530b106b1820ea235fd81d9ce9ffffffff050000000000000000625403a08601012844095ea7b30000000000000000000000001549128bbfb33b997949b4105b6a6371c998e212000000000000000000000000000000000000000000000000000000000000000014d362e096e873eb7907e205fadc6175c6fec7bc44c20000000000000000625403a08601012844095ea7b30000000000000000000000001549128bbfb33b997949b4105b6a6371c998e21200000000000000000000000000000000000000000000000000000000000927c014d362e096e873eb7907e205fadc6175c6fec7bc44c20000000000000000835403a0860101284c640c565ae300000000000000000000000000000000000000000000000000000000000493e0000000000000000000000000d362e096e873eb7907e205fadc6175c6fec7bc440000000000000000000000000000000000000000000000000000000000000000141549128bbfb33b997949b4105b6a6371c998e212c20000000000000000835403a0860101284c640c565ae300000000000000000000000000000000000000000000000000000000000493e0000000000000000000000000d362e096e873eb7907e205fadc6175c6fec7bc440000000000000000000000000000000000000000000000000000000000000001141549128bbfb33b997949b4105b6a6371c998e212c231754b04000000001976a9149e032d4b0090a11dc40fe6c47601499a35d55fbb88acf7cd8b5f").unwrap().into(); - let details = block_on(coin.transfer_details_by_hash(tx_hash)).unwrap(); + let details = block_on(coin.transfer_details_by_hash(tx_hash.into())).unwrap(); let mut it = details.into_iter().sorted_by(|(id_x, _), (id_y, _)| id_x.cmp(id_y)); let expected_fee_details = |total_gas_fee: &str| -> TxFeeDetails { diff --git a/mm2src/coins/qrc20/swap.rs b/mm2src/coins/qrc20/swap.rs index 7370926684..26dea25ab8 100644 --- a/mm2src/coins/qrc20/swap.rs +++ b/mm2src/coins/qrc20/swap.rs @@ -26,7 +26,7 @@ pub struct Erc20PaymentDetails { pub struct ReceiverSpendDetails { pub swap_id: Vec, pub value: U256, - pub secret: Vec, + pub secret: [u8; 32], pub token_address: H160, pub sender: H160, } @@ -298,11 +298,11 @@ impl Qrc20Coin { Ok(found) } - pub fn extract_secret_impl(&self, secret_hash: &[u8], spend_tx: &[u8]) -> Result, String> { + pub fn extract_secret_impl(&self, secret_hash: &[u8], spend_tx: &[u8]) -> Result<[u8; 32], String> { let secret_hash = if secret_hash.len() == 32 { ripemd160(secret_hash) } else { - chain::hash::H160::from(secret_hash) + chain::hash::H160::from_slice(secret_hash)? }; let spend_tx: UtxoTx = try_s!(deserialize(spend_tx).map_err(|e| ERRL!("{:?}", e))); @@ -936,7 +936,7 @@ pub fn receiver_spend_call_details_from_script_pubkey(script_pubkey: &Script) -> }; let secret = match decoded.next() { - Some(Token::FixedBytes(hash)) => hash, + Some(Token::FixedBytes(hash)) => try_s!(hash.as_slice().try_into()), Some(token) => return ERR!("Payment tx 'secret_hash' arg is invalid, found {:?}", token), None => return ERR!("Couldn't find 'secret_hash' in erc20Payment call"), }; @@ -970,7 +970,7 @@ fn find_receiver_spend_with_swap_id_and_secret_hash( let expected_secret_hash = if expected_secret_hash.len() == 32 { ripemd160(expected_secret_hash) } else { - chain::hash::H160::from(expected_secret_hash) + chain::hash::H160::from_slice(expected_secret_hash).expect("this shouldn't fail") }; for (output_idx, output) in tx.outputs.iter().enumerate() { diff --git a/mm2src/coins/siacoin.rs b/mm2src/coins/siacoin.rs index bc57aaaf10..d54613cd3a 100644 --- a/mm2src/coins/siacoin.rs +++ b/mm2src/coins/siacoin.rs @@ -487,7 +487,7 @@ impl SwapOps for SiaCoin { _secret_hash: &[u8], _spend_tx: &[u8], _watcher_reward: bool, - ) -> Result, String> { + ) -> Result<[u8; 32], String> { unimplemented!() } @@ -504,7 +504,7 @@ impl SwapOps for SiaCoin { fn derive_htlc_key_pair(&self, _swap_unique_data: &[u8]) -> KeyPair { unimplemented!() } - fn derive_htlc_pubkey(&self, _swap_unique_data: &[u8]) -> Vec { unimplemented!() } + fn derive_htlc_pubkey(&self, _swap_unique_data: &[u8]) -> [u8; 33] { unimplemented!() } async fn can_refund_htlc(&self, _locktime: u64) -> Result { unimplemented!() } diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index 323637599d..6d34c786b5 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -73,7 +73,7 @@ use regex::Regex; use rpc::v1::types::Bytes as BytesJson; use serde_json::{self as json, Value as Json}; use std::collections::HashMap; -use std::convert::TryFrom; +use std::convert::{TryFrom, TryInto}; use std::io; use std::num::NonZeroU32; use std::ops::Deref; @@ -2359,8 +2359,12 @@ impl MmCoin for TendermintCoin { fn get_tx_hex_by_hash(&self, tx_hash: Vec) -> RawTransactionFut { let coin = self.clone(); - let hash = hex::encode_upper(H256::from(tx_hash.as_slice())); let fut = async move { + let len = tx_hash.len(); + let hash: [u8; 32] = tx_hash.try_into().map_to_mm(|_| { + RawTransactionError::InvalidHashError(format!("Invalid hash length: expected 32, got {}", len)) + })?; + let hash = hex::encode_upper(H256::from(hash)); let tx_from_rpc = coin.request_tx(hash).await?; Ok(RawTransactionRes { tx_hex: tx_from_rpc.encode_to_vec().into(), @@ -2920,7 +2924,7 @@ impl SwapOps for TendermintCoin { secret_hash: &[u8], spend_tx: &[u8], watcher_reward: bool, - ) -> Result, String> { + ) -> Result<[u8; 32], String> { let tx = try_s!(cosmrs::Tx::from_bytes(spend_tx)); let msg = try_s!(tx.body.messages.first().ok_or("Tx body couldn't be read.")); @@ -2930,7 +2934,7 @@ impl SwapOps for TendermintCoin { )); let htlc = try_s!(ClaimHtlcMsg::try_from(htlc_proto)); - Ok(try_s!(hex::decode(htlc.secret()))) + Ok(try_s!(try_s!(hex::decode(htlc.secret())).as_slice().try_into())) } fn check_tx_signed_by_pub(&self, tx: &[u8], expected_pub: &[u8]) -> Result> { @@ -2957,17 +2961,20 @@ impl SwapOps for TendermintCoin { #[inline] fn derive_htlc_key_pair(&self, _swap_unique_data: &[u8]) -> KeyPair { key_pair_from_secret( - self.activation_policy + &self + .activation_policy .activated_key_or_err() .expect("valid priv key") - .as_ref(), + .take(), ) .expect("valid priv key") } #[inline] - fn derive_htlc_pubkey(&self, _swap_unique_data: &[u8]) -> Vec { - self.activation_policy.public_key().expect("valid pubkey").to_bytes() + fn derive_htlc_pubkey(&self, _swap_unique_data: &[u8]) -> [u8; 33] { + let mut res = [0u8; 33]; + res.copy_from_slice(&self.activation_policy.public_key().expect("valid pubkey").to_bytes()); + res } fn validate_other_pubkey(&self, raw_pubkey: &[u8]) -> MmResult<(), ValidateOtherPubKeyErr> { @@ -3105,7 +3112,7 @@ pub fn tendermint_priv_key_policy( ) -> MmResult { match priv_key_build_policy { PrivKeyBuildPolicy::IguanaPrivKey(iguana) => { - let mm2_internal_key_pair = key_pair_from_secret(iguana.as_ref()).mm_err(|e| TendermintInitError { + let mm2_internal_key_pair = key_pair_from_secret(&iguana.take()).mm_err(|e| TendermintInitError { ticker: ticker.to_string(), kind: TendermintInitErrorKind::Internal(e.to_string()), })?; diff --git a/mm2src/coins/tendermint/tendermint_token.rs b/mm2src/coins/tendermint/tendermint_token.rs index e5cc90f895..b3df4e4cee 100644 --- a/mm2src/coins/tendermint/tendermint_token.rs +++ b/mm2src/coins/tendermint/tendermint_token.rs @@ -238,7 +238,7 @@ impl SwapOps for TendermintToken { secret_hash: &[u8], spend_tx: &[u8], watcher_reward: bool, - ) -> Result, String> { + ) -> Result<[u8; 32], String> { self.platform_coin .extract_secret(secret_hash, spend_tx, watcher_reward) .await @@ -271,7 +271,7 @@ impl SwapOps for TendermintToken { } #[inline] - fn derive_htlc_pubkey(&self, swap_unique_data: &[u8]) -> Vec { + fn derive_htlc_pubkey(&self, swap_unique_data: &[u8]) -> [u8; 33] { self.platform_coin.derive_htlc_pubkey(swap_unique_data) } diff --git a/mm2src/coins/tendermint/tendermint_tx_history_v2.rs b/mm2src/coins/tendermint/tendermint_tx_history_v2.rs index 3dc95e7443..17faf3321d 100644 --- a/mm2src/coins/tendermint/tendermint_tx_history_v2.rs +++ b/mm2src/coins/tendermint/tendermint_tx_history_v2.rs @@ -23,7 +23,7 @@ use mm2_state_machine::state_machine::StateMachineTrait; use primitives::hash::H256; use rpc::v1::types::Bytes as BytesJson; use std::cmp; -use std::convert::Infallible; +use std::convert::{Infallible, TryInto}; const TX_PAGE_SIZE: u8 = 50; @@ -131,7 +131,7 @@ impl CoinWithTxHistoryV2 for TendermintToken { _target: MyTxHistoryTarget, ) -> MmResult { let denom_hash = sha256(self.denom.to_string().as_bytes()); - let id = H256::from(denom_hash.as_slice()); + let id = H256::from(denom_hash.take()); Ok(GetTxHistoryFilters::for_address(self.platform_coin.account_id.to_string()).with_token_id(id.to_string())) } @@ -703,8 +703,28 @@ where let mut internal_id_hash = index.to_le_bytes().to_vec(); internal_id_hash.extend_from_slice(tx_hash.as_bytes()); drop_mutability!(internal_id_hash); + let len = internal_id_hash.len(); + // Discuss: This truncates `internal_id_hash` to 32 bytes instead of using all 33 bytes (index + tx_hash). + // This is a limitation kept for backward compatibility. Changing to 33 bytes would + // alter the internal_id calculation, causing existing wallets to see duplicate transactions + // in their history. A proper migration would be needed to safely transition to using the full 33 bytes. + let internal_id_hash: [u8; 32] = match internal_id_hash + .get(..32) + .and_then(|slice| slice.try_into().ok()) + { + Some(hash) => hash, + None => { + log::debug!( + "Invalid internal_id_hash length for tx '{}' at index {}: expected 32 bytes, got {} bytes.", + tx_hash, + index, + len + ); + continue; + }, + }; - let internal_id = H256::from(internal_id_hash.as_slice()).reversed().to_vec().into(); + let internal_id = H256::from(internal_id_hash).reversed().to_vec().into(); if let Ok(Some(_)) = storage .get_tx_from_history(&coin.history_wallet_id(), &internal_id) @@ -744,7 +764,7 @@ where let token_id: Option = match !is_platform_coin_tx { true => { let denom_hash = sha256(transfer_details.denom.clone().as_bytes()); - Some(H256::from(denom_hash.as_slice()).to_vec().into()) + Some(H256::from(denom_hash.take()).to_vec().into()) }, false => None, }; @@ -786,7 +806,7 @@ where fee_tx_details.my_balance_change = BigDecimal::default() - &fee_details.amount; fee_tx_details.coin = coin.platform_ticker().to_string(); // Non-reversed version of original internal id - fee_tx_details.internal_id = H256::from(internal_id_hash.as_slice()).to_vec().into(); + fee_tx_details.internal_id = H256::from(internal_id_hash).to_vec().into(); fee_tx_details.transaction_type = TransactionType::FeeForTokenTx; tx_details.push(fee_tx_details); diff --git a/mm2src/coins/test_coin.rs b/mm2src/coins/test_coin.rs index 43765ab0ba..3fb59b3538 100644 --- a/mm2src/coins/test_coin.rs +++ b/mm2src/coins/test_coin.rs @@ -196,7 +196,7 @@ impl SwapOps for TestCoin { secret_hash: &[u8], spend_tx: &[u8], watcher_reward: bool, - ) -> Result, String> { + ) -> Result<[u8; 32], String> { unimplemented!() } @@ -213,7 +213,7 @@ impl SwapOps for TestCoin { fn derive_htlc_key_pair(&self, _swap_unique_data: &[u8]) -> KeyPair { unimplemented!() } - fn derive_htlc_pubkey(&self, _swap_unique_data: &[u8]) -> Vec { unimplemented!() } + fn derive_htlc_pubkey(&self, _swap_unique_data: &[u8]) -> [u8; 33] { unimplemented!() } async fn can_refund_htlc(&self, locktime: u64) -> Result { unimplemented!() } diff --git a/mm2src/coins/utxo.rs b/mm2src/coins/utxo.rs index 6d98451c7f..5fd31153fb 100644 --- a/mm2src/coins/utxo.rs +++ b/mm2src/coins/utxo.rs @@ -1158,7 +1158,7 @@ pub trait UtxoStandardOps { /// * `input_transactions` - the cache of the already requested transactions. async fn tx_details_by_hash( &self, - hash: &[u8], + hash: &H256Json, input_transactions: &mut HistoryUtxoTxMap, ) -> Result; @@ -1305,11 +1305,11 @@ impl VerboseTransactionFrom { } } -pub fn compressed_key_pair_from_bytes(raw: &[u8], prefix: u8, checksum_type: ChecksumType) -> Result { - if raw.len() != 32 { - return ERR!("Invalid raw priv key len {}", raw.len()); - } - +pub fn compressed_key_pair_from_bytes( + raw: &[u8; 32], + prefix: u8, + checksum_type: ChecksumType, +) -> Result { let private = Private { prefix, compressed: true, @@ -1319,9 +1319,12 @@ pub fn compressed_key_pair_from_bytes(raw: &[u8], prefix: u8, checksum_type: Che Ok(try_s!(KeyPair::from_private(private))) } -pub fn compressed_pub_key_from_priv_raw(raw_priv: &[u8], sum_type: ChecksumType) -> Result { +pub fn compressed_pub_key_from_priv_raw(raw_priv: &[u8; 32], sum_type: ChecksumType) -> Result { let key_pair: KeyPair = try_s!(compressed_key_pair_from_bytes(raw_priv, 0, sum_type)); - Ok(H264::from(&**key_pair.public())) + match key_pair.public() { + Public::Compressed(pub_key) => Ok(*pub_key), + _ => ERR!("Invalid public key type"), + } } #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] diff --git a/mm2src/coins/utxo/bch.rs b/mm2src/coins/utxo/bch.rs index d71b6538e3..bcdb5e739a 100644 --- a/mm2src/coins/utxo/bch.rs +++ b/mm2src/coins/utxo/bch.rs @@ -1002,7 +1002,7 @@ impl SwapOps for BchCoin { secret_hash: &[u8], spend_tx: &[u8], _watcher_reward: bool, - ) -> Result, String> { + ) -> Result<[u8; 32], String> { utxo_common::extract_secret(secret_hash, spend_tx) } @@ -1033,7 +1033,7 @@ impl SwapOps for BchCoin { utxo_common::derive_htlc_key_pair(self.as_ref(), swap_unique_data) } - fn derive_htlc_pubkey(&self, swap_unique_data: &[u8]) -> Vec { + fn derive_htlc_pubkey(&self, swap_unique_data: &[u8]) -> [u8; 33] { utxo_common::derive_htlc_pubkey(self, swap_unique_data) } diff --git a/mm2src/coins/utxo/bchd_grpc.rs b/mm2src/coins/utxo/bchd_grpc.rs index da240508c6..c7c01d073b 100644 --- a/mm2src/coins/utxo/bchd_grpc.rs +++ b/mm2src/coins/utxo/bchd_grpc.rs @@ -10,6 +10,7 @@ use get_slp_trusted_validation_response::validity_result::ValidityResultType; use keys::hash::H256; use mm2_err_handle::prelude::*; use mm2_net::grpc_web::{post_grpc_web, PostGrpcWebErr}; +use std::convert::TryInto; #[derive(Debug, Display)] #[display(fmt = "Error {:?} on request to the url {}", err, to_url)] @@ -132,7 +133,20 @@ pub async fn validate_slp_utxos( let responses: Vec<(_, GetSlpTrustedValidationResponse)> = grpc_web_multi_url_request(&urls, &request).await?; for (url, response) in responses { for validation_result in response.results { - let actual_token_id = validation_result.token_id.as_slice().into(); + let actual_token_id = { + let token_id_len = validation_result.token_id.len(); + let arr: [u8; 32] = validation_result + .token_id + .try_into() + .map_to_mm(|_| ValidateSlpUtxosErr { + to_url: url.clone(), + kind: ValidateSlpUtxosErrKind::InvalidSlpTxData(format!( + "Invalid token_id length: expected 32 bytes, got {}", + token_id_len + )), + })?; + arr.into() + }; if actual_token_id != *token_id { return MmError::err(ValidateSlpUtxosErr { to_url: url.clone(), @@ -143,8 +157,23 @@ pub async fn validate_slp_utxos( }); } + let prev_out_hash = { + let prev_out_hash_len = validation_result.prev_out_hash.len(); + let arr: [u8; 32] = validation_result + .prev_out_hash + .try_into() + .map_to_mm(|_| ValidateSlpUtxosErr { + to_url: url.clone(), + kind: ValidateSlpUtxosErrKind::InvalidSlpTxData(format!( + "Invalid prev_out_hash length: expected 32 bytes, got {}", + prev_out_hash_len + )), + })?; + arr.into() + }; + let outpoint = OutPoint { - hash: validation_result.prev_out_hash.as_slice().into(), + hash: prev_out_hash, index: validation_result.prev_out_vout, }; diff --git a/mm2src/coins/utxo/qtum.rs b/mm2src/coins/utxo/qtum.rs index c5fbc67293..233e5efeb0 100644 --- a/mm2src/coins/utxo/qtum.rs +++ b/mm2src/coins/utxo/qtum.rs @@ -488,7 +488,7 @@ impl UtxoCommonOps for QtumCoin { impl UtxoStandardOps for QtumCoin { async fn tx_details_by_hash( &self, - hash: &[u8], + hash: &H256Json, input_transactions: &mut HistoryUtxoTxMap, ) -> Result { utxo_common::tx_details_by_hash(self, hash, input_transactions).await @@ -642,7 +642,7 @@ impl SwapOps for QtumCoin { secret_hash: &[u8], spend_tx: &[u8], _watcher_reward: bool, - ) -> Result, String> { + ) -> Result<[u8; 32], String> { utxo_common::extract_secret(secret_hash, spend_tx) } @@ -673,7 +673,7 @@ impl SwapOps for QtumCoin { utxo_common::derive_htlc_key_pair(self.as_ref(), swap_unique_data) } - fn derive_htlc_pubkey(&self, swap_unique_data: &[u8]) -> Vec { + fn derive_htlc_pubkey(&self, swap_unique_data: &[u8]) -> [u8; 33] { utxo_common::derive_htlc_pubkey(self, swap_unique_data) } diff --git a/mm2src/coins/utxo/rpc_clients/electrum_rpc/rpc_responses.rs b/mm2src/coins/utxo/rpc_clients/electrum_rpc/rpc_responses.rs index 75daac6f35..c045de98a4 100644 --- a/mm2src/coins/utxo/rpc_clients/electrum_rpc/rpc_responses.rs +++ b/mm2src/coins/utxo/rpc_clients/electrum_rpc/rpc_responses.rs @@ -1,3 +1,4 @@ +use bitcrypto::dhash256; use chain::{BlockHeader, BlockHeaderBits, BlockHeaderNonce, Transaction as UtxoTx}; use mm2_number::{BigDecimal, BigInt}; use rpc::v1::types::{Bytes as BytesJson, H256 as H256Json}; @@ -103,7 +104,7 @@ pub struct ElectrumBlockHeaderV14 { } impl ElectrumBlockHeaderV14 { - pub fn hash(&self) -> H256Json { self.hex.clone().into_vec()[..].into() } + pub fn hash(&self) -> H256Json { dhash256(&self.hex.clone().into_vec()).into() } } #[derive(Clone, Debug, Deserialize)] diff --git a/mm2src/coins/utxo/slp.rs b/mm2src/coins/utxo/slp.rs index cbc7780a34..f58e47dc8b 100644 --- a/mm2src/coins/utxo/slp.rs +++ b/mm2src/coins/utxo/slp.rs @@ -54,7 +54,7 @@ use script::{Builder as ScriptBuilder, Opcode, Script, TransactionInputSigner}; use serde_json::Value as Json; use serialization::{deserialize, serialize, Deserializable, Error as SerError, Reader}; use serialization_derive::Deserializable; -use std::convert::TryInto; +use std::convert::{TryFrom, TryInto}; use std::num::TryFromIntError; use std::sync::atomic::{AtomicU64, Ordering as AtomicOrdering}; use std::sync::Arc; @@ -922,7 +922,7 @@ impl Deserializable for SlpTransaction { let additional_token_quantity = u64::from_be_bytes(bytes.try_into().expect("length is 8 bytes")); Ok(SlpTransaction::Mint { - token_id: H256::from(maybe_id.as_slice()), + token_id: H256::from_slice(maybe_id.as_slice()).map_err(|_| SerError::MalformedData)?, mint_baton_vout, additional_token_quantity, }) @@ -936,7 +936,7 @@ impl Deserializable for SlpTransaction { ))); } - let token_id = H256::from(maybe_id.as_slice()); + let token_id = H256::from_slice(maybe_id.as_slice()).map_err(|_| SerError::MalformedData)?; let mut amounts = Vec::with_capacity(1); while !reader.is_finished() { let bytes: Vec = reader.read_list()?; @@ -1130,7 +1130,8 @@ impl MarketCoinOps for SlpToken { let message_hash = self .sign_message_hash(message) .ok_or(VerificationError::PrefixNotFound)?; - let signature = CompactSignature::from(STANDARD.decode(signature)?); + let signature = CompactSignature::try_from(STANDARD.decode(signature)?) + .map_to_mm(|err| VerificationError::SignatureDecodingError(err.to_string()))?; let pubkey = Public::recover_compact(&H256::from(message_hash), &signature)?; let address_from_pubkey = self.platform_coin.address_from_pubkey(&pubkey); let slp_address = self @@ -1415,7 +1416,7 @@ impl SwapOps for SlpToken { secret_hash: &[u8], spend_tx: &[u8], _watcher_reward: bool, - ) -> Result, String> { + ) -> Result<[u8; 32], String> { utxo_common::extract_secret(secret_hash, spend_tx) } @@ -1439,7 +1440,7 @@ impl SwapOps for SlpToken { utxo_common::derive_htlc_key_pair(self.platform_coin.as_ref(), swap_unique_data) } - fn derive_htlc_pubkey(&self, swap_unique_data: &[u8]) -> Vec { + fn derive_htlc_pubkey(&self, swap_unique_data: &[u8]) -> [u8; 33] { utxo_common::derive_htlc_pubkey(self, swap_unique_data) } @@ -1644,17 +1645,19 @@ impl MmCoin for SlpToken { sat_from_big_decimal(&req.amount, coin.decimals())? }; - if address.hash.len() != 20 { - return MmError::err(WithdrawError::InvalidAddress(format!( - "Expected 20 address hash len, not {}", - address.hash.len() - ))); - } + let address_hash = address.hash.clone(); + let address_hash = { + let address_hash_len = address_hash.len(); + let address_hash: [u8; 20] = address_hash.try_into().map_err(|_| { + WithdrawError::InvalidAddress(format!("Expected 20 address hash len, not {}", address_hash_len)) + })?; + address_hash.into() + }; // TODO clarify with community whether we should support withdrawal to SLP P2SH addresses let script_pubkey = match address.address_type { CashAddrType::P2PKH => { - ScriptBuilder::build_p2pkh(&AddressHashEnum::AddressHash(address.hash.as_slice().into())).to_bytes() + ScriptBuilder::build_p2pkh(&AddressHashEnum::AddressHash(address_hash)).to_bytes() }, CashAddrType::P2SH => { return MmError::err(WithdrawError::InvalidAddress( diff --git a/mm2src/coins/utxo/utxo_common.rs b/mm2src/coins/utxo/utxo_common.rs index 70c8522b58..2722dfcdeb 100644 --- a/mm2src/coins/utxo/utxo_common.rs +++ b/mm2src/coins/utxo/utxo_common.rs @@ -60,6 +60,7 @@ use serialization::{deserialize, serialize, serialize_with_flags, CoinVariant, C SERIALIZE_TRANSACTION_WITNESS}; use std::cmp::Ordering; use std::collections::hash_map::{Entry, HashMap}; +use std::convert::TryFrom; use std::str::FromStr; use std::sync::atomic::Ordering as AtomicOrdering; use utxo_signer::with_key_pair::{calc_and_sign_sighash, p2sh_spend, signature_hash_to_sign, SIGHASH_ALL, @@ -2042,19 +2043,26 @@ pub fn watcher_validate_taker_fee( output_index: usize, ) -> ValidatePaymentFut<()> { let coin = coin.clone(); - let sender_pubkey = input.sender_pubkey; - let taker_fee_hash = input.taker_fee_hash; + let sender_pubkey = input.sender_pubkey.clone(); let min_block_number = input.min_block_number; let lock_duration = input.lock_duration; let fee_addr = input.fee_addr.to_vec(); let fut = async move { + let taker_fee_hash_len = input.taker_fee_hash.len(); + let taker_fee_hash_array: [u8; 32] = input.taker_fee_hash.try_into().map_to_mm(|_| { + ValidatePaymentError::InternalError(format!( + "Invalid taker_fee_hash length: expected 32 bytes, got {} bytes", + taker_fee_hash_len + )) + })?; + let taker_fee_hash = taker_fee_hash_array.into(); let mut attempts = 0; loop { let tx_from_rpc = match coin .as_ref() .rpc_client - .get_verbose_transaction(&H256Json::from(taker_fee_hash.as_slice())) + .get_verbose_transaction(&taker_fee_hash) .compat() .await { @@ -2582,11 +2590,12 @@ pub async fn get_taker_watcher_reward Result, String> { +pub fn extract_secret(secret_hash: &[u8], spend_tx: &[u8]) -> Result<[u8; 32], String> { let spend_tx: UtxoTx = try_s!(deserialize(spend_tx).map_err(|e| ERRL!("{:?}", e))); let expected_secret_hash = if secret_hash.len() == 32 { ripemd160(secret_hash) } else { + let secret_hash: [u8; 20] = try_s!(secret_hash.try_into()); H160::from(secret_hash) }; for input in spend_tx.inputs.into_iter() { @@ -2596,7 +2605,7 @@ pub fn extract_secret(secret_hash: &[u8], spend_tx: &[u8]) -> Result, St if let Some(secret) = instruction.data { let actual_secret_hash = dhash160(secret); if actual_secret_hash == expected_secret_hash { - return Ok(secret.to_vec()); + return Ok(try_s!(secret.try_into())); } } } @@ -2644,7 +2653,8 @@ pub fn verify_message( address: &str, ) -> VerificationResult { let message_hash = sign_message_hash(coin.as_ref(), message).ok_or(VerificationError::PrefixNotFound)?; - let signature = CompactSignature::from(STANDARD.decode(signature_base64)?); + let signature = CompactSignature::try_from(STANDARD.decode(signature_base64)?) + .map_to_mm(|err| VerificationError::SignatureDecodingError(err.to_string()))?; let recovered_pubkey = Public::recover_compact(&H256::from(message_hash), &signature)?; let received_address = checked_address_from_str(coin, address)?; Ok(AddressHashEnum::from(recovered_pubkey.address_hash()) == *received_address.hash()) @@ -2788,10 +2798,20 @@ async fn sign_raw_utxo_tx + UtxoTxGenerationOps>( let prev_hash = hex::decode(prev_utxo.tx_hash.as_bytes()) .map_to_mm(|e| RawTransactionError::DecodeError(e.to_string()))?; + let prev_hash = { + let prev_hash_len = prev_hash.len(); + let arr: [u8; 32] = prev_hash.try_into().map_to_mm(|_| { + RawTransactionError::DecodeError(format!( + "Invalid prev_out_hash length: expected 32 bytes, got {}", + prev_hash_len + )) + })?; + arr.into() + }; unspents.push(UnspentInfo { outpoint: OutPoint { - hash: prev_hash.as_slice().into(), + hash: prev_hash, index: prev_utxo.index, }, value: sat_from_big_decimal(&prev_utxo.amount, coin.as_ref().decimals) @@ -2980,9 +3000,14 @@ pub async fn get_raw_transaction(coin: &UtxoCoinFields, req: RawTransactionReque } pub async fn get_tx_hex_by_hash(coin: &UtxoCoinFields, tx_hash: Vec) -> RawTransactionResult { + let len = tx_hash.len(); + let hash: [u8; 32] = tx_hash.try_into().map_to_mm(|_| { + RawTransactionError::InvalidHashError(format!("Invalid hash length: expected 32, got {}", len)) + })?; + let hex = coin .rpc_client - .get_transaction_bytes(&H256Json::from(tx_hash.as_slice())) + .get_transaction_bytes(&H256Json::from(hash)) .compat() .await .map_err(|e| RawTransactionError::Transport(e.to_string()))?; @@ -3332,7 +3357,7 @@ where Entry::Vacant(e) => { mm_counter!(ctx.metrics, "tx.history.request.count", 1, "coin" => coin.as_ref().conf.ticker.clone(), "method" => "tx_detail_by_hash"); - match coin.tx_details_by_hash(&txid.0, &mut input_transactions).await { + match coin.tx_details_by_hash(&txid, &mut input_transactions).await { Ok(mut tx_details) => { mm_counter!(ctx.metrics, "tx.history.response.count", 1, "coin" => coin.as_ref().conf.ticker.clone(), "method" => "tx_detail_by_hash"); @@ -3366,7 +3391,7 @@ where if e.get().should_update_timestamp() || e.get().firo_negative_fee() { mm_counter!(ctx.metrics, "tx.history.request.count", 1, "coin" => coin.as_ref().conf.ticker.clone(), "method" => "tx_detail_by_hash"); - match coin.tx_details_by_hash(&txid.0, &mut input_transactions).await { + match coin.tx_details_by_hash(&txid, &mut input_transactions).await { Ok(tx_details) => { mm_counter!(ctx.metrics, "tx.history.response.count", 1, "coin" => coin.as_ref().conf.ticker.clone(), "method" => "tx_detail_by_hash"); // replace with new tx details in case we need to update any data @@ -3530,17 +3555,16 @@ where pub async fn tx_details_by_hash( coin: &T, - hash: &[u8], + hash: &H256Json, input_transactions: &mut HistoryUtxoTxMap, ) -> Result { let ticker = &coin.as_ref().conf.ticker; - let hash = H256Json::from(hash); - let verbose_tx = try_s!(coin.as_ref().rpc_client.get_verbose_transaction(&hash).compat().await); + let verbose_tx = try_s!(coin.as_ref().rpc_client.get_verbose_transaction(hash).compat().await); let mut tx: UtxoTx = try_s!(deserialize(verbose_tx.hex.as_slice()).map_err(|e| ERRL!("{:?}", e))); tx.tx_hash_algo = coin.as_ref().tx_hash_algo; let my_address = try_s!(coin.as_ref().derivation_method.single_addr_or_err().await); - input_transactions.insert(hash, HistoryUtxoTx { + input_transactions.insert(*hash, HistoryUtxoTx { tx: tx.clone(), height: verbose_tx.height, }); @@ -4748,8 +4772,12 @@ pub fn derive_htlc_key_pair(coin: &UtxoCoinFields, _swap_unique_data: &[u8]) -> } #[inline] -pub fn derive_htlc_pubkey(coin: &dyn SwapOps, swap_unique_data: &[u8]) -> Vec { - coin.derive_htlc_key_pair(swap_unique_data).public_slice().to_vec() +pub fn derive_htlc_pubkey(coin: &dyn SwapOps, swap_unique_data: &[u8]) -> [u8; 33] { + coin.derive_htlc_key_pair(swap_unique_data) + .public_slice() + .to_vec() + .try_into() + .expect("valid pubkey length") } pub fn validate_other_pubkey(raw_pubkey: &[u8]) -> MmResult<(), ValidateOtherPubKeyErr> { diff --git a/mm2src/coins/utxo/utxo_standard.rs b/mm2src/coins/utxo/utxo_standard.rs index f5a02f5095..fba3612f42 100644 --- a/mm2src/coins/utxo/utxo_standard.rs +++ b/mm2src/coins/utxo/utxo_standard.rs @@ -281,7 +281,7 @@ impl UtxoCommonOps for UtxoStandardCoin { impl UtxoStandardOps for UtxoStandardCoin { async fn tx_details_by_hash( &self, - hash: &[u8], + hash: &H256Json, input_transactions: &mut HistoryUtxoTxMap, ) -> Result { utxo_common::tx_details_by_hash(self, hash, input_transactions).await @@ -435,7 +435,7 @@ impl SwapOps for UtxoStandardCoin { secret_hash: &[u8], spend_tx: &[u8], _watcher_reward: bool, - ) -> Result, String> { + ) -> Result<[u8; 32], String> { utxo_common::extract_secret(secret_hash, spend_tx) } @@ -466,7 +466,7 @@ impl SwapOps for UtxoStandardCoin { utxo_common::derive_htlc_key_pair(self.as_ref(), swap_unique_data) } - fn derive_htlc_pubkey(&self, swap_unique_data: &[u8]) -> Vec { + fn derive_htlc_pubkey(&self, swap_unique_data: &[u8]) -> [u8; 33] { utxo_common::derive_htlc_pubkey(self, swap_unique_data) } diff --git a/mm2src/coins/utxo/utxo_tests.rs b/mm2src/coins/utxo/utxo_tests.rs index bcd7cc991f..3b162c91c4 100644 --- a/mm2src/coins/utxo/utxo_tests.rs +++ b/mm2src/coins/utxo/utxo_tests.rs @@ -13,9 +13,9 @@ use crate::rpc_command::init_scan_for_new_addresses::{InitScanAddressesRpcOps, S use crate::utxo::qtum::{qtum_coin_with_priv_key, QtumCoin, QtumDelegationOps, QtumDelegationRequest}; #[cfg(not(target_arch = "wasm32"))] use crate::utxo::rpc_clients::{BlockHashOrHeight, NativeUnspent}; -use crate::utxo::rpc_clients::{ElectrumBalance, ElectrumClient, ElectrumClientImpl, ElectrumClientSettings, - GetAddressInfoRes, ListSinceBlockRes, NativeClient, NativeClientImpl, NetworkInfo, - UtxoRpcClientOps, ValidateAddressRes, VerboseBlock}; +use crate::utxo::rpc_clients::{ElectrumBalance, ElectrumBlockHeader, ElectrumClient, ElectrumClientImpl, + ElectrumClientSettings, GetAddressInfoRes, ListSinceBlockRes, NativeClient, + NativeClientImpl, NetworkInfo, UtxoRpcClientOps, ValidateAddressRes, VerboseBlock}; use crate::utxo::spv::SimplePaymentVerification; #[cfg(not(target_arch = "wasm32"))] use crate::utxo::utxo_block_header_storage::{BlockHeaderStorage, SqliteBlockHeadersStorage}; @@ -40,6 +40,7 @@ use crypto::{privkey::key_pair_from_seed, Bip44Chain, HDPathToAccount, RpcDeriva use db_common::sqlite::rusqlite::Connection; use futures::channel::mpsc::channel; use futures::future::{join_all, Either, FutureExt, TryFutureExt}; +use hex::FromHex; use keys::prefixes::*; use mm2_core::mm_ctx::MmCtxBuilder; use mm2_number::bigdecimal::{BigDecimal, Signed}; @@ -48,7 +49,7 @@ use mm2_test_helpers::for_tests::{electrum_servers_rpc, mm_ctx_with_custom_db, D MARTY_ELECTRUM_ADDRS, T_BCH_ELECTRUMS}; use mocktopus::mocking::*; use rpc::v1::types::H256 as H256Json; -use serialization::{deserialize, CoinVariant}; +use serialization::{deserialize, CoinVariant, CompactInteger, Reader}; use spv_validation::conf::{BlockHeaderValidationParams, SPVBlockHeader}; use spv_validation::storage::BlockHeaderStorageOps; use spv_validation::work::DifficultyAlgorithm; @@ -103,7 +104,7 @@ fn utxo_coin_for_test( /// Returns `TransactionDetails` of the given `tx_hash` via [`UtxoStandardOps::tx_details_by_hash`]. #[track_caller] fn get_tx_details_by_hash(coin: &Coin, tx_hash: &str) -> TransactionDetails { - let hash = hex::decode(tx_hash).unwrap(); + let hash = <[u8; 32]>::from_hex(tx_hash).unwrap().into(); let mut input_transactions = HistoryUtxoTxMap::new(); block_on(UtxoStandardOps::tx_details_by_hash( @@ -122,7 +123,7 @@ where let my_addresses = block_on(coin.my_addresses()).unwrap(); let (_ctx, storage) = init_storage_for(coin); let params = UtxoTxDetailsParams { - hash: &hex::decode(tx_hash).unwrap().as_slice().into(), + hash: &<[u8; 32]>::from_hex(tx_hash).unwrap().into(), block_height_and_time: Some(BlockHeightAndTime { height, timestamp }), storage: &storage, my_addresses: &my_addresses, @@ -151,7 +152,8 @@ fn test_extract_secret() { let coin = utxo_coin_for_test(client.into(), None, false); let tx_hex = hex::decode("0400008085202f890125236f423b7f585e6a86d8a6c45c6805bbd5823851a57a00f6dcd3a41dc7487500000000d8483045022100ce7246314170b7c84df41a9d987dad5b572cfca5c27ee738d2682ce147c460a402206fa477fc27bec62600b13ea8a3f81fbad1fa9adad28bc1fa5c212a12ecdccd7f01205c62072b57b6473aeee6d35270c8b56d86975e6d6d4245b25425d771239fae32004c6b630476ac3765b1752103242d9cb2168968d785f6914c494c303ff1c27ba0ad882dbc3c15cfa773ea953cac6782012088a914f95ae6f5fb6a4c4e69b00b4c1dbc0698746c0f0288210210e0f210673a2024d4021270bb711664a637bb542317ed9be5ad592475320c0cac68ffffffff0128230000000000001976a9142c445a7af3da3feb2ba7d5f2a32002c772acc1e188ac76ac3765000000000000000000000000000000").unwrap(); - let expected_secret = hex::decode("5c62072b57b6473aeee6d35270c8b56d86975e6d6d4245b25425d771239fae32").unwrap(); + let expected_secret = + <[u8; 32]>::from_hex("5c62072b57b6473aeee6d35270c8b56d86975e6d6d4245b25425d771239fae32").unwrap(); let secret_hash = &*dhash160(&expected_secret); let secret = block_on(coin.extract_secret(secret_hash, &tx_hex, false)).unwrap(); assert_eq!(secret, expected_secret); @@ -909,7 +911,7 @@ fn test_withdraw_kmd_rewards_impl( UtxoStandardCoin::get_current_mtp .mock_safe(move |_fields| MockResult::Return(Box::pin(futures::future::ok(current_mtp)))); NativeClient::get_verbose_transaction.mock_safe(move |_coin, txid| { - let expected: H256Json = hex::decode(tx_hash).unwrap().as_slice().into(); + let expected: H256Json = <[u8; 32]>::from_hex(tx_hash).unwrap().into(); assert_eq!(*txid, expected); MockResult::Return(Box::new(futures01::future::ok(verbose.clone()))) }); @@ -1933,7 +1935,7 @@ fn test_get_mature_unspent_ordered_map_from_cache_impl( expected_confs: u32, ) { const TX_HASH: &str = "b43f9ed47f7b97d4766b6f1614136fa0c55b9a52c97342428333521fa13ad714"; - let tx_hash: H256Json = hex::decode(TX_HASH).unwrap().as_slice().into(); + let tx_hash: H256Json = <[u8; 32]>::from_hex(TX_HASH).unwrap().into(); let client = electrum_client_for_test(DOC_ELECTRUM_ADDRS); let mut verbose = block_on_f01(client.get_verbose_transaction(&tx_hash)).unwrap(); verbose.confirmations = cached_confs; @@ -2522,14 +2524,13 @@ fn test_find_output_spend_skips_conflicting_transactions() { const LIST_SINCE_BLOCK_JSON: &str = r#"{"transactions":[{"involvesWatchonly":true,"account":"","address":"RAsbVN52LC2hEp3UWWSLbV8pJ8CneKjW9F","category":"send","amount":-0.01537462,"vout":0,"fee":-0.00001000,"rawconfirmations":-1,"confirmations":-1,"txid":"220c337006b2581c3da734ef9f1106601e8538ebab823d0dd6719a4d4580fd04","walletconflicts":["a2144bee4eac4b41ab1aed2dd8f854785b3ddebd617d48696dd84e62d129544b"],"time":1607831631,"timereceived":1607831631,"vjoinsplit":[],"size":320},{"involvesWatchonly":true,"account":"","address":"RAsbVN52LC2hEp3UWWSLbV8pJ8CneKjW9F","category":"send","amount":-0.01537462,"vout":0,"fee":-0.00001000,"rawconfirmations":-1,"confirmations":-1,"txid":"6fb83afb1bf309515fa429814bf07552eea951656fdee913f3aa687d513cd720","walletconflicts":["4aad6471f59e5912349cd7679bc029bfbd5da54d34c235d20500249f98f549e4"],"time":1607831556,"timereceived":1607831556,"vjoinsplit":[],"size":320},{"account":"","address":"RT9MpMyucqXiX8bZLimXBnrrn2ofmdGNKd","category":"receive","amount":0.54623851,"vout":2,"rawconfirmations":1617,"confirmations":1617,"blockhash":"000000000c33a387d73180220a5a8f2fe6081bad9bdfc0dba5a9985abcee8294","blockindex":7,"blocktime":1607957613,"expiryheight":0,"txid":"45e4900a2b330800a356a74ce2a97370596ad3a25e689e3ed5c36e421d12bbf7","walletconflicts":[],"time":1607957175,"timereceived":1607957175,"vjoinsplit":[],"size":567},{"involvesWatchonly":true,"account":"","address":"RT9MpMyucqXiX8bZLimXBnrrn2ofmdGNKd","category":"send","amount":-0.00797200,"vout":0,"fee":-0.00001000,"rawconfirmations":-1,"confirmations":-1,"txid":"bfc99c06d1a060cdbeba05620dc1c6fdb7351eb4c04b7aae578688ca6aeaeafd","walletconflicts":[],"time":1607957792,"timereceived":1607957792,"vjoinsplit":[],"size":286}],"lastblock":"06082d363f78174fd13b126994210d3c3ad9d073ee3983ad59fe8b76e6e3e071"}"#; // in the json above this transaction is only one not conflicting const NON_CONFLICTING_TXID: &str = "45e4900a2b330800a356a74ce2a97370596ad3a25e689e3ed5c36e421d12bbf7"; - let expected_txid: H256Json = hex::decode(NON_CONFLICTING_TXID).unwrap().as_slice().into(); - + let expected_txid: H256Json = <[u8; 32]>::from_hex(NON_CONFLICTING_TXID).unwrap().into(); NativeClientImpl::get_block_hash.mock_safe(|_, _| { // no matter what we return here - let blockhash: H256Json = hex::decode("000000000c33a387d73180220a5a8f2fe6081bad9bdfc0dba5a9985abcee8294") - .unwrap() - .as_slice() - .into(); + let blockhash: H256Json = + <[u8; 32]>::from_hex("000000000c33a387d73180220a5a8f2fe6081bad9bdfc0dba5a9985abcee8294") + .unwrap() + .into(); MockResult::Return(Box::new(futures01::future::ok(blockhash))) }); @@ -4926,3 +4927,57 @@ fn test_block_header_utxo_loop_with_reorg() { panic!("Loop shouldn't stop") }; } + +#[test] +fn test_electrum_v14_block_hash() { + let client = electrum_client_for_test(DOC_ELECTRUM_ADDRS); + + // First verify BlockHeader hash implementation works correctly with a known reference block + let headers = + block_on_f01(client.blockchain_block_headers(841548, NonZeroU64::new(1).expect("Failed to create NonZeroU64"))) + .expect("Failed to fetch block headers"); + + // Deserialize the reference block header + let serialized = serialize(&CompactInteger::from(headers.count)) + .take() + .into_iter() + .chain(headers.hex.0) + .collect::>(); + let headers = Reader::new_with_coin_variant(&serialized, CoinVariant::RICK) + .read_list::() + .expect("Failed to deserialize headers"); + + // Confirm BlockHeader hash matches the known hash value + assert_eq!( + headers[0].hash().reversed().to_string(), + "0f0a6ce253b0536000636f85491db8030659064de8c27423b46ceef824d4ad28" + ); + + // Now get the latest block via V14 subscription to test its hash implementation + let header = + block_on_f01(client.blockchain_headers_subscribe()).expect("Failed to subscribe to blockchain headers"); + + // Extract hash and height from V14 header + let (hash, height) = match header { + ElectrumBlockHeader::V14(header) => (header.hash(), header.height), + _ => panic!("Expected ElectrumBlockHeader::V14"), + }; + + // Get the same block data to create a BlockHeader for comparison + let headers = + block_on_f01(client.blockchain_block_headers(height, NonZeroU64::new(1).expect("Failed to create NonZeroU64"))) + .expect("Failed to fetch block headers"); + + // Create BlockHeader from the same block (using the implementation we just verified) + let serialized = serialize(&CompactInteger::from(headers.count)) + .take() + .into_iter() + .chain(headers.hex.0) + .collect::>(); + let headers = Reader::new_with_coin_variant(&serialized, CoinVariant::RICK) + .read_list::() + .expect("Failed to deserialize headers"); + + // Verify V14 header produces the same hash as our verified BlockHeader implementation + assert_eq!(hash, headers[0].hash().into()); +} diff --git a/mm2src/coins/utxo/utxo_wasm_tests.rs b/mm2src/coins/utxo/utxo_wasm_tests.rs index bd059c8627..d0cbb4cbfc 100644 --- a/mm2src/coins/utxo/utxo_wasm_tests.rs +++ b/mm2src/coins/utxo/utxo_wasm_tests.rs @@ -4,6 +4,7 @@ use super::utxo_standard::UtxoStandardCoin; use super::*; use crate::utxo::utxo_common_tests; use crate::{IguanaPrivKey, PrivKeyBuildPolicy}; +use hex::FromHex; use mm2_core::mm_ctx::MmCtxBuilder; use mm2_test_helpers::for_tests::DOC_ELECTRUM_ADDRS; use serialization::deserialize; @@ -51,9 +52,8 @@ pub async fn electrum_client_for_test(servers: &[&str]) -> ElectrumClient { async fn test_electrum_rpc_client() { let client = electrum_client_for_test(DOC_ELECTRUM_ADDRS).await; - let tx_hash: H256Json = hex::decode("a3ebedbe20f82e43708f276152cf7dfb03a6050921c8f266e48c00ab66e891fb") + let tx_hash: H256Json = <[u8; 32]>::from_hex("a3ebedbe20f82e43708f276152cf7dfb03a6050921c8f266e48c00ab66e891fb") .unwrap() - .as_slice() .into(); let verbose_tx = client .get_verbose_transaction(&tx_hash) diff --git a/mm2src/coins/z_coin.rs b/mm2src/coins/z_coin.rs index b8c7c8c944..7ccebadf06 100644 --- a/mm2src/coins/z_coin.rs +++ b/mm2src/coins/z_coin.rs @@ -532,7 +532,7 @@ impl ZCoin { } let mut transparent_input_amount = Amount::zero(); - let hash = H256Json::from(tx_item.tx_hash.as_slice()); + let hash = H256Json::from(tx_item.tx_hash); let z_tx = transactions.remove(&hash).or_mm_err(|| NoInfoAboutTx(hash))?; for input in z_tx.vin.iter() { let mut hash = H256Json::from(*input.prevout.hash()); @@ -622,7 +622,7 @@ impl ZCoin { let hashes_for_verbose = req_result .transactions .iter() - .map(|item| H256Json::from(item.tx_hash.as_slice())) + .map(|item| H256Json::from(item.tx_hash)) .collect(); let mut transactions = self.z_transactions_from_cache_or_rpc(hashes_for_verbose).await?; @@ -1513,7 +1513,7 @@ impl SwapOps for ZCoin { secret_hash: &[u8], spend_tx: &[u8], _watcher_reward: bool, - ) -> Result, String> { + ) -> Result<[u8; 32], String> { utxo_common::extract_secret(secret_hash, spend_tx) } @@ -1542,12 +1542,16 @@ impl SwapOps for ZCoin { let signature = self.secp_keypair().private().sign(&message).expect("valid privkey"); let key = secp_privkey_from_hash(dhash256(&signature)); - key_pair_from_secret(key.as_slice()).expect("valid privkey") + key_pair_from_secret(&key.take()).expect("valid privkey") } #[inline] - fn derive_htlc_pubkey(&self, swap_unique_data: &[u8]) -> Vec { - self.derive_htlc_key_pair(swap_unique_data).public_slice().to_vec() + fn derive_htlc_pubkey(&self, swap_unique_data: &[u8]) -> [u8; 33] { + self.derive_htlc_key_pair(swap_unique_data) + .public_slice() + .to_vec() + .try_into() + .expect("valid pubkey length") } #[inline] diff --git a/mm2src/coins/z_coin/z_tx_history.rs b/mm2src/coins/z_coin/z_tx_history.rs index 57eb2fdb4c..ea9543f941 100644 --- a/mm2src/coins/z_coin/z_tx_history.rs +++ b/mm2src/coins/z_coin/z_tx_history.rs @@ -1,6 +1,8 @@ use crate::z_coin::{ZCoin, ZTxHistoryError}; use common::PagingOptionsEnum; use mm2_err_handle::prelude::MmError; +use primitives::hash::H256; +use std::convert::TryInto; cfg_wasm32!( use crate::z_coin::storage::wasm::tables::{WalletDbBlocksTable, WalletDbReceivedNotesTable, WalletDbTransactionsTable}; @@ -11,18 +13,19 @@ cfg_wasm32!( cfg_native!( use crate::z_coin::BLOCKS_TABLE; + use common::async_blocking; use db_common::sqlite::sql_builder::{name, SqlBuilder, SqlName}; use db_common::sqlite::rusqlite::Error as SqliteError; use db_common::sqlite::rusqlite::Row; use db_common::sqlite::offset_by_id; - use common::async_blocking; + use db_common::sqlite::rusqlite::types::Type; ); #[cfg(not(target_arch = "wasm32"))] const TRANSACTIONS_TABLE: &str = "transactions"; pub(crate) struct ZCoinTxHistoryItem { - pub(crate) tx_hash: Vec, + pub(crate) tx_hash: H256, pub(crate) internal_id: i64, pub(crate) height: i64, pub(crate) timestamp: i64, @@ -118,11 +121,14 @@ pub(crate) async fn fetch_tx_history_from_db( } } - let mut tx_hash = tx.txid; + let mut tx_hash: [u8; 32] = tx + .txid + .try_into() + .map_err(|_| ZTxHistoryError::IndexedDbError("Expected 32 bytes for transaction hash".to_string()))?; tx_hash.reverse(); tx_details.push(ZCoinTxHistoryItem { - tx_hash, + tx_hash: H256::from(tx_hash), internal_id: internal_id as i64, height: *height as i64, timestamp: *time as i64, @@ -142,10 +148,21 @@ pub(crate) async fn fetch_tx_history_from_db( #[cfg(not(target_arch = "wasm32"))] impl ZCoinTxHistoryItem { fn try_from_sql_row(row: &Row<'_>) -> Result { - let mut tx_hash: Vec = row.get(0)?; + let tx_bytes: Vec = row.get(0)?; + let mut tx_hash: [u8; 32] = tx_bytes.try_into().map_err(|_| { + SqliteError::FromSqlConversionFailure( + 0, + Type::Blob, + Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Expected 32 bytes for transaction hash", + )), + ) + })?; tx_hash.reverse(); + Ok(ZCoinTxHistoryItem { - tx_hash, + tx_hash: H256::from(tx_hash), internal_id: row.get(1)?, height: row.get(2)?, timestamp: row.get(3)?, diff --git a/mm2src/crypto/src/hw_ctx.rs b/mm2src/crypto/src/hw_ctx.rs index 1ac7c9877f..8d2c84a8f9 100644 --- a/mm2src/crypto/src/hw_ctx.rs +++ b/mm2src/crypto/src/hw_ctx.rs @@ -185,4 +185,4 @@ impl HardwareWalletCtx { fn h160_from_h264(h264: &H264) -> H160 { dhash160(h264.as_slice()) } /// Converts `H264` into a serializable/deserializable Hardware wallet pubkey. -fn hw_pubkey_from_h264(h264: &H264) -> HwPubkey { HwPubkey::from(h160_from_h264(h264).as_slice()) } +fn hw_pubkey_from_h264(h264: &H264) -> HwPubkey { HwPubkey::from(h160_from_h264(h264).take()) } diff --git a/mm2src/crypto/src/privkey.rs b/mm2src/crypto/src/privkey.rs index d86a0e930f..4d296d72a8 100644 --- a/mm2src/crypto/src/privkey.rs +++ b/mm2src/crypto/src/privkey.rs @@ -104,11 +104,7 @@ pub fn key_pair_from_seed(seed: &str) -> PrivKeyResult { Ok(pair) } -pub fn key_pair_from_secret(secret: &[u8]) -> PrivKeyResult { - if secret.len() != 32 { - return MmError::err(PrivKeyError::InvalidPrivKey(KeysError::InvalidPrivate.to_string())); - } - +pub fn key_pair_from_secret(secret: &[u8; 32]) -> PrivKeyResult { let private = Private { prefix: 0, secret: secret.into(), diff --git a/mm2src/mm2_bitcoin/crypto/src/lib.rs b/mm2src/mm2_bitcoin/crypto/src/lib.rs index 393712ccb1..fc034858f2 100644 --- a/mm2src/mm2_bitcoin/crypto/src/lib.rs +++ b/mm2src/mm2_bitcoin/crypto/src/lib.rs @@ -34,15 +34,17 @@ pub enum ChecksumType { pub fn ripemd160(input: &[u8]) -> H160 { let mut hasher = Ripemd160::new(); hasher.update(input); - (*hasher.finalize()).into() + let array: [u8; 20] = hasher.finalize().into(); + array.into() } /// SHA-1 #[inline] pub fn sha1(input: &[u8]) -> H160 { - let mut hasher = Sha1::default(); + let mut hasher = Sha1::new(); hasher.update(input); - (*hasher.finalize()).into() + let array: [u8; 20] = hasher.finalize().into(); + array.into() } /// SHA-256 @@ -50,7 +52,8 @@ pub fn sha1(input: &[u8]) -> H160 { pub fn sha256(input: &[u8]) -> H256 { let mut hasher = Sha256::new(); hasher.update(input); - (*hasher.finalize()).into() + let array: [u8; 32] = hasher.finalize().into(); + array.into() } /// Groestl-512 @@ -58,7 +61,8 @@ pub fn sha256(input: &[u8]) -> H256 { pub fn groestl512(input: &[u8]) -> H512 { let mut hasher = Groestl512::new(); hasher.update(input); - (*hasher.finalize()).into() + let array: [u8; 64] = hasher.finalize().into(); + array.into() } /// Keccak-256 @@ -66,7 +70,8 @@ pub fn groestl512(input: &[u8]) -> H512 { pub fn keccak256(input: &[u8]) -> H256 { let mut hasher = Keccak256::new(); hasher.update(input); - (*hasher.finalize()).into() + let array: [u8; 32] = hasher.finalize().into(); + array.into() } /// Double Keccak-256 diff --git a/mm2src/mm2_bitcoin/keys/src/signature.rs b/mm2src/mm2_bitcoin/keys/src/signature.rs index 46edce268f..90122ed630 100644 --- a/mm2src/mm2_bitcoin/keys/src/signature.rs +++ b/mm2src/mm2_bitcoin/keys/src/signature.rs @@ -4,7 +4,8 @@ use hash::H520; use hex::{FromHex, ToHex}; -use std::{fmt, ops, str}; +use std::convert::TryInto; +use std::{array::TryFromSliceError, convert::TryFrom, fmt, ops, str}; use Error; #[derive(PartialEq, Clone)] @@ -91,6 +92,10 @@ impl From for CompactSignature { fn from(h: H520) -> Self { CompactSignature(h) } } -impl From> for CompactSignature { - fn from(v: Vec) -> Self { CompactSignature(H520::from(&v[..])) } +impl TryFrom> for CompactSignature { + type Error = TryFromSliceError; + fn try_from(value: Vec) -> Result { + let bytes: &[u8; 65] = &value.as_slice().try_into()?; + Ok(CompactSignature(H520::from(bytes))) + } } diff --git a/mm2src/mm2_bitcoin/primitives/src/hash.rs b/mm2src/mm2_bitcoin/primitives/src/hash.rs index 46f86df4d4..7bf56f5700 100644 --- a/mm2src/mm2_bitcoin/primitives/src/hash.rs +++ b/mm2src/mm2_bitcoin/primitives/src/hash.rs @@ -2,6 +2,7 @@ use bitcoin_hashes::{sha256d, Hash as ExtHash}; use hex::{FromHex, FromHexError, ToHex}; +use std::convert::TryInto; use std::hash::{Hash, Hasher}; use std::{cmp, fmt, ops, str}; @@ -39,10 +40,10 @@ macro_rules! impl_hash { fn from(h: $name) -> Self { h.0 } } - impl<'a> From<&'a [u8]> for $name { - fn from(slc: &[u8]) -> Self { + impl<'a> From<&'a [u8; $size]> for $name { + fn from(slc: &[u8; $size]) -> Self { let mut inner = [0u8; $size]; - inner[..].clone_from_slice(&slc[0..$size]); + inner.copy_from_slice(slc); $name(inner) } } @@ -61,17 +62,9 @@ macro_rules! impl_hash { impl str::FromStr for $name { type Err = FromHexError; - fn from_str(s: &str) -> Result { let vec: Vec = s.from_hex()?; - match vec.len() { - $size => { - let mut result = [0u8; $size]; - result.copy_from_slice(&vec); - Ok($name(result)) - }, - _ => Err(FromHexError::InvalidHexLength), - } + Self::from_slice(&vec).map_err(|_| FromHexError::InvalidHexLength) } } @@ -143,6 +136,14 @@ macro_rules! impl_hash { pub fn size() -> usize { $size } pub fn is_zero(&self) -> bool { self.0.iter().all(|b| *b == 0) } + + /// Preferred method for constructing from a slice - checks length and returns Result + pub fn from_slice(slc: &[u8]) -> Result { + let bytes: [u8; $size] = slc + .try_into() + .map_err(|_| "Slice length must be exactly 40 bytes")?; + Ok(bytes.into()) + } } }; } diff --git a/mm2src/mm2_bitcoin/script/src/script.rs b/mm2src/mm2_bitcoin/script/src/script.rs index 9244f043f7..758dd02736 100644 --- a/mm2src/mm2_bitcoin/script/src/script.rs +++ b/mm2src/mm2_bitcoin/script/src/script.rs @@ -2,6 +2,7 @@ use bytes::Bytes; use keys::{self, AddressHashEnum, Public}; +use std::convert::TryFrom; use std::{fmt, ops}; use {Error, Opcode}; @@ -350,7 +351,7 @@ impl Script { ScriptType::WitnessKey } else if self.is_pay_to_witness_script_hash() { ScriptType::WitnessScript - // TODO add Call + // TODO add Call } else { ScriptType::NonStandard } @@ -426,10 +427,14 @@ impl Script { }) }, ScriptType::PubKeyHash => Ok(vec![ScriptAddress::new_p2pkh(AddressHashEnum::AddressHash( - self.data[3..23].into(), + <[u8; 20]>::try_from(self.data.get(3..23).ok_or(keys::Error::InvalidAddress)?) + .map_err(|_| keys::Error::InvalidAddress)? + .into(), ))]), ScriptType::ScriptHash => Ok(vec![ScriptAddress::new_p2sh(AddressHashEnum::AddressHash( - self.data[2..22].into(), + <[u8; 20]>::try_from(self.data.get(2..22).ok_or(keys::Error::InvalidAddress)?) + .map_err(|_| keys::Error::InvalidAddress)? + .into(), ))]), ScriptType::Multisig => { let mut addresses: Vec = Vec::new(); @@ -449,10 +454,14 @@ impl Script { }, ScriptType::NullData => Ok(vec![]), ScriptType::WitnessScript => Ok(vec![ScriptAddress::new_p2wsh(AddressHashEnum::WitnessScriptHash( - self.data[2..34].into(), + <[u8; 32]>::try_from(self.data.get(2..34).ok_or(keys::Error::InvalidAddress)?) + .map_err(|_| keys::Error::InvalidAddress)? + .into(), ))]), ScriptType::WitnessKey => Ok(vec![ScriptAddress::new_p2wpkh(AddressHashEnum::AddressHash( - self.data[2..22].into(), + <[u8; 20]>::try_from(self.data.get(2..22).ok_or(keys::Error::InvalidAddress)?) + .map_err(|_| keys::Error::InvalidAddress)? + .into(), ))]), ScriptType::CallSender => { Ok(vec![]) // TODO diff --git a/mm2src/mm2_bitcoin/script/src/sign.rs b/mm2src/mm2_bitcoin/script/src/sign.rs index a70e423eea..95b9d5a0ea 100644 --- a/mm2src/mm2_bitcoin/script/src/sign.rs +++ b/mm2src/mm2_bitcoin/script/src/sign.rs @@ -9,6 +9,7 @@ use hash::{H256, H512}; use keys::KeyPair; use ser::Stream; use serde::Deserialize; +use std::convert::TryInto; use {Builder, Script}; const ZCASH_PREVOUTS_HASH_PERSONALIZATION: &[u8] = b"ZcashPrevoutHash"; @@ -466,7 +467,7 @@ impl TransactionInputSigner { sig_hash_stream.append(&blake_2b_256_personal( &prev_out_stream.out(), ZCASH_PREVOUTS_HASH_PERSONALIZATION, - )); + )?); } else { sig_hash_stream.append(&H256::default()); } @@ -480,7 +481,7 @@ impl TransactionInputSigner { sig_hash_stream.append(&blake_2b_256_personal( &sequence_stream.out(), ZCASH_SEQUENCE_HASH_PERSONALIZATION, - )); + )?); } else { sig_hash_stream.append(&H256::default()); } @@ -494,7 +495,7 @@ impl TransactionInputSigner { sig_hash_stream.append(&blake_2b_256_personal( &outputs_stream.out(), ZCASH_OUTPUTS_HASH_PERSONALIZATION, - )); + )?); } else if sighash.base == SighashBase::Single && input_index < self.outputs.len() { let mut outputs_stream = Stream::new(); outputs_stream.append(&self.outputs[input_index]); @@ -502,7 +503,7 @@ impl TransactionInputSigner { sig_hash_stream.append(&blake_2b_256_personal( &outputs_stream.out(), ZCASH_OUTPUTS_HASH_PERSONALIZATION, - )); + )?); } else { sig_hash_stream.append(&H256::default()); } @@ -515,7 +516,7 @@ impl TransactionInputSigner { sig_hash_stream.append(&blake_2b_256_personal( &join_splits_stream.out(), ZCASH_JOIN_SPLITS_HASH_PERSONALIZATION, - )); + )?); } else { sig_hash_stream.append(&H256::default()); } @@ -533,7 +534,7 @@ impl TransactionInputSigner { sig_hash_stream.append(&blake_2b_256_personal( &s_spends_stream.out(), ZCASH_SHIELDED_SPENDS_HASH_PERSONALIZATION, - )); + )?); } else { sig_hash_stream.append(&H256::default()); } @@ -544,7 +545,7 @@ impl TransactionInputSigner { s_outputs_stream.append(output); } let hash_shielded_outputs = - blake_2b_256_personal(&s_outputs_stream.out(), ZCASH_SHIELDED_OUTPUTS_HASH_PERSONALIZATION); + blake_2b_256_personal(&s_outputs_stream.out(), ZCASH_SHIELDED_OUTPUTS_HASH_PERSONALIZATION)?; sig_hash_stream.append(&hash_shielded_outputs); } else { sig_hash_stream.append(&H256::default()); @@ -560,7 +561,7 @@ impl TransactionInputSigner { sig_hash_stream.append(&self.inputs[input_index].amount); sig_hash_stream.append(&self.inputs[input_index].sequence); - Ok(blake_2b_256_personal(&sig_hash_stream.out(), &personalization)) + blake_2b_256_personal(&sig_hash_stream.out(), &personalization) } } @@ -608,16 +609,17 @@ fn compute_hash_outputs(sighash: Sighash, input_index: usize, outputs: &[Transac } } -fn blake_2b_256_personal(input: &[u8], personal: &[u8]) -> H256 { - H256::from( - Blake2b::new() - .hash_length(32) - .personal(personal) - .to_state() - .update(input) - .finalize() - .as_bytes(), - ) +fn blake_2b_256_personal(input: &[u8], personal: &[u8]) -> Result { + let bytes: [u8; 32] = Blake2b::new() + .hash_length(32) + .personal(personal) + .to_state() + .update(input) + .finalize() + .as_bytes() + .try_into() + .map_err(|_| "Invalid length".to_string())?; + Ok(H256::from(bytes)) } #[cfg(test)] @@ -783,7 +785,7 @@ mod tests { #[test] fn test_blake_2b_personal() { - let hash = blake_2b_256_personal(b"", b"ZcashPrevoutHash"); + let hash = blake_2b_256_personal(b"", b"ZcashPrevoutHash").unwrap(); assert_eq!( H256::from("d53a633bbecf82fe9e9484d8a0e727c73bb9e68c96e72dec30144f6a84afa136"), hash diff --git a/mm2src/mm2_main/src/lp_ordermatch.rs b/mm2src/mm2_main/src/lp_ordermatch.rs index 620cb79bfb..b9444d917c 100644 --- a/mm2src/mm2_main/src/lp_ordermatch.rs +++ b/mm2src/mm2_main/src/lp_ordermatch.rs @@ -436,11 +436,19 @@ async fn request_and_fill_orderbook(ctx: &MmArc, base: &str, rel: &str) -> Resul }, }; + let pubkey_without_prefix: [u8; 32] = match pubkey_bytes.get(1..).map(|slice| slice.try_into()) { + Some(Ok(arr)) => arr, + _ => { + warn!("Invalid pubkey length (not 32 bytes) for {}", pubkey); + continue; + }, + }; + if is_my_order(&pubkey, &my_pubsecp, &orderbook.my_p2p_pubkeys) { continue; } - if is_pubkey_banned(ctx, &pubkey_bytes[1..].into()) { + if is_pubkey_banned(ctx, &pubkey_without_prefix.into()) { warn!("Pubkey {} is banned", pubkey); continue; } @@ -2929,7 +2937,7 @@ fn lp_connect_start_bob(ctx: MmArc, maker_match: MakerMatch, maker_order: MakerO // lp_connect_start_bob is called only from process_taker_connect, which returns if CryptoCtx is not initialized let crypto_ctx = CryptoCtx::from_ctx(&ctx).expect("'CryptoCtx' must be initialized already"); let raw_priv = crypto_ctx.mm2_internal_privkey_secret(); - let my_persistent_pub = compressed_pub_key_from_priv_raw(raw_priv.as_slice(), ChecksumType::DSHA256).unwrap(); + let my_persistent_pub = compressed_pub_key_from_priv_raw(&raw_priv.take(), ChecksumType::DSHA256).unwrap(); let my_conf_settings = choose_maker_confs_and_notas( maker_order.conf_settings.clone(), @@ -3086,7 +3094,7 @@ fn lp_connected_alice(ctx: MmArc, taker_order: TakerOrder, taker_match: TakerMat // lp_connected_alice is called only from process_maker_connected, which returns if CryptoCtx is not initialized let crypto_ctx = CryptoCtx::from_ctx(&ctx).expect("'CryptoCtx' must be initialized already"); let raw_priv = crypto_ctx.mm2_internal_privkey_secret(); - let my_persistent_pub = compressed_pub_key_from_priv_raw(raw_priv.as_slice(), ChecksumType::DSHA256).unwrap(); + let my_persistent_pub = compressed_pub_key_from_priv_raw(&raw_priv.take(), ChecksumType::DSHA256).unwrap(); let maker_amount = taker_match.reserved.get_base_amount().clone(); let taker_amount = taker_match.reserved.get_rel_amount().clone(); diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index 0acb7fc443..17f070a4a4 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -78,7 +78,7 @@ use mm2_libp2p::{decode_signed, encode_and_sign, pub_sub_topic, PeerId, TopicPre use mm2_number::{BigDecimal, BigRational, MmNumber, MmNumberMultiRepr}; use mm2_state_machine::storable_state_machine::StateMachineStorage; use parking_lot::Mutex as PaMutex; -use rpc::v1::types::{Bytes as BytesJson, H256 as H256Json}; +use rpc::v1::types::{Bytes as BytesJson, H256 as H256Json, H264}; use secp256k1::{PublicKey, SecretKey, Signature}; use serde::Serialize; use serde_json::{self as json, Value as Json}; @@ -853,7 +853,7 @@ pub struct NegotiationDataV1 { started_at: u64, payment_locktime: u64, secret_hash: [u8; 20], - persistent_pubkey: Vec, + persistent_pubkey: H264, } #[derive(Clone, Debug, Eq, Deserialize, PartialEq, Serialize)] @@ -861,7 +861,7 @@ pub struct NegotiationDataV2 { started_at: u64, payment_locktime: u64, secret_hash: Vec, - persistent_pubkey: Vec, + persistent_pubkey: H264, maker_coin_swap_contract: Vec, taker_coin_swap_contract: Vec, } @@ -873,8 +873,8 @@ pub struct NegotiationDataV3 { secret_hash: Vec, maker_coin_swap_contract: Vec, taker_coin_swap_contract: Vec, - maker_coin_htlc_pub: Vec, - taker_coin_htlc_pub: Vec, + maker_coin_htlc_pub: H264, + taker_coin_htlc_pub: H264, } #[derive(Clone, Debug, Eq, Deserialize, PartialEq, Serialize)] @@ -910,7 +910,7 @@ impl NegotiationDataMsg { } } - pub fn maker_coin_htlc_pub(&self) -> &[u8] { + pub fn maker_coin_htlc_pub(&self) -> &H264 { match self { NegotiationDataMsg::V1(v1) => &v1.persistent_pubkey, NegotiationDataMsg::V2(v2) => &v2.persistent_pubkey, @@ -918,7 +918,7 @@ impl NegotiationDataMsg { } } - pub fn taker_coin_htlc_pub(&self) -> &[u8] { + pub fn taker_coin_htlc_pub(&self) -> &H264 { match self { NegotiationDataMsg::V1(v1) => &v1.persistent_pubkey, NegotiationDataMsg::V2(v2) => &v2.persistent_pubkey, @@ -2072,14 +2072,14 @@ mod lp_swap_tests { started_at: 0, payment_locktime: 0, secret_hash: [0; 20], - persistent_pubkey: vec![1; 33], + persistent_pubkey: [1; 33].into(), }; let expected = NegotiationDataMsg::V1(NegotiationDataV1 { started_at: 0, payment_locktime: 0, secret_hash: [0; 20], - persistent_pubkey: vec![1; 33], + persistent_pubkey: [1; 33].into(), }); let serialized = rmp_serde::to_vec_named(&v1).unwrap(); @@ -2093,7 +2093,7 @@ mod lp_swap_tests { started_at: 0, payment_locktime: 0, secret_hash: vec![0; 20], - persistent_pubkey: vec![1; 33], + persistent_pubkey: [1; 33].into(), maker_coin_swap_contract: vec![1; 20], taker_coin_swap_contract: vec![1; 20], }); @@ -2102,7 +2102,7 @@ mod lp_swap_tests { started_at: 0, payment_locktime: 0, secret_hash: [0; 20], - persistent_pubkey: vec![1; 33], + persistent_pubkey: [1; 33].into(), }; let serialized = rmp_serde::to_vec_named(&v2).unwrap(); @@ -2116,7 +2116,7 @@ mod lp_swap_tests { started_at: 0, payment_locktime: 0, secret_hash: vec![0; 20], - persistent_pubkey: vec![1; 33], + persistent_pubkey: [1; 33].into(), maker_coin_swap_contract: vec![1; 20], taker_coin_swap_contract: vec![1; 20], }); @@ -2133,8 +2133,8 @@ mod lp_swap_tests { secret_hash: vec![0; 20], maker_coin_swap_contract: vec![1; 20], taker_coin_swap_contract: vec![1; 20], - maker_coin_htlc_pub: vec![1; 33], - taker_coin_htlc_pub: vec![1; 33], + maker_coin_htlc_pub: [1; 33].into(), + taker_coin_htlc_pub: [1; 33].into(), }); // v3 must be deserialized to v3, backward compatibility is not required @@ -2348,7 +2348,7 @@ mod lp_swap_tests { taker_key_pair.public().compressed_unprefixed().unwrap().into(), maker_amount.clone(), taker_amount.clone(), - maker_key_pair.public_slice().into(), + <[u8; 33]>::try_from(maker_key_pair.public_slice()).unwrap().into(), uuid, None, conf_settings, @@ -2369,7 +2369,7 @@ mod lp_swap_tests { maker_key_pair.public().compressed_unprefixed().unwrap().into(), maker_amount.into(), taker_amount.into(), - taker_key_pair.public_slice().into(), + <[u8; 33]>::try_from(taker_key_pair.public_slice()).unwrap().into(), uuid, None, conf_settings, diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap.rs b/mm2src/mm2_main/src/lp_swap/maker_swap.rs index 0eb72b8a71..a0c63175c7 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap.rs @@ -36,6 +36,7 @@ use parking_lot::Mutex as PaMutex; use primitives::hash::{H256, H264}; use rpc::v1::types::{Bytes as BytesJson, H256 as H256Json, H264 as H264Json}; use std::any::TypeId; +use std::convert::TryInto; use std::ops::Deref; use std::path::PathBuf; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; @@ -426,7 +427,7 @@ impl MakerSwap { NegotiationDataMsg::V2(NegotiationDataV2 { started_at: r.data.started_at, payment_locktime: r.data.maker_payment_lock, - persistent_pubkey: r.data.my_persistent_pub.0.to_vec(), + persistent_pubkey: r.data.my_persistent_pub, secret_hash, maker_coin_swap_contract, taker_coin_swap_contract, @@ -438,8 +439,8 @@ impl MakerSwap { secret_hash, maker_coin_swap_contract, taker_coin_swap_contract, - maker_coin_htlc_pub: self.my_maker_coin_htlc_pub().into(), - taker_coin_htlc_pub: self.my_taker_coin_htlc_pub().into(), + maker_coin_htlc_pub: self.my_maker_coin_htlc_pub(), + taker_coin_htlc_pub: self.my_taker_coin_htlc_pub(), }) } } @@ -569,8 +570,8 @@ impl MakerSwap { taker_payment_spend_trade_fee: Some(SavedTradeFee::from(taker_payment_spend_trade_fee)), maker_coin_swap_contract_address, taker_coin_swap_contract_address, - maker_coin_htlc_pubkey: Some(maker_coin_htlc_pubkey.as_slice().into()), - taker_coin_htlc_pubkey: Some(taker_coin_htlc_pubkey.as_slice().into()), + maker_coin_htlc_pubkey: Some(maker_coin_htlc_pubkey.into()), + taker_coin_htlc_pubkey: Some(taker_coin_htlc_pubkey.into()), p2p_privkey: self.p2p_privkey.map(SerializableSecp256k1Keypair::from), }; @@ -658,7 +659,10 @@ impl MakerSwap { }; // Validate maker_coin_htlc_pubkey realness - if let Err(err) = self.maker_coin.validate_other_pubkey(taker_data.maker_coin_htlc_pub()) { + if let Err(err) = self + .maker_coin + .validate_other_pubkey(&taker_data.maker_coin_htlc_pub().0) + { self.broadcast_negotiated_false(); return Ok((Some(MakerSwapCommand::Finish), vec![MakerSwapEvent::NegotiateFailed( ERRL!("!taker_data.maker_coin_htlc_pub {}", err).into(), @@ -666,7 +670,10 @@ impl MakerSwap { }; // Validate taker_coin_htlc_pubkey realness - if let Err(err) = self.taker_coin.validate_other_pubkey(taker_data.taker_coin_htlc_pub()) { + if let Err(err) = self + .taker_coin + .validate_other_pubkey(&taker_data.taker_coin_htlc_pub().0) + { self.broadcast_negotiated_false(); return Ok((Some(MakerSwapCommand::Finish), vec![MakerSwapEvent::NegotiateFailed( ERRL!("!taker_data.taker_coin_htlc_pub {}", err).into(), @@ -681,8 +688,8 @@ impl MakerSwap { taker_pubkey: H264Json::default(), maker_coin_swap_contract_addr, taker_coin_swap_contract_addr, - maker_coin_htlc_pubkey: Some(taker_data.maker_coin_htlc_pub().into()), - taker_coin_htlc_pubkey: Some(taker_data.taker_coin_htlc_pub().into()), + maker_coin_htlc_pubkey: Some(*taker_data.maker_coin_htlc_pub()), + taker_coin_htlc_pubkey: Some(*taker_data.taker_coin_htlc_pub()), }), ])) } @@ -1341,7 +1348,9 @@ impl MakerSwap { taker.bytes = data.taker.0; let crypto_ctx = try_s!(CryptoCtx::from_ctx(&ctx)); - let my_persistent_pub = H264::from(&**crypto_ctx.mm2_internal_key_pair().public()); + let my_persistent_pub = H264::from(try_s!(TryInto::<[u8; 33]>::try_into( + crypto_ctx.mm2_internal_key_pair().public_slice() + ))); let conf_settings = SwapConfirmationsSettings { maker_coin_confs: data.maker_payment_confirmations, diff --git a/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs b/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs index e4e430c71c..4d9b6c18fc 100644 --- a/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs +++ b/mm2src/mm2_main/src/lp_swap/recreate_swap_data.rs @@ -454,7 +454,7 @@ async fn convert_maker_to_taker_events( MakerSwapEvent::TakerPaymentSpent(tx_ident) => { //Is the watcher_reward argument important here? let secret = match maker_coin.extract_secret(&secret_hash.0, &tx_ident.tx_hex, false).await { - Ok(secret) => H256Json::from(secret.as_slice()), + Ok(secret) => H256Json::from(secret), Err(e) => { push_event!(TakerSwapEvent::TakerPaymentWaitForSpendFailed(ERRL!("{}", e).into())); push_event!(TakerSwapEvent::TakerPaymentWaitRefundStarted { wait_until: wait_refund_until }); @@ -495,6 +495,7 @@ mod tests { use super::*; use coins::{CoinsContext, MarketCoinOps, SwapOps, TestCoin}; use common::block_on; + use hex::FromHex; use mm2_core::mm_ctx::MmCtxBuilder; use mocktopus::mocking::{MockResult, Mockable}; use serde_json as json; @@ -534,7 +535,8 @@ mod tests { #[test] fn test_recreate_taker_swap() { TestCoin::extract_secret.mock_safe(|_coin, _secret_hash, _spend_tx, _watcher_reward| { - let secret = hex::decode("23a6bb64bc0ab2cc14cb84277d8d25134b814e5f999c66e578c9bba3c5e2d3a4").unwrap(); + let secret = + <[u8; 32]>::from_hex("23a6bb64bc0ab2cc14cb84277d8d25134b814e5f999c66e578c9bba3c5e2d3a4").unwrap(); MockResult::Return(Box::pin(async move { Ok(secret) })) }); TestCoin::platform_ticker.mock_safe(|_| MockResult::Return("TestCoin")); diff --git a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs index 312f62e5c5..ccb9b3d970 100644 --- a/mm2src/mm2_main/src/lp_swap/swap_watcher.rs +++ b/mm2src/mm2_main/src/lp_swap/swap_watcher.rs @@ -20,7 +20,7 @@ use mm2_state_machine::state_machine::StateMachineTrait; use serde::{Deserialize, Serialize}; use serde_json as json; use std::cmp::min; -use std::convert::Infallible; +use std::convert::{Infallible, TryInto}; use std::sync::Arc; use uuid::Uuid; @@ -381,7 +381,7 @@ impl State for WaitForTakerPaymentSpend { .extract_secret(&watcher_ctx.data.secret_hash, &tx_hex, true) .await { - Ok(bytes) => H256Json::from(bytes.as_slice()), + Ok(secret) => H256Json::from(secret), Err(err) => { return Self::change_state(Stopped::from_reason(StopReason::Error( WatcherError::UnableToExtractSecret(err).into(), @@ -608,7 +608,16 @@ fn spawn_taker_swap_watcher(ctx: MmArc, watcher_data: TakerSwapWatcherData, veri }; let spawner = ctx.spawner(); - let fee_hash = H256Json::from(watcher_data.taker_fee_hash.as_slice()); + let fee_hash = match TryInto::<[u8; 32]>::try_into(watcher_data.taker_fee_hash.as_slice()) { + Ok(bytes) => H256Json::from(bytes), + Err(_) => { + error!( + "Invalid taker fee hash length for {}", + hex::encode(&watcher_data.taker_fee_hash) + ); + return; + }, + }; let fut = async move { let taker_coin = match lp_coinfind(&ctx, &watcher_data.taker_coin).await { diff --git a/mm2src/mm2_main/src/lp_swap/taker_restart.rs b/mm2src/mm2_main/src/lp_swap/taker_restart.rs index d934b6b11e..3dd79fe895 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_restart.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_restart.rs @@ -196,7 +196,7 @@ pub async fn add_taker_payment_spent_event( .extract_secret(&secret_hash, &tx_ident.tx_hex, watcher_reward) .await { - Ok(bytes) => H256::from(bytes.as_slice()), + Ok(secret) => H256::from(secret), Err(_) => { return ERR!("Could not extract secret from taker payment spend transaction"); }, diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index c7b1cf59a9..8dc7ab3ff3 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -36,6 +36,7 @@ use parking_lot::Mutex as PaMutex; use primitives::hash::H264; use rpc::v1::types::{Bytes as BytesJson, H256 as H256Json, H264 as H264Json}; use serde_json::{self as json, Value as Json}; +use std::convert::TryInto; use std::ops::Deref; use std::path::PathBuf; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; @@ -971,7 +972,7 @@ impl TakerSwap { started_at: r.data.started_at, secret_hash, payment_locktime: r.data.taker_payment_lock, - persistent_pubkey: self.my_persistent_pub.to_vec(), + persistent_pubkey: self.my_persistent_pub.into(), maker_coin_swap_contract, taker_coin_swap_contract, }) @@ -982,8 +983,8 @@ impl TakerSwap { secret_hash, maker_coin_swap_contract, taker_coin_swap_contract, - maker_coin_htlc_pub: self.my_maker_coin_htlc_pub().into(), - taker_coin_htlc_pub: self.my_taker_coin_htlc_pub().into(), + maker_coin_htlc_pub: self.my_maker_coin_htlc_pub(), + taker_coin_htlc_pub: self.my_taker_coin_htlc_pub(), }) } } @@ -1131,8 +1132,8 @@ impl TakerSwap { maker_payment_spend_trade_fee: Some(SavedTradeFee::from(maker_payment_spend_trade_fee)), maker_coin_swap_contract_address, taker_coin_swap_contract_address, - maker_coin_htlc_pubkey: Some(maker_coin_htlc_pubkey.as_slice().into()), - taker_coin_htlc_pubkey: Some(taker_coin_htlc_pubkey.as_slice().into()), + maker_coin_htlc_pubkey: Some(maker_coin_htlc_pubkey.into()), + taker_coin_htlc_pubkey: Some(taker_coin_htlc_pubkey.into()), p2p_privkey: self.p2p_privkey.map(SerializableSecp256k1Keypair::from), }; @@ -1215,14 +1216,20 @@ impl TakerSwap { }; // Validate maker_coin_htlc_pubkey realness - if let Err(err) = self.maker_coin.validate_other_pubkey(maker_data.maker_coin_htlc_pub()) { + if let Err(err) = self + .maker_coin + .validate_other_pubkey(&maker_data.maker_coin_htlc_pub().0) + { return Ok((Some(TakerSwapCommand::Finish), vec![TakerSwapEvent::NegotiateFailed( ERRL!("!maker_data.maker_coin_htlc_pub {}", err).into(), )])); }; // Validate taker_coin_htlc_pubkey realness - if let Err(err) = self.taker_coin.validate_other_pubkey(maker_data.taker_coin_htlc_pub()) { + if let Err(err) = self + .taker_coin + .validate_other_pubkey(&maker_data.taker_coin_htlc_pub().0) + { return Ok((Some(TakerSwapCommand::Finish), vec![TakerSwapEvent::NegotiateFailed( ERRL!("!maker_data.taker_coin_htlc_pub {}", err).into(), )])); @@ -1287,8 +1294,8 @@ impl TakerSwap { secret_hash: maker_data.secret_hash().into(), maker_coin_swap_contract_addr, taker_coin_swap_contract_addr, - maker_coin_htlc_pubkey: Some(maker_data.maker_coin_htlc_pub().into()), - taker_coin_htlc_pubkey: Some(maker_data.taker_coin_htlc_pub().into()), + maker_coin_htlc_pubkey: Some(*maker_data.maker_coin_htlc_pub()), + taker_coin_htlc_pubkey: Some(*maker_data.taker_coin_htlc_pub()), }, )])) } @@ -1776,7 +1783,7 @@ impl TakerSwap { .extract_secret(&secret_hash.0, &tx_ident.tx_hex, watcher_reward) .await { - Ok(bytes) => H256Json::from(bytes.as_slice()), + Ok(secret) => H256Json::from(secret), Err(e) => { return Ok((Some(TakerSwapCommand::Finish), vec![ TakerSwapEvent::TakerPaymentWaitForSpendFailed(ERRL!("{}", e).into()), @@ -2040,7 +2047,10 @@ impl TakerSwap { } let crypto_ctx = try_s!(CryptoCtx::from_ctx(&ctx)); - let my_persistent_pub = H264::from(&**crypto_ctx.mm2_internal_key_pair().public()); + let my_persistent_pub = { + let my_persistent_pub: [u8; 33] = try_s!(crypto_ctx.mm2_internal_key_pair().public_slice().try_into()); + my_persistent_pub.into() + }; let mut maker = bits256::from([0; 32]); maker.bytes = data.maker.0; @@ -2846,7 +2856,7 @@ mod taker_swap_tests { TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); TestCoin::swap_contract_address.mock_safe(|_| MockResult::Return(None)); - TestCoin::extract_secret.mock_safe(|_, _, _, _| MockResult::Return(Box::pin(async move { Ok(vec![]) }))); + TestCoin::extract_secret.mock_safe(|_, _, _, _| MockResult::Return(Box::pin(async move { Ok([0; 32]) }))); static mut MY_PAYMENT_SENT_CALLED: bool = false; TestCoin::check_if_my_payment_sent.mock_safe(|_, _| { @@ -2973,7 +2983,7 @@ mod taker_swap_tests { TestCoin::ticker.mock_safe(|_| MockResult::Return("ticker")); TestCoin::swap_contract_address.mock_safe(|_| MockResult::Return(None)); - TestCoin::extract_secret.mock_safe(|_, _, _, _| MockResult::Return(Box::pin(async move { Ok(vec![]) }))); + TestCoin::extract_secret.mock_safe(|_, _, _, _| MockResult::Return(Box::pin(async move { Ok([0; 32]) }))); static mut SEARCH_TX_SPEND_CALLED: bool = false; TestCoin::search_for_swap_tx_spend_my.mock_safe(|_, _| { diff --git a/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs b/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs index 3050f22826..e67eaaeaaa 100644 --- a/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs +++ b/mm2src/mm2_main/tests/docker_tests/docker_tests_common.rs @@ -39,6 +39,7 @@ use script::Builder; use secp256k1::Secp256k1; pub use secp256k1::{PublicKey, SecretKey}; use serde_json::{self as json, Value as Json}; +use std::convert::TryFrom; use std::process::{Command, Stdio}; #[cfg(any(feature = "sepolia-maker-swap-v2-tests", feature = "sepolia-taker-swap-v2-tests"))] use std::str::FromStr; @@ -297,7 +298,9 @@ impl BchDockerOps { let adex_slp = SlpToken::new( 8, "ADEXSLP".into(), - slp_genesis_tx.tx_hash_as_bytes().as_slice().into(), + <&[u8; 32]>::try_from(slp_genesis_tx.tx_hash_as_bytes().as_slice()) + .unwrap() + .into(), self.coin.clone(), 1, ) @@ -313,7 +316,9 @@ impl BchDockerOps { }; block_on_f01(self.coin.wait_for_confirmations(confirm_payment_input)).unwrap(); *SLP_TOKEN_OWNERS.lock().unwrap() = slp_privkeys; - *SLP_TOKEN_ID.lock().unwrap() = slp_genesis_tx.tx_hash_as_bytes().as_slice().into(); + *SLP_TOKEN_ID.lock().unwrap() = <[u8; 32]>::try_from(slp_genesis_tx.tx_hash_as_bytes().as_slice()) + .unwrap() + .into(); } } diff --git a/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs b/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs index abcdc2ce9a..5844459b9f 100644 --- a/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs @@ -55,7 +55,6 @@ const SEPOLIA_MAKER_PRIV: &str = "6e2f3a6223b928a05a3a3622b0c3f3573d03663b704a61 const SEPOLIA_TAKER_PRIV: &str = "e0be82dca60ff7e4c6d6db339ac9e1ae249af081dba2110bddd281e711608f16"; const NFT_ETH: &str = "NFT_ETH"; const ETH: &str = "ETH"; - #[cfg(any(feature = "sepolia-maker-swap-v2-tests", feature = "sepolia-taker-swap-v2-tests"))] const ERC20: &str = "ERC20DEV"; diff --git a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs index 49abc5c77f..53ceadae03 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -1346,7 +1346,7 @@ fn test_watcher_validate_taker_fee_eth() { })); assert!(validate_taker_fee_res.is_ok()); - let wrong_keypair = key_pair_from_secret(random_secp256k1_secret().as_slice()).unwrap(); + let wrong_keypair = key_pair_from_secret(&random_secp256k1_secret().take()).unwrap(); let error = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), sender_pubkey: wrong_keypair.public().to_vec(), @@ -1443,7 +1443,7 @@ fn test_watcher_validate_taker_fee_erc20() { })); assert!(validate_taker_fee_res.is_ok()); - let wrong_keypair = key_pair_from_secret(random_secp256k1_secret().as_slice()).unwrap(); + let wrong_keypair = key_pair_from_secret(&random_secp256k1_secret().take()).unwrap(); let error = block_on_f01(taker_coin.watcher_validate_taker_fee(WatcherValidateTakerFeeInput { taker_fee_hash: taker_fee.tx_hash_as_bytes().into_vec(), sender_pubkey: wrong_keypair.public().to_vec(),