Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Verifier's Deposit Finalize handler #423

Open
wants to merge 5 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 55 additions & 5 deletions core/src/database/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,10 +279,10 @@ impl Database {
"UPDATE deposit_kickoff_generator_txs
SET cur_unused_kickoff_index = cur_unused_kickoff_index + 1
WHERE id = (
SELECT id
FROM deposit_kickoff_generator_txs
SELECT id
FROM deposit_kickoff_generator_txs
WHERE cur_unused_kickoff_index < num_kickoffs
ORDER BY id DESC
ORDER BY id DESC
LIMIT 1
)
RETURNING txid, raw_signed_tx, cur_unused_kickoff_index;", // This query returns the updated cur_unused_kickoff_index.
Expand Down Expand Up @@ -606,8 +606,8 @@ impl Database {
.push_bind(OutPointDB(deposit_outpoint))
.push(
" RETURNING nonces.internal_idx, sec_nonce, agg_nonce)
SELECT updated.sec_nonce, updated.agg_nonce
FROM updated
SELECT updated.sec_nonce, updated.agg_nonce
FROM updated
ORDER BY updated.internal_idx;",
)
.build_query_as();
Expand Down Expand Up @@ -1142,6 +1142,56 @@ impl Database {

Ok(XOnlyPublicKey::from_slice(&xonly_key.0)?)
}

/// Saves the verified deposit signatures to the database
#[tracing::instrument(skip(self, tx, signatures), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))]
pub async fn save_deposit_signatures(
&self,
tx: Option<&mut sqlx::Transaction<'_, Postgres>>,
deposit_outpoint: OutPoint,
operator_idx: u32,
signatures: Vec<schnorr::Signature>,
) -> Result<(), BridgeError> {
let query = sqlx::query(
"INSERT INTO deposit_signatures (deposit_outpoint, operator_idx, signatures) VALUES ($1, $2, $3);"
)
.bind(OutPointDB(deposit_outpoint))
.bind(operator_idx as i64)
.bind(SignaturesDB(signatures));

match tx {
Some(tx) => query.execute(&mut **tx).await?,
None => query.execute(&self.connection).await?,
};

Ok(())
}

/// Retrieves the deposit signatures from the database
#[tracing::instrument(skip(self, tx), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))]
pub async fn get_deposit_signatures(
&self,
tx: Option<&mut sqlx::Transaction<'_, Postgres>>,
deposit_outpoint: OutPoint,
operator_idx: u32,
) -> Result<Option<Vec<schnorr::Signature>>, BridgeError> {
let query = sqlx::query_as(
"SELECT signatures FROM deposit_signatures WHERE deposit_outpoint = $1 AND operator_idx = $2;"
)
.bind(OutPointDB(deposit_outpoint))
.bind(operator_idx as i64);

let result: Result<(SignaturesDB,), sqlx::Error> = match tx {
Some(tx) => query.fetch_one(&mut **tx).await,
None => query.fetch_one(&self.connection).await,
};

match result {
Ok((SignaturesDB(signatures),)) => Ok(Some(signatures)),
Err(sqlx::Error::RowNotFound) => Ok(None),
Err(e) => Err(BridgeError::DatabaseError(e)),
}
}
}

#[cfg(test)]
Expand Down
142 changes: 77 additions & 65 deletions core/src/rpc/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@ use super::clementine::{
VerifierDepositSignParams, VerifierParams, VerifierPublicKeys, WatchtowerParams,
};
use crate::{
actor::Actor,
builder::{
self,
sighash::{calculate_num_required_sigs, create_nofn_sighash_stream},
transaction::create_move_txhandler,
},
errors::BridgeError,
musig2::{self, MuSigPubNonce, MuSigSecNonce},
sha256_hash,
verifier::{NofN, NonceSession, Verifier},
ByteArray32, ByteArray66, EVMAddress,
};
use bitcoin::{hashes::Hash, Amount, TapSighash, Txid};
use bitcoin::{address::NetworkUnchecked, hashes::Hash, Amount, TapSighash, Txid};
use bitvm::{
bridge::transactions::signing_winternitz::WinternitzPublicKey, signatures::winternitz,
};
Expand All @@ -25,6 +27,42 @@ use tokio::sync::mpsc;
use tokio_stream::wrappers::ReceiverStream;
use tonic::{async_trait, Request, Response, Status, Streaming};

fn get_deposit_params(
deposit_sign_session: clementine::DepositSignSession,
verifier_idx: usize,
) -> Result<
(
bitcoin::OutPoint,
EVMAddress,
bitcoin::Address<NetworkUnchecked>,
u64,
u32,
),
Status,
> {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe even TryFrom<(DepositSignSession, usize)> for (...result tuple...)

But usize wouldn't be clear that it's actually a verifier_idx.

I was thinking of doing some impls on Prost-generated types for ease of use and code quality, I think we can attempt to do it after the gRPC API stabilizes further

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, maybe we can create an issue for that to not forget?

let deposit_params = deposit_sign_session
.deposit_params
.ok_or(Status::internal("No deposit outpoint received"))?;
let deposit_outpoint: bitcoin::OutPoint = deposit_params
.deposit_outpoint
.ok_or(Status::internal("No deposit outpoint received"))?
.try_into()?;
let evm_address: EVMAddress = deposit_params.evm_address.try_into().unwrap();
let recovery_taproot_address = deposit_params
.recovery_taproot_address
.parse::<bitcoin::Address<_>>()
.map_err(|e| Status::internal(e.to_string()))?;
let user_takes_after = deposit_params.user_takes_after;

let session_id = deposit_sign_session.nonce_gen_first_responses[verifier_idx].id;
Ok((
deposit_outpoint,
evm_address,
recovery_taproot_address,
user_takes_after,
session_id,
))
}
#[async_trait]
impl ClementineVerifier for Verifier {
type NonceGenStream = ReceiverStream<Result<NonceGenResponse, Status>>;
Expand Down Expand Up @@ -324,43 +362,16 @@ impl ClementineVerifier for Verifier {
session_id,
) = match params {
clementine::verifier_deposit_sign_params::Params::DepositSignFirstParam(
deposit_sign_first_param,
) => {
let deposit_params = deposit_sign_first_param
.deposit_params
.ok_or(Status::internal("No deposit outpoint received"))
.unwrap();
let deposit_outpoint: bitcoin::OutPoint = deposit_params
.deposit_outpoint
.ok_or(Status::internal("No deposit outpoint received"))
.unwrap()
.try_into()
.unwrap();
let evm_address: EVMAddress = deposit_params.evm_address.try_into().unwrap();
let recovery_taproot_address = deposit_params
.recovery_taproot_address
.parse::<bitcoin::Address<_>>()
.unwrap();
let user_takes_after = deposit_params.user_takes_after;

let session_id =
deposit_sign_first_param.nonce_gen_first_responses[verifier.idx].id;
(
deposit_outpoint,
evm_address,
recovery_taproot_address,
user_takes_after,
session_id,
)
}
deposit_sign_session,
) => get_deposit_params(deposit_sign_session, verifier.idx).unwrap(),
_ => panic!("Expected DepositOutpoint"),
};

let binding = verifier.nonces.lock().await;
let session = binding
let session_map = verifier.nonces.lock().await;
let session = session_map
.sessions
.get(&session_id)
.ok_or(Status::internal("No session found"))
.ok_or_else(|| Status::internal(format!("Could not find session id {session_id}")))
.unwrap();
let mut nonce_idx: usize = 0;

Expand Down Expand Up @@ -442,42 +453,19 @@ impl ClementineVerifier for Verifier {
.ok_or(Status::internal("No first message received"))?;

// Parse the first message
let params = first_message
.params
.ok_or(Status::internal("No deposit outpoint received"))?;
let (
deposit_outpoint,
evm_address,
recovery_taproot_address,
user_takes_after,
_session_id,
) = match params {
) = match first_message
.params
.ok_or(Status::internal("No deposit outpoint received"))?
{
clementine::verifier_deposit_finalize_params::Params::DepositSignFirstParam(
deposit_sign_first_param,
) => {
let deposit_params = deposit_sign_first_param
.deposit_params
.ok_or(Status::internal("No deposit outpoint received"))?;
let deposit_outpoint: bitcoin::OutPoint = deposit_params
.deposit_outpoint
.ok_or(Status::internal("No deposit outpoint received"))?
.try_into()?;
let evm_address: EVMAddress = deposit_params.evm_address.try_into().unwrap();
let recovery_taproot_address = deposit_params
.recovery_taproot_address
.parse::<bitcoin::Address<_>>()
.map_err(|e| BridgeError::Error(e.to_string()))?;
let user_takes_after = deposit_params.user_takes_after;

let session_id = deposit_sign_first_param.nonce_gen_first_responses[self.idx].id;
(
deposit_outpoint,
evm_address,
recovery_taproot_address,
user_takes_after,
session_id,
)
}
deposit_sign_session,
) => get_deposit_params(deposit_sign_session, self.idx)?,
_ => panic!("Expected DepositOutpoint"),
};

Expand All @@ -486,7 +474,7 @@ impl ClementineVerifier for Verifier {
self.config.clone(),
deposit_outpoint,
evm_address,
recovery_taproot_address,
recovery_taproot_address.clone(),
self.nofn_xonly_pk,
user_takes_after,
Amount::from_sat(200_000_000), // TODO: Fix this.
Expand All @@ -495,12 +483,16 @@ impl ClementineVerifier for Verifier {
self.config.bridge_amount_sats,
self.config.network,
));

let num_required_sigs = calculate_num_required_sigs(
self.config.num_operators,
self.config.num_time_txs,
self.config.num_watchtowers,
);

let mut verified_sigs = Vec::with_capacity(num_required_sigs);
let mut nonce_idx: usize = 0;

while let Some(result) = in_stream.message().await.unwrap() {
let sighash = sighash_stream.next().await.unwrap().unwrap();
let final_sig = result
Expand All @@ -523,6 +515,7 @@ impl ClementineVerifier for Verifier {
)
.unwrap();

verified_sigs.push(final_sig);
tracing::debug!("Final Signature Verified");

nonce_idx += 1;
Expand All @@ -531,10 +524,29 @@ impl ClementineVerifier for Verifier {
}
}

tracing::info!("Deposit finalized");
// Save signatures to database
self.db
mmtftr marked this conversation as resolved.
Show resolved Hide resolved
.save_deposit_signatures(None, deposit_outpoint, 0, verified_sigs)
.await?;

// Generate partial signature for move transaction
let mut move_txhandler = create_move_txhandler(
deposit_outpoint,
evm_address,
&recovery_taproot_address,
self.nofn_xonly_pk,
u32::try_from(user_takes_after).expect("User takes after is too large"),
mmtftr marked this conversation as resolved.
Show resolved Hide resolved
self.config.bridge_amount_sats,
self.config.network,
);

let move_tx_sighash = Actor::convert_tx_to_sighash_script_spend(&mut move_txhandler, 0, 0)?;

let partial_sig = self.signer.sign(move_tx_sighash);
mmtftr marked this conversation as resolved.
Show resolved Hide resolved

tracing::info!("Deposit finalized");
Ok(Response::new(PartialSig {
partial_sig: vec![1, 2],
partial_sig: partial_sig.serialize().to_vec(),
}))
}
}
16 changes: 12 additions & 4 deletions scripts/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ create table if not exists deposit_infos (

-- Verifier table for nonces related to deposits
/* This table holds the public, secret, and aggregated nonces related to a deposit.
For each deposit, we have (2 + num_operators) nonce triples. The first triple is for
For each deposit, we have (2 + num_operators) nonce triples. The first triple is for
move_commit_tx, the second triple is for move_reveal_tx, and the rest is for operator_takes_tx
for each operator. Also for each triple, we hold the sig_hash to be signed to prevent reuse
of the nonces. */
of the nonces. */
create table if not exists nonces (
deposit_outpoint text not null check (deposit_outpoint ~ '^[a-fA-F0-9]{64}:(0|[1-9][0-9]{0,9})$'),
internal_idx int not null,
Expand Down Expand Up @@ -71,8 +71,8 @@ $$ LANGUAGE plpgsql;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM pg_trigger
SELECT 1
FROM pg_trigger
WHERE tgname = 'prevent_sighash_update_trigger'
) THEN
CREATE TRIGGER prevent_sighash_update_trigger
Expand Down Expand Up @@ -152,4 +152,12 @@ create table if not exists operator_winternitz_public_keys (
primary key (operator_id)
);

-- Deposit signatures
create table if not exists deposit_signatures (
deposit_outpoint text not null check (deposit_outpoint ~ '^[a-fA-F0-9]{64}:(0|[1-9][0-9]{0,9})$'),
operator_idx int not null,
signatures bytea not null,
primary key (deposit_outpoint, operator_idx)
);

COMMIT;
Loading