Skip to content

Commit

Permalink
feat(bridge-exec)!: aggregate_[deposit|withdraw]_sig
Browse files Browse the repository at this point in the history
  • Loading branch information
storopoli committed Sep 25, 2024
1 parent 44a311b commit 840d444
Show file tree
Hide file tree
Showing 6 changed files with 355 additions and 45 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions crates/bridge-exec/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ rust.unused_must_use = "deny"
rustdoc.all = "warn"

[dependencies]
alpen-express-btcio = { workspace = true }
alpen-express-primitives = { workspace = true }
alpen-express-rpc-api = { workspace = true, features = ["client"] }
express-bridge-sig-manager = { workspace = true }
Expand Down
181 changes: 172 additions & 9 deletions crates/bridge-exec/src/deposit_handler/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use alpen_express_primitives::{
relay::{types::Scope, util::MessageSigner},
};
use alpen_express_rpc_api::AlpenApiClient;
use bitcoin::{secp256k1::SECP256K1, Txid};
use bitcoin::{secp256k1::SECP256K1, Transaction, Txid};
use express_bridge_sig_manager::manager::SignatureManager;
use express_bridge_tx_builder::{deposit::DepositInfo, prelude::*, TxKind};
use jsonrpsee::tokio::time::{sleep, Duration};
Expand Down Expand Up @@ -180,12 +180,175 @@ pub async fn sign_deposit_tx(
}
}

/// Add the signature to the already accumulated set of signatures for a deposit transaction and
/// produce the aggregated signature if all operators have signed. Also update the database
/// entry with the signatures accumulated so far.
//
// TODO: this method will also accept a `BridgeMessage` that holds the signature attached to a
// particular deposit info by other operators.
pub async fn aggregate_signature() -> DepositExecResult<Option<Signature>> {
unimplemented!()
/// Pools and aggregates signatures for the deposit transaction
/// into a fully-signed ready-to-be-broadcasted Bitcoin [`Transaction`].
///
/// Also broadcasts to the bridge transaction database.
///
/// # Arguments
///
/// - `txid`: a [`Txid`] that is in the bridge transaction database.
/// - `l1_rpc_client`: anything that is able to sign transactions and messages; i.e. holds private
/// keys.
/// - `l2_rpc_client`: anything that can communicate with the [`AlpenApiClient`].
/// - `sig_manager`: a stateful [`SignatureManager`].
/// - `tx_build_context`: stateful [`TxBuildContext`].
///
/// # Notes
///
/// Both the [`SignatureManager`] and the [`TxBuildContext`] can be reused
/// for multiple signing sessions if the operators'
/// [`PublickeyTable`](alpen_express_primitives::bridge::PublickeyTable)
/// does _not_ change.
///
/// We don't need mutexes since all functions to [`SignatureManager`] and
/// [`TxBuildContext`] takes non-mutable references.
pub async fn aggregate_deposit_sig(
txid: &Txid,
l1_rpc_client: &Arc<impl Signer>,
l2_rpc_client: &Arc<impl AlpenApiClient + Sync>,
sig_manager: &Arc<SignatureManager>,
tx_build_context: &Arc<TxBuildContext>,
) -> DepositExecResult<Transaction> {

Check warning on line 212 in crates/bridge-exec/src/deposit_handler/handler.rs

View check run for this annotation

Codecov / codecov/patch

crates/bridge-exec/src/deposit_handler/handler.rs#L206-L212

Added lines #L206 - L212 were not covered by tests
// setup logging
let span = span!(
Level::INFO,

Check warning on line 215 in crates/bridge-exec/src/deposit_handler/handler.rs

View check run for this annotation

Codecov / codecov/patch

crates/bridge-exec/src/deposit_handler/handler.rs#L214-L215

Added lines #L214 - L215 were not covered by tests
"starting deposit transaction signature aggregation"
);
let _guard = span.enter();

let operator_pubkeys = tx_build_context.pubkey_table();
let own_index = tx_build_context.own_index();
let own_pubkey = operator_pubkeys
.0
.get(&own_index)
.expect("could not find operator's pubkey in public key table");

event!(
Level::INFO,

Check warning on line 228 in crates/bridge-exec/src/deposit_handler/handler.rs

View check run for this annotation

Codecov / codecov/patch

crates/bridge-exec/src/deposit_handler/handler.rs#L218-L228

Added lines #L218 - L228 were not covered by tests
event = "got the basic self information",
%txid,
%own_index,
%own_pubkey,
);

let tx_state = sig_manager
.get_tx_state(txid)
.await
.map_err(|e| DepositExecError::Signing(e.to_string()))?;

Check warning on line 238 in crates/bridge-exec/src/deposit_handler/handler.rs

View check run for this annotation

Codecov / codecov/patch

crates/bridge-exec/src/deposit_handler/handler.rs#L235-L238

Added lines #L235 - L238 were not covered by tests

event!(
Level::DEBUG,

Check warning on line 241 in crates/bridge-exec/src/deposit_handler/handler.rs

View check run for this annotation

Codecov / codecov/patch

crates/bridge-exec/src/deposit_handler/handler.rs#L240-L241

Added lines #L240 - L241 were not covered by tests
event = "got transaction state from bridge database",
%txid,
?tx_state,
);

// Fully signed and in the database, nothing to do here...
if tx_state.is_fully_signed() {
event!(
Level::INFO,

Check warning on line 250 in crates/bridge-exec/src/deposit_handler/handler.rs

View check run for this annotation

Codecov / codecov/patch

crates/bridge-exec/src/deposit_handler/handler.rs#L248-L250

Added lines #L248 - L250 were not covered by tests
event = "deposit transaction fully signed and in the bridge database",
%txid,
);
let tx = sig_manager
.finalize_transaction(txid)
.await
.map_err(|e| DepositExecError::Signing(e.to_string()))?;
return Ok(tx);
}

Check warning on line 259 in crates/bridge-exec/src/deposit_handler/handler.rs

View check run for this annotation

Codecov / codecov/patch

crates/bridge-exec/src/deposit_handler/handler.rs#L254-L259

Added lines #L254 - L259 were not covered by tests

// Not fully signed, then partially sign transaction, construct, and sign a message
let xpriv = l1_rpc_client.get_xpriv().await?;
if let Some(xpriv) = xpriv {
let keypair = xpriv.to_keypair(SECP256K1);

// First check if it needs this operator's partial signature
let needs_our_sig = tx_state.collected_sigs().get(&own_index).is_none();
if needs_our_sig {
sig_manager
.add_own_partial_sig(txid)
.await
.map_err(|e| DepositExecError::Signing(e.to_string()))?;
event!(
Level::INFO,

Check warning on line 274 in crates/bridge-exec/src/deposit_handler/handler.rs

View check run for this annotation

Codecov / codecov/patch

crates/bridge-exec/src/deposit_handler/handler.rs#L262-L274

Added lines #L262 - L274 were not covered by tests
event = "added own's partial signature to the bridge transaction database",
%txid,
);
}

Check warning on line 278 in crates/bridge-exec/src/deposit_handler/handler.rs

View check run for this annotation

Codecov / codecov/patch

crates/bridge-exec/src/deposit_handler/handler.rs#L278

Added line #L278 was not covered by tests

// Now, get this operator's partial sig
let partial_sig = sig_manager
.get_own_partial_sig(txid)
.await
.map_err(|e| DepositExecError::Signing(e.to_string()))?
.expect("should've been signed");

event!(
Level::INFO,

Check warning on line 288 in crates/bridge-exec/src/deposit_handler/handler.rs

View check run for this annotation

Codecov / codecov/patch

crates/bridge-exec/src/deposit_handler/handler.rs#L281-L288

Added lines #L281 - L288 were not covered by tests
event = "got own's partial signature from the bridge transaction database",
?partial_sig,
);

// submit_message RPC call
let bitcoin_txid: BitcoinTxid = (*txid).into();
let scope = Scope::V0DepositSig(bitcoin_txid);
event!(
Level::DEBUG,

Check warning on line 297 in crates/bridge-exec/src/deposit_handler/handler.rs

View check run for this annotation

Codecov / codecov/patch

crates/bridge-exec/src/deposit_handler/handler.rs#L294-L297

Added lines #L294 - L297 were not covered by tests
event = "create the deposit partial signature scope",
?scope
);
let message = MessageSigner::new(own_index, keypair.secret_key().into())
.sign_scope(&scope, &partial_sig)
.map_err(|e| DepositExecError::Signing(e.to_string()))?;
event!(
Level::DEBUG,

Check warning on line 305 in crates/bridge-exec/src/deposit_handler/handler.rs

View check run for this annotation

Codecov / codecov/patch

crates/bridge-exec/src/deposit_handler/handler.rs#L301-L305

Added lines #L301 - L305 were not covered by tests
event = "create the deposit partial signature message",
?message,
);
let raw_message: Vec<u8> = message
.try_into()
.expect("could not serialize bridge message into raw bytes");

l2_rpc_client.submit_bridge_msg(raw_message.into()).await?;
event!(
Level::INFO,

Check warning on line 315 in crates/bridge-exec/src/deposit_handler/handler.rs

View check run for this annotation

Codecov / codecov/patch

crates/bridge-exec/src/deposit_handler/handler.rs#L309-L315

Added lines #L309 - L315 were not covered by tests
event = "broadcasted the withdrawal partial signature message",
);

// Wait for all the partial signatures to be broadcasted by other operators.
// Collect all partial signature.
loop {
event!(
Level::DEBUG,

Check warning on line 323 in crates/bridge-exec/src/deposit_handler/handler.rs

View check run for this annotation

Codecov / codecov/patch

crates/bridge-exec/src/deposit_handler/handler.rs#L322-L323

Added lines #L322 - L323 were not covered by tests
event = "trying to get all partial signatures from the bridge transaction database, waiting for other operators' signatures",
);
let got_all_sigs = sig_manager
.get_tx_state(txid)
.await
.map_err(|e| DepositExecError::Signing(e.to_string()))?
.is_fully_signed();
if got_all_sigs {
event!(
Level::INFO,

Check warning on line 333 in crates/bridge-exec/src/deposit_handler/handler.rs

View check run for this annotation

Codecov / codecov/patch

crates/bridge-exec/src/deposit_handler/handler.rs#L326-L333

Added lines #L326 - L333 were not covered by tests
event = "got all partial signatures from the bridge transaction database",
%got_all_sigs
);
break;

Check warning on line 337 in crates/bridge-exec/src/deposit_handler/handler.rs

View check run for this annotation

Codecov / codecov/patch

crates/bridge-exec/src/deposit_handler/handler.rs#L337

Added line #L337 was not covered by tests
} else {
// TODO: this is hardcoded, maybe move this to a user-configurable Config
sleep(Duration::from_millis(100)).await;
continue;

Check warning on line 341 in crates/bridge-exec/src/deposit_handler/handler.rs

View check run for this annotation

Codecov / codecov/patch

crates/bridge-exec/src/deposit_handler/handler.rs#L340-L341

Added lines #L340 - L341 were not covered by tests
}
}
let tx = sig_manager
.finalize_transaction(txid)
.await
.map_err(|e| DepositExecError::Signing(e.to_string()))?;
Ok(tx)

Check warning on line 348 in crates/bridge-exec/src/deposit_handler/handler.rs

View check run for this annotation

Codecov / codecov/patch

crates/bridge-exec/src/deposit_handler/handler.rs#L344-L348

Added lines #L344 - L348 were not covered by tests
} else {
Err(DepositExecError::Signing(
"Could not get wallet's xpriv".to_string(),
))

Check warning on line 352 in crates/bridge-exec/src/deposit_handler/handler.rs

View check run for this annotation

Codecov / codecov/patch

crates/bridge-exec/src/deposit_handler/handler.rs#L350-L352

Added lines #L350 - L352 were not covered by tests
}
}
Loading

0 comments on commit 840d444

Please sign in to comment.