Skip to content

Commit

Permalink
Add extra validation for duplicated coins in rust lib
Browse files Browse the repository at this point in the history
  • Loading branch information
ssantos21 committed Nov 5, 2024
1 parent da76505 commit 30a2c35
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 14 deletions.
5 changes: 3 additions & 2 deletions clients/apps/rust/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ enum Commands {
force_send: Option<bool>,
/// Batch id for atomic transfers
batch_id: Option<String>,
duplicated_indexes: Option<Vec<u32>>,
},
/// Send a statechain coin to a transfer address
TransferReceive { wallet_name: String },
Expand Down Expand Up @@ -158,12 +159,12 @@ async fn main() -> Result<()> {

println!("{}", serde_json::to_string_pretty(&obj).unwrap());
},
Commands::TransferSend { wallet_name, statechain_id, to_address, force_send, batch_id } => {
Commands::TransferSend { wallet_name, statechain_id, to_address, force_send, batch_id, duplicated_indexes } => {
mercuryrustlib::coin_status::update_coins(&client_config, &wallet_name).await?;

let force_send = force_send.unwrap_or(false);

mercuryrustlib::transfer_sender::execute(&client_config, &to_address, &wallet_name, &statechain_id, None, force_send, batch_id).await?;
mercuryrustlib::transfer_sender::execute(&client_config, &to_address, &wallet_name, &statechain_id, duplicated_indexes, force_send, batch_id).await?;

let obj = json!({"Transfer": "sent"});

Expand Down
1 change: 0 additions & 1 deletion clients/libs/rust/src/coin_status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ async fn check_deposit(client_config: &ClientConfig, coin: &mut Coin, wallet_net
let activity_utxo = format!("{}:{}", utxo.tx_hash.to_string(), utxo.tx_pos);

let activity = Some(create_activity(&activity_utxo, utxo.value as u32, "deposit"));
// return Ok(Some(activity));

deposit_result = Some(DepositResult {
activity: activity.unwrap(),
Expand Down
39 changes: 30 additions & 9 deletions clients/libs/rust/src/transfer_sender.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::cmp::Ordering;
use std::{cmp::Ordering, str::FromStr};

use crate::{client_config::ClientConfig, deposit::create_tx1, sqlite_manager::{get_backup_txs, get_wallet, update_backup_txs, update_wallet}, transaction::new_transaction, utils::info_config};
use anyhow::{anyhow, Result};
Expand Down Expand Up @@ -88,14 +88,35 @@ pub async fn create_backup_transactions(
return Err(anyhow!("There must be at least one coin with duplicate_index == 0"));
}

// Move the coin with CONFIRMED status to the last position
// coin_list.sort_by(|a, b| {
// match (&a.status, &b.status) {
// (CoinStatus::CONFIRMED, _) => Ordering::Greater,
// (_, CoinStatus::CONFIRMED) => Ordering::Less,
// _ => Ordering::Equal,
// }
// });
for coin in coin_list.iter_mut() {
if coin.status == CoinStatus::DUPLICATED {
let address = bitcoin::Address::from_str(&coin.aggregated_address.as_ref().unwrap())?.require_network(client_config.network)?;
let utxo_list = client_config.electrum_client.script_list_unspent(&address.script_pubkey())?;

for unspent in utxo_list {
if coin.utxo_txid == Some(unspent.tx_hash.to_string()) && coin.utxo_vout == Some(unspent.tx_pos as u32) {
let mut is_confirmed = false;

if unspent.height > 0 {
let block_header = client_config.electrum_client.block_headers_subscribe_raw()?;
let blockheight = block_header.height;

let confirmations = blockheight - unspent.height + 1;

if confirmations as u32 >= client_config.confirmation_target {
is_confirmed = true;
}
}

if !is_confirmed {
return Err(anyhow!("The coin with duplicated index {} has not yet been confirmed. This transfer cannot be performed.", coin.duplicate_index));
}

break;
}
}
}
}

// Move the coin with CONFIRMED status to the first position
coin_list.sort_by(|a, b| {
Expand Down
82 changes: 80 additions & 2 deletions clients/tests/rust/src/ta03_multiple_deposits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ async fn deposit(amount_in_sats: u32, client_config: &ClientConfig, deposit_addr
let mut is_tx_indexed = false;

while !is_tx_indexed {
is_tx_indexed = electrs::check_address(client_config, &deposit_address, 1000).await?;
is_tx_indexed = electrs::check_address(client_config, &deposit_address, amount_in_sats).await?;
thread::sleep(Duration::from_secs(1));
}

Expand Down Expand Up @@ -461,7 +461,7 @@ async fn multiple_sends_workflow(client_config: &ClientConfig, wallet1: &Wallet,
Ok(())
}

async fn send_to_itself_workflow(client_config: &ClientConfig, wallet1: &Wallet, wallet2: &Wallet) -> Result<()> {
async fn send_to_itself_workflow(client_config: &ClientConfig, wallet1: &Wallet, wallet2: &Wallet) -> Result<()> {

let amount = 1000;

Expand Down Expand Up @@ -616,6 +616,82 @@ async fn send_to_itself_workflow(client_config: &ClientConfig, wallet1: &Wallet,

}

async fn send_unconfirmed_duplicated_workflow(client_config: &ClientConfig, wallet1: &Wallet, wallet2: &Wallet) -> Result<()> {

let amount = 1000;

let token_id = mercuryrustlib::deposit::get_token(client_config).await?;

let deposit_address = mercuryrustlib::deposit::get_deposit_bitcoin_address(&client_config, &wallet1.name, &token_id, amount).await?;

deposit(amount, &client_config, &deposit_address).await?;

let amount = 1000;

deposit(amount, &client_config, &deposit_address).await?;

let amount = 2000;

let _ = bitcoin_core::sendtoaddress(amount, &deposit_address)?;

let mut is_tx_indexed = false;

while !is_tx_indexed {
is_tx_indexed = electrs::check_address(client_config, &deposit_address, amount).await?;
thread::sleep(Duration::from_secs(1));
}

mercuryrustlib::coin_status::update_coins(&client_config, &wallet1.name).await?;
let wallet1: mercuryrustlib::Wallet = mercuryrustlib::sqlite_manager::get_wallet(&client_config.pool, &wallet1.name).await?;

let new_coin = wallet1.coins.iter().find(|&coin| coin.aggregated_address == Some(deposit_address.clone()) && coin.duplicate_index == 0 && coin.status == CoinStatus::CONFIRMED);
let confirmed_duplicated_coin = wallet1.coins.iter().find(|&coin| coin.aggregated_address == Some(deposit_address.clone()) && coin.status == CoinStatus::DUPLICATED && coin.amount == Some(1000));
let unconfirmed_duplicated_coin = wallet1.coins.iter().find(|&coin| coin.aggregated_address == Some(deposit_address.clone()) && coin.status == CoinStatus::DUPLICATED && coin.amount == Some(2000));

assert!(new_coin.is_some());
assert!(confirmed_duplicated_coin.is_some());
assert!(unconfirmed_duplicated_coin.is_some());

let unconfirmed_duplicated_coin = unconfirmed_duplicated_coin.unwrap();

let new_coin = new_coin.unwrap();

let statechain_id = new_coin.statechain_id.as_ref().unwrap();

let wallet2_transfer_adress = mercuryrustlib::transfer_receiver::new_transfer_address(&client_config, &wallet2.name).await?;

let batch_id = None;

let force_send = true;

let duplicated_indexes = vec![1, 2];

// try to send the unconfirmed duplicated coin
let result = mercuryrustlib::transfer_sender::execute(&client_config, &wallet2_transfer_adress, &wallet1.name, &statechain_id, Some(duplicated_indexes), force_send, batch_id).await;

assert!(result.is_err());

let error_message = result.err().unwrap().to_string();

let expected_error_message = format!("The coin with duplicated index {} has not yet been confirmed. This transfer cannot be performed.", unconfirmed_duplicated_coin.duplicate_index);

assert!(error_message.contains(expected_error_message.as_str()));

let core_wallet_address = bitcoin_core::getnewaddress()?;
let remaining_blocks = client_config.confirmation_target;
let _ = bitcoin_core::generatetoaddress(remaining_blocks, &core_wallet_address)?;

let batch_id = None;

let duplicated_indexes = vec![1, 2];

let result = mercuryrustlib::transfer_sender::execute(&client_config, &wallet2_transfer_adress, &wallet1.name, &statechain_id, Some(duplicated_indexes), force_send, batch_id).await;

assert!(result.is_ok());

Ok(())
}

pub async fn execute() -> Result<()> {

let _ = Command::new("rm").arg("wallet.db").arg("wallet.db-shm").arg("wallet.db-wal").output().expect("failed to execute process");
Expand Down Expand Up @@ -650,6 +726,8 @@ pub async fn execute() -> Result<()> {

send_to_itself_workflow(&client_config, &wallet1, &wallet2).await?;

send_unconfirmed_duplicated_workflow(&client_config, &wallet1, &wallet2).await?;

println!("TA03 - Test \"Multiple Deposits in the Same Adress\" completed successfully");

Ok(())
Expand Down

0 comments on commit 30a2c35

Please sign in to comment.