Skip to content

Commit

Permalink
Merge ozan/withdrawal-endpoint-test into ekrem-new-architecture (#230)
Browse files Browse the repository at this point in the history
* wip

* WIP: created operators and verifiers

* Add steps for the flow

* Add withdrawal endpoint test,organize configs

* Flaky but ok

* Fix flakiness

* Remove unnecessary serialization

---------

Co-authored-by: lemonpartee <lemonpartey@proton.me>
Co-authored-by: Ekrem BAL <mail.ekrembal@gmail.com>
  • Loading branch information
3 people authored Aug 22, 2024
1 parent c84ae30 commit 85fd149
Show file tree
Hide file tree
Showing 12 changed files with 230 additions and 524 deletions.
7 changes: 6 additions & 1 deletion core/src/actor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,12 @@ impl Actor {
let mut sighash_cache = SighashCache::new(tx);
let sig_hash = sighash_cache.taproot_key_spend_signature_hash(
input_index,
&bitcoin::sighash::Prevouts::All(prevouts),
&match sighash_type {
Some(TapSighashType::SinglePlusAnyoneCanPay) => {
bitcoin::sighash::Prevouts::One(input_index, prevouts[input_index].clone())
}
_ => bitcoin::sighash::Prevouts::All(prevouts),
},
sighash_type.unwrap_or(TapSighashType::Default),
)?;
self.sign_with_tweak(sig_hash, None)
Expand Down
3 changes: 1 addition & 2 deletions core/src/bin/operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@ use clementine_core::{cli, extended_rpc::ExtendedRpc, servers::create_operator_s
#[tokio::main]
async fn main() {
let config = cli::get_configuration();
let verifier_endpoints = config.verifier_endpoints.clone().unwrap();
let rpc = ExtendedRpc::<bitcoincore_rpc::Client>::new(
config.bitcoin_rpc_url.clone(),
config.bitcoin_rpc_user.clone(),
config.bitcoin_rpc_password.clone(),
);

create_operator_server(config, rpc, verifier_endpoints)
create_operator_server(config, rpc)
.await
.unwrap()
.1
Expand Down
28 changes: 14 additions & 14 deletions core/src/bin/operator_and_verifiers.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use clementine_core::{cli, extended_rpc::ExtendedRpc, servers::create_operator_and_verifiers};
use clementine_core::{cli, extended_rpc::ExtendedRpc};

/// ```bash
/// curl -X POST http://127.0.0.1:3434 -H "Content-Type: application/json" -d '{
Expand All @@ -14,20 +14,20 @@ use clementine_core::{cli, extended_rpc::ExtendedRpc, servers::create_operator_a
/// ```
#[tokio::main]
async fn main() {
let config = cli::get_configuration();
let rpc = ExtendedRpc::<bitcoincore_rpc::Client>::new(
config.bitcoin_rpc_url.clone(),
config.bitcoin_rpc_user.clone(),
config.bitcoin_rpc_password.clone(),
);
// let config = cli::get_configuration();
// let rpc = ExtendedRpc::<bitcoincore_rpc::Client>::new(
// config.bitcoin_rpc_url.clone(),
// config.bitcoin_rpc_user.clone(),
// config.bitcoin_rpc_password.clone(),
// );

let (operator_client, operator_handle, _verifiers) =
create_operator_and_verifiers(config, rpc).await;
// let (operator_client, operator_handle, _verifiers) =
// create_operator_and_verifiers(config, rpc).await;

println!("Operator server started: {:?}", operator_client);
// println!("Operator server started: {:?}", operator_client);

operator_handle.stopped().await;
for verifier in _verifiers {
verifier.1.stopped().await;
}
// operator_handle.stopped().await;
// for verifier in _verifiers {
// verifier.1.stopped().await;
// }
}
3 changes: 2 additions & 1 deletion core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ pub struct BridgeConfig {
/// Bitcoin RPC user password.
pub bitcoin_rpc_password: String,
/// All Secret keys. Just for testing purposes.
pub all_operators_secret_keys: Option<Vec<secp256k1::SecretKey>>,
pub all_verifiers_secret_keys: Option<Vec<secp256k1::SecretKey>>,
/// All Secret keys. Just for testing purposes.
pub all_operators_secret_keys: Option<Vec<secp256k1::SecretKey>>,
/// Verifier endpoints.
pub verifier_endpoints: Option<Vec<String>>,
/// PostgreSQL database host address.
Expand Down
4 changes: 2 additions & 2 deletions core/src/extended_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,15 +203,15 @@ where
Ok(block_height)
}

pub fn get_txout_from_utxo(&self, outpoint: &OutPoint) -> Result<TxOut, BridgeError> {
pub fn get_txout_from_outpoint(&self, outpoint: &OutPoint) -> Result<TxOut, BridgeError> {
let tx = self.client.get_raw_transaction(&outpoint.txid, None)?;
let txout = tx.output[outpoint.vout as usize].clone();

Ok(txout)
}

// Following methods are just wrappers around the bitcoincore_rpc::Client methods
pub fn fundrawtransaction(
pub fn fund_raw_transaction(
&self,
tx: &Transaction,
options: Option<&bitcoincore_rpc::json::FundRawTransactionOptions>,
Expand Down
89 changes: 50 additions & 39 deletions core/src/operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use bitcoin::hashes::Hash;
use bitcoin::sighash::SighashCache;
use bitcoin::{Address, OutPoint, TapSighash, Transaction, TxOut, Txid};
use bitcoin_mock_rpc::RpcApiWrapper;
use bitcoincore_rpc::json::SigHashType;
use bitcoincore_rpc::json::{self, SigHashType};
use bitcoincore_rpc::RawTx;
use clementine_circuits::constants::BRIDGE_AMOUNT_SATS;
use clementine_circuits::sha256_hash;
Expand Down Expand Up @@ -46,9 +46,6 @@ where
let num_verifiers = config.verifiers_public_keys.len();

let signer = Actor::new(config.secret_key, config.network);
if signer.public_key != config.verifiers_public_keys[num_verifiers - 1] {
return Err(BridgeError::InvalidOperatorKey);
}

let db = OperatorDB::new(config.clone()).await;

Expand Down Expand Up @@ -222,44 +219,58 @@ where
return Ok(None);
}
let tx_ins = TransactionBuilder::create_tx_ins(vec![input_utxo.outpoint]);
// let user_xonly_pk = secp256k1::XOnlyPublicKey::from_slice(
// &input_utxo.txout.script_pubkey.as_bytes()[2..34],
// )?;
let tx_outs = vec![output_txout];
let user_xonly_pk = secp256k1::XOnlyPublicKey::from_slice(
&input_utxo.txout.script_pubkey.as_bytes()[2..34],
)?;
let tx_outs = vec![output_txout.clone()];
let mut tx = TransactionBuilder::create_btc_tx(tx_ins, tx_outs);
// let mut sighash_cache = SighashCache::new(tx.clone());
// let sighash = sighash_cache.taproot_key_spend_signature_hash(
// 0,
// &bitcoin::sighash::Prevouts::One(0, &input_utxo.txout),
// bitcoin::sighash::TapSighashType::SinglePlusAnyoneCanPay,
// )?;
tx.input[0].witness.push(user_sig.as_ref());

tx.verify(|_| Some(input_utxo.txout.clone())).unwrap();
// utils::SECP.verify_schnorr(
// &user_sig,
// &Message::from_digest_slice(sighash.as_byte_array()).expect("should be hash"),
// &user_xonly_pk,
// )?;
let mut sighash_cache = SighashCache::new(tx.clone());
let sighash = sighash_cache.taproot_key_spend_signature_hash(
0,
&bitcoin::sighash::Prevouts::One(0, &input_utxo.txout),
bitcoin::sighash::TapSighashType::SinglePlusAnyoneCanPay,
)?;
let user_sig_wrapped = bitcoin::taproot::Signature {
signature: user_sig,
sighash_type: bitcoin::sighash::TapSighashType::SinglePlusAnyoneCanPay,
};
tx.input[0].witness.push(user_sig_wrapped.serialize());
utils::SECP.verify_schnorr(
&user_sig,
&Message::from_digest_slice(sighash.as_byte_array()).expect("should be hash"),
&user_xonly_pk,
)?;
let op_return_txout = script_builder::op_return_txout(5u32.to_be_bytes()); // TODO: Instead of 5u32 use the index of the operator.
tx.output.push(op_return_txout.clone());
let mut funded_tx: Transaction = deserialize(&self.rpc.fundrawtransaction(&tx, None, None)?.hex)?;
// OP_RETURN should be the last output
if funded_tx.output[funded_tx.output.len() - 1] != op_return_txout.clone() {
// it should be one previous to the last
if funded_tx.output[funded_tx.output.len() - 2] != op_return_txout {
return Err(BridgeError::TxInputNotFound); // TODO: Fix ths error
}

let len = funded_tx.output.len();
if len >= 2 {
let (left, right) = funded_tx.output.split_at_mut(len - 1);
swap(&mut left[len - 2], &mut right[0]);
}
}
let signed_tx: Transaction = deserialize(&self.rpc.sign_raw_transaction_with_wallet(&funded_tx, None, None)?.hex)?;
self.rpc.send_raw_transaction(&signed_tx)?;
Ok(Some(signed_tx.compute_txid()))
let funded_tx = self
.rpc
.fund_raw_transaction(
&tx,
Some(&bitcoincore_rpc::json::FundRawTransactionOptions {
add_inputs: Some(true),
change_address: None,
change_position: Some(1),
change_type: None,
include_watching: None,
lock_unspents: None,
fee_rate: None,
subtract_fee_from_outputs: None,
replaceable: None,
conf_target: None,
estimate_mode: None,
}),
None,
)?
.hex;

let signed_tx: Transaction = deserialize(
&self
.rpc
.sign_raw_transaction_with_wallet(&funded_tx, None, None)?
.hex,
)?;
let final_txid = self.rpc.send_raw_transaction(&signed_tx)?;
Ok(Some(final_txid))
}
}

Expand Down
149 changes: 76 additions & 73 deletions core/src/servers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use traits::rpc::OperatorRpcServer;
pub async fn create_verifier_server<R>(
config: BridgeConfig,
rpc: ExtendedRpc<R>,
) -> Result<(std::net::SocketAddr, ServerHandle), BridgeError>
) -> Result<(HttpClient, ServerHandle), BridgeError>
where
R: RpcApiWrapper,
{
Expand All @@ -43,17 +43,20 @@ where
};
let handle = server.start(verifier.into_rpc());

let client = HttpClientBuilder::default()
.build(format!("http://{}:{}/", addr.ip(), addr.port()))
.unwrap();

tracing::info!("Verifier server started with address: {}", addr);

Ok((addr, handle))
Ok((client, handle))
}

/// Starts the server for the operator.
pub async fn create_operator_server<R>(
config: BridgeConfig,
rpc: ExtendedRpc<R>,
verifier_endpoints: Vec<String>,
) -> Result<(std::net::SocketAddr, ServerHandle), BridgeError>
) -> Result<(HttpClient, ServerHandle), BridgeError>
where
R: RpcApiWrapper,
{
Expand All @@ -73,76 +76,76 @@ where
};
let handle = server.start(operator.into_rpc());

let client = HttpClientBuilder::default()
.build(format!("http://{}:{}/", addr.ip(), addr.port()))
.unwrap();

tracing::info!("Operator server started with address: {}", addr);

Ok((addr, handle))
Ok((client, handle))
}

/// Starts operator and verifiers servers. This function's intended use is for
/// tests.
///
/// # Returns
///
/// Returns a tuple, containing `HttpClient` for operator, `ServerHandle` for
/// operator and a vector containing `SocketAddr` and `ServerHandle` for
/// verifiers + operator (operator last).
///
/// # Panics
///
/// Panics if there was an error while creating any of the servers.
pub async fn create_operator_and_verifiers<R>(
config: BridgeConfig,
rpc: ExtendedRpc<R>,
) -> (
HttpClient,
ServerHandle,
Vec<(std::net::SocketAddr, ServerHandle)>,
)
where
R: RpcApiWrapper,
{
let mut all_verifiers_secret_keys = config
.all_verifiers_secret_keys
.clone()
.expect("All secret keys are required for testing");
// Remove the operator secret key.
all_verifiers_secret_keys.pop().unwrap();

let futures = all_verifiers_secret_keys
.iter()
.enumerate()
.map(|(i, sk)| {
create_verifier_server(
BridgeConfig {
verifiers_public_keys: config.verifiers_public_keys.clone(),
secret_key: *sk,
port: 0, // Use the index to calculate the port
db_name: config.db_name.clone() + &i.to_string(),
..config.clone()
},
rpc.clone(),
)
})
.collect::<Vec<_>>();
let mut results = futures::future::try_join_all(futures).await.unwrap();

let verifier_endpoints: Vec<String> = results
.iter()
.map(|(socket_addr, _)| format!("http://{}:{}/", socket_addr.ip(), socket_addr.port()))
.collect();

let (operator_socket_addr, operator_handle) =
create_operator_server(config, rpc, verifier_endpoints)
.await
.unwrap();
let operator_client = HttpClientBuilder::default()
.build(format!(
"http://{}:{}/",
operator_socket_addr.ip(),
operator_socket_addr.port()
))
.unwrap();
results.push((operator_socket_addr, operator_handle.clone()));

(operator_client, operator_handle, results)
}
// / Starts operator and verifiers servers. This function's intended use is for
// / tests.
// /
// / # Returns
// /
// / Returns a tuple, containing `HttpClient` for operator, `ServerHandle` for
// / operator and a vector containing `SocketAddr` and `ServerHandle` for
// / verifiers + operator (operator last).
// /
// / # Panics
// /
// / Panics if there was an error while creating any of the servers.
// pub async fn create_operator_and_verifiers<R>(
// config: BridgeConfig,
// rpc: ExtendedRpc<R>,
// ) -> (
// Vec<(HttpClient, ServerHandle)>, // Verifier clients
// Vec<(HttpClient, ServerHandle)>, // Operator clients
// )
// where
// R: RpcApiWrapper,
// {
// let mut all_secret_keys = config.all_secret_keys.clone().unwrap_or_else(|| {
// panic!("All secret keys are required for testing");
// });

// let futures = all_verifiers_secret_keys
// .iter()
// .enumerate()
// .map(|(i, sk)| {
// create_verifier_server(
// BridgeConfig {
// verifiers_public_keys: config.verifiers_public_keys.clone(),
// secret_key: *sk,
// port: 0, // Use the index to calculate the port
// db_name: config.db_name.clone() + &i.to_string(),
// ..config.clone()
// },
// rpc.clone(),
// )
// })
// .collect::<Vec<_>>();
// let mut results = futures::future::try_join_all(futures).await.unwrap();

// let verifier_endpoints: Vec<String> = results
// .iter()
// .map(|(socket_addr, _)| format!("http://{}:{}/", socket_addr.ip(), socket_addr.port()))
// .collect();

// let (operator_socket_addr, operator_handle) =
// create_operator_server(config, rpc)
// .await
// .unwrap();
// let operator_client = HttpClientBuilder::default()
// .build(format!(
// "http://{}:{}/",
// operator_socket_addr.ip(),
// operator_socket_addr.port()
// ))
// .unwrap();
// results.push((operator_socket_addr, operator_handle.clone()));

// (operator_client, operator_handle, results)
// }
Loading

0 comments on commit 85fd149

Please sign in to comment.