-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
782 additions
and
466 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,289 @@ | ||
//! Musig2 signer client | ||
use std::{future::Future, sync::Arc}; | ||
|
||
use bitcoin::{hashes::Hash, Txid}; | ||
use musig2::{ | ||
errors::{RoundContributionError, RoundFinalizeError}, | ||
secp256k1::PublicKey, | ||
AggNonce, LiftedSignature, PubNonce, | ||
}; | ||
use quinn::Connection; | ||
use secret_service_proto::v1::{ | ||
traits::{ | ||
Client, ClientError, Musig2SessionId, Musig2Signer, Musig2SignerFirstRound, | ||
Musig2SignerSecondRound, Origin, SignerIdxOutOfBounds, | ||
}, | ||
wire::{ClientMessage, ServerMessage}, | ||
}; | ||
use strata_bridge_primitives::scripts::taproot::TaprootWitness; | ||
|
||
use crate::{make_v1_req, Config}; | ||
|
||
pub struct Musig2Client { | ||
conn: Connection, | ||
config: Arc<Config>, | ||
} | ||
|
||
impl Musig2Client { | ||
pub fn new(conn: Connection, config: Arc<Config>) -> Self { | ||
Self { conn, config } | ||
} | ||
} | ||
|
||
impl Musig2Signer<Client, Musig2FirstRound> for Musig2Client { | ||
fn new_session( | ||
&self, | ||
pubkeys: Vec<PublicKey>, | ||
witness: TaprootWitness, | ||
input_txid: Txid, | ||
input_vout: u32, | ||
) -> impl Future<Output = Result<Result<Musig2FirstRound, SignerIdxOutOfBounds>, ClientError>> + Send | ||
{ | ||
async move { | ||
let msg = ClientMessage::Musig2NewSession { | ||
pubkeys: pubkeys.into_iter().map(|pk| pk.serialize()).collect(), | ||
witness: witness.into(), | ||
input_txid: input_txid.to_byte_array(), | ||
input_vout, | ||
}; | ||
let res = make_v1_req(&self.conn, msg, self.config.timeout).await?; | ||
let ServerMessage::Musig2NewSession(maybe_session_id) = res else { | ||
return Err(ClientError::WrongMessage(res)); | ||
}; | ||
|
||
Ok(match maybe_session_id { | ||
Ok(session_id) => Ok(Musig2FirstRound { | ||
session_id, | ||
connection: self.conn.clone(), | ||
config: self.config.clone(), | ||
}), | ||
Err(e) => Err(e), | ||
}) | ||
} | ||
} | ||
|
||
fn pubkey(&self) -> impl Future<Output = <Client as Origin>::Container<PublicKey>> + Send { | ||
async move { | ||
let msg = ClientMessage::Musig2Pubkey; | ||
let res = make_v1_req(&self.conn, msg, self.config.timeout).await?; | ||
let ServerMessage::Musig2Pubkey { pubkey } = res else { | ||
return Err(ClientError::WrongMessage(res)); | ||
}; | ||
|
||
PublicKey::from_slice(&pubkey).map_err(|_| ClientError::WrongMessage(res)) | ||
} | ||
} | ||
} | ||
|
||
#[derive(Clone)] | ||
pub struct Musig2FirstRound { | ||
session_id: Musig2SessionId, | ||
connection: Connection, | ||
config: Arc<Config>, | ||
} | ||
|
||
impl Musig2SignerFirstRound<Client, Musig2SecondRound> for Musig2FirstRound { | ||
fn our_nonce(&self) -> impl Future<Output = <Client as Origin>::Container<PubNonce>> + Send { | ||
async move { | ||
let msg = ClientMessage::Musig2FirstRoundOurNonce { | ||
session_id: self.session_id, | ||
}; | ||
let res = make_v1_req(&self.connection, msg, self.config.timeout).await?; | ||
let ServerMessage::Musig2FirstRoundOurNonce { our_nonce } = res else { | ||
return Err(ClientError::WrongMessage(res)); | ||
}; | ||
PubNonce::from_bytes(&our_nonce).map_err(|_| ClientError::BadData) | ||
} | ||
} | ||
|
||
fn holdouts( | ||
&self, | ||
) -> impl Future<Output = <Client as Origin>::Container<Vec<PublicKey>>> + Send { | ||
async move { | ||
let msg = ClientMessage::Musig2FirstRoundHoldouts { | ||
session_id: self.session_id, | ||
}; | ||
let res = make_v1_req(&self.connection, msg, self.config.timeout).await?; | ||
let ServerMessage::Musig2FirstRoundHoldouts { pubkeys } = res else { | ||
return Err(ClientError::WrongMessage(res)); | ||
}; | ||
pubkeys | ||
.into_iter() | ||
.map(|pk| PublicKey::from_slice(&pk)) | ||
.collect::<Result<Vec<PublicKey>, musig2::secp256k1::Error>>() | ||
.map_err(|_| ClientError::BadData) | ||
} | ||
} | ||
|
||
fn is_complete(&self) -> impl Future<Output = <Client as Origin>::Container<bool>> + Send { | ||
async move { | ||
let msg = ClientMessage::Musig2FirstRoundIsComplete { | ||
session_id: self.session_id, | ||
}; | ||
let res = make_v1_req(&self.connection, msg, self.config.timeout).await?; | ||
let ServerMessage::Musig2FirstRoundIsComplete { complete } = res else { | ||
return Err(ClientError::WrongMessage(res)); | ||
}; | ||
Ok(complete) | ||
} | ||
} | ||
|
||
fn receive_pub_nonce( | ||
&mut self, | ||
pubkey: PublicKey, | ||
pubnonce: PubNonce, | ||
) -> impl Future<Output = <Client as Origin>::Container<Result<(), RoundContributionError>>> + Send | ||
{ | ||
async move { | ||
let msg = ClientMessage::Musig2FirstRoundReceivePubNonce { | ||
session_id: self.session_id, | ||
pubkey: pubkey.serialize(), | ||
pubnonce: pubnonce.serialize(), | ||
}; | ||
let res = make_v1_req(&self.connection, msg, self.config.timeout).await?; | ||
let ServerMessage::Musig2FirstRoundReceivePubNonce(maybe_err) = res else { | ||
return Err(ClientError::WrongMessage(res)); | ||
}; | ||
Ok(maybe_err.map_or(Ok(()), Err)) | ||
} | ||
} | ||
|
||
fn finalize( | ||
self, | ||
hash: [u8; 32], | ||
) -> impl Future< | ||
Output = <Client as Origin>::Container<Result<Musig2SecondRound, RoundFinalizeError>>, | ||
> + Send { | ||
async move { | ||
let msg = ClientMessage::Musig2FirstRoundFinalize { | ||
session_id: self.session_id, | ||
digest: hash, | ||
}; | ||
let res = make_v1_req(&self.connection, msg, self.config.timeout).await?; | ||
let ServerMessage::Musig2FirstRoundFinalize(maybe_err) = res else { | ||
return Err(ClientError::WrongMessage(res)); | ||
}; | ||
Ok(match maybe_err { | ||
Some(e) => Err(e), | ||
None => Ok(Musig2SecondRound { | ||
session_id: self.session_id, | ||
connection: self.connection, | ||
config: self.config, | ||
}), | ||
}) | ||
} | ||
} | ||
} | ||
|
||
pub struct Musig2SecondRound { | ||
session_id: Musig2SessionId, | ||
connection: Connection, | ||
config: Arc<Config>, | ||
} | ||
|
||
impl Musig2SignerSecondRound<Client> for Musig2SecondRound { | ||
fn agg_nonce(&self) -> impl Future<Output = <Client as Origin>::Container<AggNonce>> + Send { | ||
async move { | ||
let msg = ClientMessage::Musig2SecondRoundAggNonce { | ||
session_id: self.session_id, | ||
}; | ||
let res = make_v1_req(&self.connection, msg, self.config.timeout).await?; | ||
let ServerMessage::Musig2SecondRoundAggNonce { nonce } = res else { | ||
return Err(ClientError::WrongMessage(res)); | ||
}; | ||
AggNonce::from_bytes(&nonce).map_err(|_| ClientError::BadData) | ||
} | ||
} | ||
|
||
fn holdouts( | ||
&self, | ||
) -> impl Future<Output = <Client as Origin>::Container<Vec<PublicKey>>> + Send { | ||
async move { | ||
let msg = ClientMessage::Musig2SecondRoundHoldouts { | ||
session_id: self.session_id, | ||
}; | ||
let res = make_v1_req(&self.connection, msg, self.config.timeout).await?; | ||
let ServerMessage::Musig2SecondRoundHoldouts { pubkeys } = res else { | ||
return Err(ClientError::WrongMessage(res)); | ||
}; | ||
pubkeys | ||
.into_iter() | ||
.map(|pk| PublicKey::from_slice(&pk)) | ||
.collect::<Result<Vec<PublicKey>, musig2::secp256k1::Error>>() | ||
.map_err(|_| ClientError::BadData) | ||
} | ||
} | ||
|
||
fn our_signature( | ||
&self, | ||
) -> impl Future<Output = <Client as Origin>::Container<musig2::PartialSignature>> + Send { | ||
async move { | ||
let msg = ClientMessage::Musig2SecondRoundOurSignature { | ||
session_id: self.session_id, | ||
}; | ||
let res = make_v1_req(&self.connection, msg, self.config.timeout).await?; | ||
let ServerMessage::Musig2SecondRoundOurSignature { sig } = res else { | ||
return Err(ClientError::WrongMessage(res)); | ||
}; | ||
musig2::PartialSignature::from_slice(&sig).map_err(|_| ClientError::BadData) | ||
} | ||
} | ||
|
||
fn is_complete(&self) -> impl Future<Output = <Client as Origin>::Container<bool>> + Send { | ||
async move { | ||
let msg = ClientMessage::Musig2SecondRoundIsComplete { | ||
session_id: self.session_id, | ||
}; | ||
let res = make_v1_req(&self.connection, msg, self.config.timeout).await?; | ||
let ServerMessage::Musig2SecondRoundIsComplete { complete } = res else { | ||
return Err(ClientError::WrongMessage(res)); | ||
}; | ||
Ok(complete) | ||
} | ||
} | ||
|
||
fn receive_signature( | ||
&mut self, | ||
pubkey: PublicKey, | ||
signature: musig2::PartialSignature, | ||
) -> impl Future<Output = <Client as Origin>::Container<Result<(), RoundContributionError>>> + Send | ||
{ | ||
async move { | ||
let msg = ClientMessage::Musig2SecondRoundReceiveSignature { | ||
session_id: self.session_id, | ||
pubkey: pubkey.serialize(), | ||
signature: signature.serialize(), | ||
}; | ||
let res = make_v1_req(&self.connection, msg, self.config.timeout).await?; | ||
let ServerMessage::Musig2SecondRoundReceiveSignature(maybe_err) = res else { | ||
return Err(ClientError::WrongMessage(res)); | ||
}; | ||
Ok(maybe_err.map_or(Ok(()), Err)) | ||
} | ||
} | ||
|
||
fn finalize( | ||
self, | ||
) -> impl Future< | ||
Output = <Client as Origin>::Container<Result<musig2::LiftedSignature, RoundFinalizeError>>, | ||
> + Send { | ||
async move { | ||
let msg = ClientMessage::Musig2SecondRoundFinalize { | ||
session_id: self.session_id, | ||
}; | ||
let res = make_v1_req(&self.connection, msg, self.config.timeout).await?; | ||
let ServerMessage::Musig2SecondRoundFinalize(res) = res else { | ||
return Err(ClientError::WrongMessage(res)); | ||
}; | ||
let res: Result<_, _> = res.into(); | ||
Ok(match res { | ||
Ok(sig) => { | ||
let sig = | ||
LiftedSignature::from_bytes(&sig).map_err(|_| ClientError::BadData)?; | ||
Ok(sig) | ||
} | ||
Err(e) => Err(e), | ||
}) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
//! Operator signer client | ||
use std::{future::Future, sync::Arc}; | ||
|
||
use musig2::secp256k1::{schnorr::Signature, PublicKey}; | ||
use quinn::Connection; | ||
use secret_service_proto::v1::{ | ||
traits::{Client, ClientError, OperatorSigner, Origin}, | ||
wire::{ClientMessage, ServerMessage}, | ||
}; | ||
|
||
use crate::{make_v1_req, Config}; | ||
|
||
pub struct OperatorClient { | ||
conn: Connection, | ||
config: Arc<Config>, | ||
} | ||
|
||
impl OperatorClient { | ||
pub fn new(conn: Connection, config: Arc<Config>) -> Self { | ||
Self { conn, config } | ||
} | ||
} | ||
|
||
impl OperatorSigner<Client> for OperatorClient { | ||
fn sign( | ||
&self, | ||
digest: &[u8; 32], | ||
) -> impl Future<Output = <Client as Origin>::Container<Signature>> + Send { | ||
async move { | ||
let msg = ClientMessage::OperatorSign { | ||
digest: digest.clone(), | ||
}; | ||
let res = make_v1_req(&self.conn, msg, self.config.timeout).await?; | ||
match res { | ||
ServerMessage::OperatorSign { sig } => { | ||
Signature::from_slice(&sig).map_err(|_| ClientError::BadData) | ||
} | ||
_ => Err(ClientError::WrongMessage(res)), | ||
} | ||
} | ||
} | ||
|
||
fn pubkey(&self) -> impl Future<Output = <Client as Origin>::Container<PublicKey>> + Send { | ||
async move { | ||
let msg = ClientMessage::OperatorPubkey; | ||
let res = make_v1_req(&self.conn, msg, self.config.timeout).await?; | ||
match res { | ||
ServerMessage::OperatorPubkey { pubkey } => { | ||
PublicKey::from_slice(&pubkey).map_err(|_| ClientError::BadData) | ||
} | ||
_ => Err(ClientError::WrongMessage(res)), | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
//! P2P signer client | ||
use std::{future::Future, sync::Arc}; | ||
|
||
use musig2::secp256k1::{schnorr::Signature, PublicKey}; | ||
use quinn::Connection; | ||
use secret_service_proto::v1::{ | ||
traits::{Client, ClientError, Origin, P2PSigner}, | ||
wire::{ClientMessage, ServerMessage}, | ||
}; | ||
|
||
use crate::{make_v1_req, Config}; | ||
|
||
pub struct P2PClient { | ||
conn: Connection, | ||
config: Arc<Config>, | ||
} | ||
|
||
impl P2PClient { | ||
pub fn new(conn: Connection, config: Arc<Config>) -> Self { | ||
Self { conn, config } | ||
} | ||
} | ||
|
||
impl P2PSigner<Client> for P2PClient { | ||
fn sign( | ||
&self, | ||
digest: &[u8; 32], | ||
) -> impl Future<Output = <Client as Origin>::Container<Signature>> + Send { | ||
async move { | ||
let msg = ClientMessage::P2PSign { | ||
digest: digest.clone(), | ||
}; | ||
let res = make_v1_req(&self.conn, msg, self.config.timeout).await?; | ||
let ServerMessage::P2PSign { sig } = res else { | ||
return Err(ClientError::WrongMessage(res)); | ||
}; | ||
Signature::from_slice(&sig).map_err(|_| ClientError::BadData) | ||
} | ||
} | ||
|
||
fn pubkey(&self) -> impl Future<Output = <Client as Origin>::Container<PublicKey>> + Send { | ||
async move { | ||
let msg = ClientMessage::P2PPubkey; | ||
let res = make_v1_req(&self.conn, msg, self.config.timeout).await?; | ||
let ServerMessage::P2PPubkey { pubkey } = res else { | ||
return Err(ClientError::WrongMessage(res)); | ||
}; | ||
PublicKey::from_slice(&pubkey).map_err(|_| ClientError::BadData) | ||
} | ||
} | ||
} |
Oops, something went wrong.