Skip to content

Commit

Permalink
db: common: Fix get_withdrawal_sig_by_idx parse error. (#197)
Browse files Browse the repository at this point in the history
* db: common: Fix get_withdrawal_sig_by_idx parse error.

* db: common: Remove tuple type in get_withdrawal_sig_by_idx.

* tests: flow: Add verifier_down_for_withdrawal_signature test.

* test: flow: Fix malformed test config.

* cargo: Update bitcoin-mock-rpc version to 0.0.4.

* extended_rpc: Use the new `new_without_cleanup` call for cloning instead if `new`.

* general: Add changes made in main branch to dev branch.

* cargo: Update bitcoin-mock-rpc version to 0.0.5.
  • Loading branch information
ceyhunsen authored Jul 29, 2024
1 parent eefa458 commit d861936
Show file tree
Hide file tree
Showing 6 changed files with 185 additions and 7 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ toml = "0.8.12"
sqlx = { version = "0.7.4", default-features = false }
k256 = { version = "=0.13.3", default-features = false }
risc0-build = "0.21.0"
bitcoin-mock-rpc = { git = "https://github.com/chainwayxyz/bitcoin-mock-rpc", tag = "v0.0.3" }
bitcoin-mock-rpc = { git = "https://github.com/chainwayxyz/bitcoin-mock-rpc", tag = "v0.0.5" }

# Always optimize; building and running the guest takes much longer without optimization.
[profile.dev]
Expand Down
37 changes: 33 additions & 4 deletions core/src/database/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ impl Database {
idx: usize,
) -> Result<(Txid, secp256k1::schnorr::Signature), BridgeError> {
let qr: (String, String) =
sqlx::query_as("SELECT (bridge_fund_txid, sig) FROM withdrawal_sigs WHERE idx = $1;")
sqlx::query_as("SELECT bridge_fund_txid, sig FROM withdrawal_sigs WHERE idx = $1;")
.bind(idx as i64)
.fetch_one(&self.connection)
.await?;
Expand All @@ -247,10 +247,10 @@ mod tests {
use super::Database;
use crate::{
config::BridgeConfig, create_test_config, create_test_config_with_thread_name,
mock::common, EVMAddress,
mock::common, transaction_builder::TransactionBuilder, EVMAddress,
};
use bitcoin::{Address, OutPoint, XOnlyPublicKey};
use secp256k1::Secp256k1;
use bitcoin::{Address, Amount, OutPoint, ScriptBuf, TxOut, XOnlyPublicKey};
use secp256k1::{schnorr::Signature, Secp256k1};
use std::thread;

#[tokio::test]
Expand Down Expand Up @@ -314,6 +314,35 @@ mod tests {
.await
.unwrap();
}

#[tokio::test]
async fn get_save_withdrawal_sig() {
let config = create_test_config!("get_save_withdrawal_sig", "test_config.toml");
let db = Database::new(config).await.unwrap();

let txout = TxOut {
value: Amount::from_sat(0x45),
script_pubkey: ScriptBuf::new(),
};
let tx = TransactionBuilder::create_btc_tx(vec![], vec![txout]);
let txid = tx.compute_txid();

let sig_arr = [
0x14, 0x5f, 0x50, 0x9d, 0xab, 0x82, 0xb0, 0xa1, 0x51, 0x1a, 0x20, 0x00, 0x93, 0x03,
0x19, 0xb1, 0x11, 0x29, 0xa5, 0x77, 0x3e, 0xe5, 0xc8, 0x6a, 0x13, 0x42, 0x0c, 0x23,
0x8e, 0x97, 0x26, 0x0b, 0xbe, 0x8b, 0x8e, 0xdd, 0xcd, 0x71, 0x6e, 0x76, 0xd4, 0x06,
0xb6, 0x1d, 0x54, 0x7d, 0xac, 0xd9, 0xb9, 0x32, 0xdc, 0x93, 0xbf, 0x33, 0xf5, 0xb0,
0x3c, 0x2f, 0x99, 0x2c, 0x04, 0xf6, 0x70, 0x73,
];
let signature = Signature::from_slice(&sig_arr).unwrap();

db.save_withdrawal_sig(0x45, txid, signature).await.unwrap();

let (read_txid, read_signature) = db.get_withdrawal_sig_by_idx(0x45).await.unwrap();

assert_eq!(txid, read_txid);
assert_eq!(signature, read_signature);
}
}

#[cfg(poc)]
Expand Down
5 changes: 4 additions & 1 deletion core/src/extended_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,10 @@ where
R: RpcApiWrapper,
{
fn clone(&self) -> Self {
let new_client = R::new(&self.url, self.auth.clone())
// `new_without_cleanup` call is essentially same with `new` call. But
// it won't clean mock RPC database when called. It will only establish
// a new connection with database.
let new_client = R::new_without_cleanup(&self.url, self.auth.clone())
.unwrap_or_else(|e| panic!("Failed to clone Bitcoin RPC client: {}", e));

Self {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
tracing_debug = "debug,bitcoincore_rpc=info,hyper=error"
host = "127.0.0.1"
port = 3000
secret_key = "5555555555555555555555555555555555555555555555555555555555555555"
verifiers_public_keys = [
"4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa",
"466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f27",
"3c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b1",
"2c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991",
"9ac20335eb38768d2052be1dbbc3c8f6178407458e51e6b4ad22f1d91758895b",
]
num_verifiers = 4
min_relay_fee = 305
user_takes_after = 5
confirmation_treshold = 1
network = "regtest"
bitcoin_rpc_url = "http://127.0.0.1:18443"
bitcoin_rpc_user = "admin"
bitcoin_rpc_password = "admin"
all_secret_keys = [
"1111111111111111111111111111111111111111111111111111111111111111",
"2222222222222222222222222222222222222222222222222222222222222222",
"3333333333333333333333333333333333333333333333333333333333333333",
"4444444444444444444444444444444444444444444444444444444444444444",
"5555555555555555555555555555555555555555555555555555555555555555",
]
db_host = "127.0.0.1"
db_port = 5432
db_user = "clementine"
db_password = ""
db_name = "clementine"
115 changes: 115 additions & 0 deletions core/tests/flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use clementine_core::servers::*;
use clementine_core::traits::rpc::OperatorRpcClient;
use clementine_core::transaction_builder::{CreateTxOutputs, TransactionBuilder};
use clementine_core::utils::handle_taproot_witness_new;
use clementine_core::utils::SECP;
use clementine_core::EVMAddress;
use clementine_core::{
create_extended_rpc, create_test_config, create_test_config_with_thread_name,
Expand Down Expand Up @@ -220,3 +221,117 @@ async fn test_flow_2() {
.unwrap();
tracing::debug!("User takes back tx: {:#?}", user_takes_back_tx);
}

#[tokio::test]
async fn verifier_down_for_withdrawal_signature() {
let mut config = create_test_config_with_thread_name!(
"test_config_verifier_down_for_withdrawal_signature.toml"
);
let rpc = create_extended_rpc!(config);
let handle = thread::current()
.name()
.unwrap()
.split(':')
.last()
.unwrap()
.to_owned();
for i in 0..4 {
create_test_config!(
handle.clone() + i.to_string().as_str(),
"test_config_verifier_down_for_withdrawal_signature.toml"
);
}

let (xonly_pk, _) = config.secret_key.public_key(&SECP).x_only_public_key();
let taproot_address = Address::p2tr(&SECP, xonly_pk, None, config.network);

let tx_builder = TransactionBuilder::new(config.verifiers_public_keys.clone(), config.network);

let (operator_client, _operator_handler, results) =
create_operator_and_verifiers(config.clone(), rpc.clone()).await;

let evm_addresses = [
EVMAddress([1u8; 20]),
EVMAddress([2u8; 20]),
EVMAddress([3u8; 20]),
EVMAddress([4u8; 20]),
];
let deposit_addresses = evm_addresses
.iter()
.map(|evm_address| {
tx_builder
.generate_deposit_address(
taproot_address.as_unchecked(),
evm_address,
BRIDGE_AMOUNT_SATS,
config.user_takes_after,
)
.unwrap()
.0
})
.collect::<Vec<_>>();
println!("Deposit addresses: {:#?}", deposit_addresses);

for (idx, deposit_address) in deposit_addresses.iter().enumerate() {
let deposit_utxo = rpc
.send_to_address(deposit_address, BRIDGE_AMOUNT_SATS)
.unwrap();
println!("Deposit UTXO #{}: {:#?}", idx, deposit_utxo);

rpc.mine_blocks(18).unwrap();

let output = operator_client
.new_deposit_rpc(
deposit_utxo,
taproot_address.as_unchecked().clone(),
evm_addresses[idx],
)
.await
.unwrap();
println!("Output #{}: {:#?}", idx, output);
}

// Assume one of the verifier is down.
const VERIFIER_IDX: usize = 3;
results.get(VERIFIER_IDX).unwrap().1.stop().unwrap();

let withdrawal_address = Address::p2tr(&SECP, xonly_pk, None, config.network);

if let Ok(_withdraw_txid) = operator_client
.new_withdrawal_direct_rpc(0, withdrawal_address.as_unchecked().clone())
.await
{
println!(
"Verifier {} is down, this should not be possible.",
VERIFIER_IDX
);
assert!(false);
};

// Restart all servers.
results.iter().for_each(|server| {
let _ = server.1.stop();
});
let (operator_client, _operator_handler, _results) =
create_operator_and_verifiers(config.clone(), rpc.clone()).await;

let withdraw_txid = operator_client
.new_withdrawal_direct_rpc(0, withdrawal_address.as_unchecked().clone())
.await
.unwrap();
println!("Withdrawal send to address: {:?}", withdrawal_address);
println!("Withdrawal TXID: {:#?}", withdraw_txid);

// check whether it has an output with the withdrawal address
let tx = rpc.get_raw_transaction(&withdraw_txid, None).unwrap();
let rpc_withdraw_script = tx.output[0].script_pubkey.clone();
let rpc_withdraw_amount = tx.output[0].value;
let expected_withdraw_script = withdrawal_address.script_pubkey();
assert_eq!(rpc_withdraw_script, expected_withdraw_script);

// check if the amounts match
let anyone_can_spend_amount = script_builder::anyone_can_spend_txout().value;
let expected_withdraw_amount = Amount::from_sat(BRIDGE_AMOUNT_SATS - 2 * config.min_relay_fee)
- anyone_can_spend_amount * 2;
assert_eq!(expected_withdraw_amount, rpc_withdraw_amount);
}

0 comments on commit d861936

Please sign in to comment.