Skip to content

Commit

Permalink
Change transaction sequence to 0
Browse files Browse the repository at this point in the history
  • Loading branch information
ssantos21 committed Sep 2, 2024
1 parent d858cc7 commit 403e316
Show file tree
Hide file tree
Showing 16 changed files with 253 additions and 103 deletions.
4 changes: 2 additions & 2 deletions clients/apps/rust/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ enum Commands {
BroadcastBackupTransaction {
wallet_name: String,
statechain_id: String,
to_address: String,
to_address: Option<String>,
/// Transaction fee rate in sats per byte
fee_rate: Option<f64>
},
Expand Down Expand Up @@ -115,7 +115,7 @@ async fn main() -> Result<()> {
},
Commands::BroadcastBackupTransaction { wallet_name, statechain_id, to_address, fee_rate } => {
mercuryrustlib::coin_status::update_coins(&client_config, &wallet_name).await?;
mercuryrustlib::broadcast_backup_tx::execute(&client_config, &wallet_name, &statechain_id, &to_address, fee_rate).await?;
mercuryrustlib::broadcast_backup_tx::execute(&client_config, &wallet_name, &statechain_id, to_address, fee_rate).await?;
},
Commands::ListStatecoins { wallet_name } => {
mercuryrustlib::coin_status::update_coins(&client_config, &wallet_name).await?;
Expand Down
67 changes: 39 additions & 28 deletions clients/libs/rust/src/broadcast_backup_tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@ use anyhow::{anyhow, Result};
use electrum_client::ElectrumApi;
use mercurylib::wallet::{cpfp_tx, CoinStatus};

pub async fn execute(client_config: &ClientConfig, wallet_name: &str, statechain_id: &str, to_address: &str, fee_rate: Option<f64>) -> Result<()> {
pub async fn execute(client_config: &ClientConfig, wallet_name: &str, statechain_id: &str, to_address: Option<String>, fee_rate: Option<f64>) -> Result<()> {

let mut wallet: mercurylib::wallet::Wallet = get_wallet(&client_config.pool, &wallet_name).await?;

let is_address_valid = mercurylib::validate_address(to_address, &wallet.network)?;
if to_address.is_some() {
let to_address = to_address.clone().unwrap();
let is_address_valid = mercurylib::validate_address(&to_address, &wallet.network)?;

if !is_address_valid {
return Err(anyhow!("Invalid address"));
if !is_address_valid {
return Err(anyhow!("Invalid address"));
}
}

let backup_txs = get_backup_txs(&client_config.pool, &statechain_id).await?;
Expand All @@ -34,38 +37,46 @@ pub async fn execute(client_config: &ClientConfig, wallet_name: &str, statechain

let backup_tx = cpfp_tx::latest_backup_tx_pays_to_user_pubkey(&backup_txs, &coin, &wallet.network)?;

let fee_rate = match fee_rate {
Some(fee_rate) => fee_rate,
None => {
let mut fee_rate_btc_per_kb = client_config.electrum_client.estimate_fee(1)?;
let cpfp_tx = if to_address.is_some() {

// Why does it happen?
if fee_rate_btc_per_kb <= 0.0 {
fee_rate_btc_per_kb = 0.00001;
}
let to_address = to_address.clone().unwrap();

let fee_rate_sats_per_byte = fee_rate_btc_per_kb * 100000.0;
let fee_rate = match fee_rate {
Some(fee_rate) => fee_rate,
None => {
let mut fee_rate_btc_per_kb = client_config.electrum_client.estimate_fee(1)?;

if fee_rate_sats_per_byte > client_config.max_fee_rate {
client_config.max_fee_rate
} else {
fee_rate_sats_per_byte
}
},
};
// Why does it happen?
if fee_rate_btc_per_kb <= 0.0 {
fee_rate_btc_per_kb = 0.00001;
}

let cpfp_tx = cpfp_tx::create_cpfp_tx(&backup_tx, &coin, to_address, fee_rate, &wallet.network)?;
let fee_rate_sats_per_byte = fee_rate_btc_per_kb * 100000.0;

let tx_bytes = hex::decode(&backup_tx.tx)?;
let txid = client_config.electrum_client.transaction_broadcast_raw(&tx_bytes)?;
println!("Broadcasting backup transaction: {}", txid);
if fee_rate_sats_per_byte > client_config.max_fee_rate {
client_config.max_fee_rate
} else {
fee_rate_sats_per_byte
}
},
};

let tx_bytes = hex::decode(&cpfp_tx)?;
let txid = client_config.electrum_client.transaction_broadcast_raw(&tx_bytes)?;
println!("Broadcasting CPFP transaction: {}", txid);
Some(cpfp_tx::create_cpfp_tx(&backup_tx, &coin, &to_address, fee_rate, &wallet.network)?)
} else {
None
};

let tx_bytes = hex::decode(&backup_tx.tx)?;
let mut txid = client_config.electrum_client.transaction_broadcast_raw(&tx_bytes)?;

if cpfp_tx.is_some() {
let cpfp_tx = cpfp_tx.unwrap();
let tx_bytes = hex::decode(&cpfp_tx)?;
txid = client_config.electrum_client.transaction_broadcast_raw(&tx_bytes)?;
}

coin.tx_cpfp = Some(txid.to_string());
coin.withdrawal_address = Some(to_address.to_string());
coin.withdrawal_address = if to_address.is_some() { Some(to_address.unwrap()) } else { Some(coin.backup_address.clone()) };
coin.status = CoinStatus::WITHDRAWING;

let signed_statechain_id = coin.signed_statechain_id.as_ref().unwrap().to_string();
Expand Down
5 changes: 5 additions & 0 deletions clients/tests/rust/src/electrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,9 @@ pub async fn check_address(client_config: &ClientConfig, address: &str, amount:
}

Ok(true)
}

pub async fn get_blockheight(client_config: &ClientConfig) -> Result<usize> {
let blockheight = client_config.electrum_client.block_headers_subscribe()?.height;
Ok(blockheight)
}
2 changes: 2 additions & 0 deletions clients/tests/rust/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod tb02_transfer_address_reuse;
pub mod tb03_simple_atomic_transfer;
pub mod tb04_simple_lightning_latch;
pub mod tm01_sender_double_spends;
mod tv05;
use anyhow::{Result, Ok};

#[tokio::main(flavor = "current_thread")]
Expand All @@ -20,6 +21,7 @@ async fn main() -> Result<()> {
tm01_sender_double_spends::execute().await?;
ta01_sign_second_not_called::execute().await?;
ta02_duplicate_deposits::execute().await?;
tv05::execute().await?;

Ok(())
}
103 changes: 103 additions & 0 deletions clients/tests/rust/src/tv05.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use std::{env, process::Command, thread, time::Duration};

use anyhow::{Result, Ok};
use mercuryrustlib::{client_config::ClientConfig, CoinStatus, Wallet};

use crate::{bitcoin_core, electrs};

async fn w1_transfer_to_w2(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?;

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

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

// It appears that Electrs takes a few seconds to index the transaction
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.status == CoinStatus::CONFIRMED).unwrap();
let statechain_id = new_coin.statechain_id.as_ref().unwrap();

assert!(new_coin.status == CoinStatus::CONFIRMED);

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

let batch_id = None;
let force_send = false;

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

let transfer_receive_result = mercuryrustlib::transfer_receiver::execute(&client_config, &wallet2.name).await?;
let received_statechain_ids = transfer_receive_result.received_statechain_ids;

assert!(received_statechain_ids.contains(&statechain_id.to_string()));
assert!(received_statechain_ids.len() == 1);

mercuryrustlib::coin_status::update_coins(&client_config, &wallet2.name).await?;
let local_wallet_2: mercuryrustlib::Wallet = mercuryrustlib::sqlite_manager::get_wallet(&client_config.pool, &wallet2.name).await?;
let new_w2_coin = local_wallet_2.coins.iter().find(|&coin| coin.statechain_id == Some(statechain_id.clone())).unwrap();

assert!(new_coin.status == CoinStatus::CONFIRMED);
assert!(new_w2_coin.status == CoinStatus::CONFIRMED);

let fee_rate = None;

let core_wallet_address = Some(core_wallet_address);

let result = mercuryrustlib::broadcast_backup_tx::execute(&client_config, &wallet1.name, &statechain_id, core_wallet_address.clone(), fee_rate).await;

assert!(result.is_err());

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

assert!(err.to_string() == "Electrum server error: {\"code\":2,\"message\":\"non-final\"}");

let _ = bitcoin_core::generatetoaddress(990, &core_wallet_address.clone().unwrap())?;

let result = mercuryrustlib::broadcast_backup_tx::execute(&client_config, &wallet2.name, &statechain_id, core_wallet_address, fee_rate).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");

env::set_var("ML_NETWORK", "regtest");

let client_config = mercuryrustlib::client_config::load().await;

let wallet1 = mercuryrustlib::wallet::create_wallet(
"wallet1",
&client_config).await?;

mercuryrustlib::sqlite_manager::insert_wallet(&client_config.pool, &wallet1).await?;

let wallet2 = mercuryrustlib::wallet::create_wallet(
"wallet2",
&client_config).await?;

mercuryrustlib::sqlite_manager::insert_wallet(&client_config.pool, &wallet2).await?;

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

println!("TV05 - Result as reported.");

Ok(())
}
2 changes: 2 additions & 0 deletions lib/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pub enum MercuryError {
Bech32Error,
HexError,
LocktimeNotBlockHeightError,
TransactionSequenceDifferentThanZeroError,
BitcoinConsensusEncodeError,
MusigNonceGenError,
InvalidStatechainAddressError,
Expand All @@ -68,6 +69,7 @@ pub enum MercuryError {
SecpError,
NoBackupTransactionFound,
Tx1HasMoreThanOneInput,
EmptyInput,
InvalidSignature,
EmptyWitness,
EmptyWitnessData,
Expand Down
2 changes: 1 addition & 1 deletion lib/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ pub fn get_musig_session(
input: vec![TxIn {
previous_output: OutPoint { txid: input_txid, vout: input_vout },
script_sig: ScriptBuf::new(),
sequence: bitcoin::Sequence(0xFFFFFFFF), // Ignore nSequence.
sequence: bitcoin::Sequence(0x0), // Ignore nSequence.
witness: Witness::default(),
}],
output: outputs,
Expand Down
29 changes: 28 additions & 1 deletion lib/src/transfer/receiver.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::str::FromStr;

use bitcoin::{PrivateKey, Transaction, hashes::{sha256, Hash}, Txid, Address, sighash::{TapSighashType, SighashCache, self}, TxOut, taproot::TapTweakHash};
use bitcoin::{hashes::{sha256, Hash}, sighash::{self, SighashCache, TapSighashType}, taproot::TapTweakHash, Address, PrivateKey, Sequence, Transaction, TxOut, Txid};
use secp256k1_zkp::{PublicKey, schnorr::Signature, Secp256k1, Message, XOnlyPublicKey, musig::{MusigPubNonce, BlindingFactor, blinded_musig_pubkey_xonly_tweak_add, MusigAggNonce, MusigSession}, SecretKey, Scalar, KeyPair};
use serde::{Serialize, Deserialize};

Expand Down Expand Up @@ -291,6 +291,12 @@ pub fn validate_signature_scheme(
break;
}

if verify_transaction_sequence(&backup_tx.tx).is_err() {
println!("transaction sequence is not correct");
sig_scheme_validation = false;
break;
}

if previous_lock_time.is_some() {
let prev_lock_time = previous_lock_time.unwrap();
let current_lock_time = crate::utils::get_blockheight(&backup_tx)?;
Expand Down Expand Up @@ -369,6 +375,27 @@ pub fn verify_transaction_signature(tx_n_hex: &str, tx0_hex: &str, fee_rate_tole

}

#[cfg_attr(feature = "bindings", uniffi::export)]
pub fn verify_transaction_sequence(tx_n_hex: &str) -> Result<(), MercuryError> {

let tx_n: Transaction = bitcoin::consensus::encode::deserialize(&hex::decode(&tx_n_hex)?)?;

if tx_n.input.is_empty() {
return Err(MercuryError::EmptyInput);
}

if tx_n.input.len() > 1 {
return Err(MercuryError::Tx1HasMoreThanOneInput);
}

let input = tx_n.input.first().unwrap();

if input.sequence != Sequence(0) {
return Err(MercuryError::TransactionSequenceDifferentThanZeroError);
}

Ok(())
}

fn get_tx_hash(tx_0: &Transaction, tx_n: &Transaction) -> Result<Message, MercuryError> {

Expand Down
10 changes: 5 additions & 5 deletions wasm/node_pkg/debug/mercury_wasm.js
Original file line number Diff line number Diff line change
Expand Up @@ -1176,17 +1176,17 @@ module.exports.__wbg_buffer_085ec1f694018c4f = function() { return logError(func
return addHeapObject(ret);
}, arguments) };

module.exports.__wbg_get_97b561fb56f034b5 = function() { return handleError(function (arg0, arg1) {
const ret = Reflect.get(getObject(arg0), getObject(arg1));
return addHeapObject(ret);
}, arguments) };

module.exports.__wbindgen_is_function = function(arg0) {
const ret = typeof(getObject(arg0)) === 'function';
_assertBoolean(ret);
return ret;
};

module.exports.__wbg_get_97b561fb56f034b5 = function() { return handleError(function (arg0, arg1) {
const ret = Reflect.get(getObject(arg0), getObject(arg1));
return addHeapObject(ret);
}, arguments) };

module.exports.__wbindgen_debug_string = function(arg0, arg1) {
const ret = debugString(getObject(arg1));
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
Expand Down
Binary file modified wasm/node_pkg/debug/mercury_wasm_bg.wasm
Binary file not shown.
Loading

0 comments on commit 403e316

Please sign in to comment.