Browse Source
Extract the functionality into its own module, as the logic was exactly the same on both sides. I've introduced 2 new concepts: - "own" - the caller of the function, it's either taker or maker, depending on the OwnParams parameter, - "other" - the other party, the opposite role.no-contract-setup-message
Mariusz Klochowicz
3 years ago
5 changed files with 331 additions and 332 deletions
@ -0,0 +1,310 @@ |
|||||
|
use crate::model::cfd::{AsBlocks, FinalizedCfd, Order}; |
||||
|
use crate::wire::{AdaptorSignature, Msg0, Msg1, SetupMsg}; |
||||
|
use anyhow::{Context, Result}; |
||||
|
use bdk::bitcoin::secp256k1::{schnorrsig, SecretKey, Signature, SECP256K1}; |
||||
|
use bdk::bitcoin::{Amount, PublicKey, Transaction, Txid}; |
||||
|
use bdk::descriptor::Descriptor; |
||||
|
use cfd_protocol::{ |
||||
|
commit_descriptor, compute_signature_point, create_cfd_transactions, lock_descriptor, |
||||
|
spending_tx_sighash, EcdsaAdaptorSignature, PartyParams, PunishParams, |
||||
|
}; |
||||
|
use futures::Future; |
||||
|
use std::collections::HashMap; |
||||
|
use tokio::sync::mpsc; |
||||
|
|
||||
|
/// A factor to be added to the CFD order term for calculating the refund timelock.
|
||||
|
///
|
||||
|
/// The refund timelock is important in case the oracle disappears or never publishes a signature.
|
||||
|
/// Ideally, both users collaboratively settle in the refund scenario. This factor is important if
|
||||
|
/// the users do not settle collaboratively.
|
||||
|
/// `1.5` times the term as defined in CFD order should be safe in the extreme case where a user
|
||||
|
/// publishes the commit transaction right after the contract was initialized. In this case, the
|
||||
|
/// oracle still has `1.0 * cfdorder.term` time to attest and no one can publish the refund
|
||||
|
/// transaction.
|
||||
|
/// The downside is that if the oracle disappears: the users would only notice at the end
|
||||
|
/// of the cfd term. In this case the users has to wait for another `1.5` times of the
|
||||
|
/// term to get his funds back.
|
||||
|
pub const REFUND_THRESHOLD: f32 = 1.5; |
||||
|
|
||||
|
/// Given an initial set of parameters, sets up the CFD contract with the other party.
|
||||
|
/// Passing OwnParams identifies whether caller is the maker or the taker.
|
||||
|
///
|
||||
|
/// Returns the [`FinalizedCfd`] which contains the lock transaction, ready to be signed and sent to
|
||||
|
/// the other party. Signing of the lock transaction is not included in this function because we
|
||||
|
/// want the Cfd actor to own the wallet.
|
||||
|
pub fn new( |
||||
|
send_to_other: impl Fn(SetupMsg), |
||||
|
own_params: OwnParams, |
||||
|
sk: SecretKey, |
||||
|
oracle_pk: schnorrsig::PublicKey, |
||||
|
order: Order, |
||||
|
) -> ( |
||||
|
impl Future<Output = FinalizedCfd>, |
||||
|
mpsc::UnboundedSender<SetupMsg>, |
||||
|
) { |
||||
|
let (sender, mut receiver) = mpsc::unbounded_channel::<SetupMsg>(); |
||||
|
|
||||
|
let actor = async move { |
||||
|
let (rev_sk, rev_pk) = crate::keypair::new(&mut rand::thread_rng()); |
||||
|
let (publish_sk, publish_pk) = crate::keypair::new(&mut rand::thread_rng()); |
||||
|
|
||||
|
let params = { |
||||
|
let (own, own_role) = match own_params.clone() { |
||||
|
OwnParams::Maker(maker) => (maker, Role::Maker), |
||||
|
OwnParams::Taker(taker) => (taker, Role::Taker), |
||||
|
}; |
||||
|
|
||||
|
let own_punish = PunishParams { |
||||
|
revocation_pk: rev_pk, |
||||
|
publish_pk, |
||||
|
}; |
||||
|
send_to_other(SetupMsg::Msg0(Msg0::from((own.clone(), own_punish)))); |
||||
|
|
||||
|
let msg0 = receiver.recv().await.unwrap().try_into_msg0().unwrap(); |
||||
|
let (other, other_punish) = msg0.into(); |
||||
|
|
||||
|
AllParams::new(own, own_punish, other, other_punish, own_role) |
||||
|
}; |
||||
|
|
||||
|
let own_cfd_txs = create_cfd_transactions( |
||||
|
(params.maker().clone(), *params.maker_punish()), |
||||
|
(params.taker().clone(), *params.taker_punish()), |
||||
|
oracle_pk, |
||||
|
order.term.mul_f32(REFUND_THRESHOLD).as_blocks().ceil() as u32, |
||||
|
vec![], |
||||
|
sk, |
||||
|
) |
||||
|
.unwrap(); |
||||
|
|
||||
|
send_to_other(SetupMsg::Msg1(Msg1::from(own_cfd_txs.clone()))); |
||||
|
let msg1 = receiver.recv().await.unwrap().try_into_msg1().unwrap(); |
||||
|
|
||||
|
let lock_desc = lock_descriptor(params.maker().identity_pk, params.taker().identity_pk); |
||||
|
|
||||
|
let lock_amount = params.maker().lock_amount + params.taker().lock_amount; |
||||
|
|
||||
|
let commit_desc = commit_descriptor( |
||||
|
( |
||||
|
params.maker().identity_pk, |
||||
|
params.maker_punish().revocation_pk, |
||||
|
params.maker_punish().publish_pk, |
||||
|
), |
||||
|
( |
||||
|
params.taker().identity_pk, |
||||
|
params.taker_punish().revocation_pk, |
||||
|
params.taker_punish().publish_pk, |
||||
|
), |
||||
|
); |
||||
|
|
||||
|
let own_cets = own_cfd_txs.cets; |
||||
|
let commit_tx = own_cfd_txs.commit.0.clone(); |
||||
|
|
||||
|
let commit_amount = Amount::from_sat(commit_tx.output[0].value); |
||||
|
|
||||
|
verify_adaptor_signature( |
||||
|
&commit_tx, |
||||
|
&lock_desc, |
||||
|
lock_amount, |
||||
|
&msg1.commit, |
||||
|
¶ms.own_punish.publish_pk, |
||||
|
¶ms.other.identity_pk, |
||||
|
) |
||||
|
.unwrap(); |
||||
|
|
||||
|
verify_cets( |
||||
|
&oracle_pk, |
||||
|
¶ms.other, |
||||
|
&own_cets, |
||||
|
&msg1.cets, |
||||
|
&commit_desc, |
||||
|
commit_amount, |
||||
|
) |
||||
|
.unwrap(); |
||||
|
|
||||
|
let lock_tx = own_cfd_txs.lock; |
||||
|
let refund_tx = own_cfd_txs.refund.0; |
||||
|
|
||||
|
verify_signature( |
||||
|
&refund_tx, |
||||
|
&commit_desc, |
||||
|
commit_amount, |
||||
|
&msg1.refund, |
||||
|
¶ms.other.identity_pk, |
||||
|
) |
||||
|
.unwrap(); |
||||
|
|
||||
|
let mut cet_by_id = own_cets |
||||
|
.into_iter() |
||||
|
.map(|(tx, _, msg, _)| (tx.txid(), (tx, msg))) |
||||
|
.collect::<HashMap<_, _>>(); |
||||
|
|
||||
|
FinalizedCfd { |
||||
|
identity: sk, |
||||
|
revocation: rev_sk, |
||||
|
publish: publish_sk, |
||||
|
lock: lock_tx, |
||||
|
commit: (commit_tx, *msg1.commit), |
||||
|
cets: msg1 |
||||
|
.cets |
||||
|
.into_iter() |
||||
|
.map(|(txid, sig)| { |
||||
|
let (cet, msg) = cet_by_id.remove(&txid).expect("unknown CET"); |
||||
|
|
||||
|
(cet, *sig, msg) |
||||
|
}) |
||||
|
.collect::<Vec<_>>(), |
||||
|
refund: (refund_tx, msg1.refund), |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
(actor, sender) |
||||
|
} |
||||
|
|
||||
|
#[derive(Clone)] |
||||
|
#[allow(dead_code)] |
||||
|
pub enum OwnParams { |
||||
|
Maker(PartyParams), |
||||
|
Taker(PartyParams), |
||||
|
} |
||||
|
|
||||
|
/// Role of the actor's owner in the upcoming contract
|
||||
|
enum Role { |
||||
|
Maker, |
||||
|
Taker, |
||||
|
} |
||||
|
|
||||
|
/// A convenience struct for storing PartyParams and PunishParams of both
|
||||
|
/// parties and the role of the caller.
|
||||
|
struct AllParams { |
||||
|
pub own: PartyParams, |
||||
|
pub own_punish: PunishParams, |
||||
|
pub other: PartyParams, |
||||
|
pub other_punish: PunishParams, |
||||
|
pub own_role: Role, |
||||
|
} |
||||
|
|
||||
|
impl AllParams { |
||||
|
fn new( |
||||
|
own: PartyParams, |
||||
|
own_punish: PunishParams, |
||||
|
other: PartyParams, |
||||
|
other_punish: PunishParams, |
||||
|
own_role: Role, |
||||
|
) -> Self { |
||||
|
Self { |
||||
|
own, |
||||
|
own_punish, |
||||
|
other, |
||||
|
other_punish, |
||||
|
own_role, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fn maker(&self) -> &PartyParams { |
||||
|
match self.own_role { |
||||
|
Role::Maker => &self.own, |
||||
|
Role::Taker => &self.other, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fn taker(&self) -> &PartyParams { |
||||
|
match self.own_role { |
||||
|
Role::Maker => &self.other, |
||||
|
Role::Taker => &self.own, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fn maker_punish(&self) -> &PunishParams { |
||||
|
match self.own_role { |
||||
|
Role::Maker => &self.own_punish, |
||||
|
Role::Taker => &self.other_punish, |
||||
|
} |
||||
|
} |
||||
|
fn taker_punish(&self) -> &PunishParams { |
||||
|
match self.own_role { |
||||
|
Role::Maker => &self.other_punish, |
||||
|
Role::Taker => &self.own_punish, |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fn verify_cets( |
||||
|
oracle_pk: &schnorrsig::PublicKey, |
||||
|
other: &PartyParams, |
||||
|
own_cets: &[( |
||||
|
Transaction, |
||||
|
EcdsaAdaptorSignature, |
||||
|
Vec<u8>, |
||||
|
schnorrsig::PublicKey, |
||||
|
)], |
||||
|
cets: &[(Txid, AdaptorSignature)], |
||||
|
commit_desc: &Descriptor<PublicKey>, |
||||
|
commit_amount: Amount, |
||||
|
) -> Result<()> { |
||||
|
for (tx, _, msg, nonce_pk) in own_cets.iter() { |
||||
|
let other_encsig = cets |
||||
|
.iter() |
||||
|
.find_map(|(txid, encsig)| (txid == &tx.txid()).then(|| encsig)) |
||||
|
.expect("one encsig per cet, per party"); |
||||
|
|
||||
|
verify_cet_encsig( |
||||
|
tx, |
||||
|
other_encsig, |
||||
|
msg, |
||||
|
&other.identity_pk, |
||||
|
(oracle_pk, nonce_pk), |
||||
|
commit_desc, |
||||
|
commit_amount, |
||||
|
) |
||||
|
.expect("valid maker cet encsig") |
||||
|
} |
||||
|
Ok(()) |
||||
|
} |
||||
|
|
||||
|
fn verify_adaptor_signature( |
||||
|
tx: &Transaction, |
||||
|
spent_descriptor: &Descriptor<PublicKey>, |
||||
|
spent_amount: Amount, |
||||
|
encsig: &AdaptorSignature, |
||||
|
encryption_point: &PublicKey, |
||||
|
pk: &PublicKey, |
||||
|
) -> Result<()> { |
||||
|
let sighash = spending_tx_sighash(tx, spent_descriptor, spent_amount); |
||||
|
|
||||
|
encsig |
||||
|
.verify(SECP256K1, &sighash, &pk.key, &encryption_point.key) |
||||
|
.context("failed to verify encsig spend tx") |
||||
|
} |
||||
|
|
||||
|
fn verify_signature( |
||||
|
tx: &Transaction, |
||||
|
spent_descriptor: &Descriptor<PublicKey>, |
||||
|
spent_amount: Amount, |
||||
|
sig: &Signature, |
||||
|
pk: &PublicKey, |
||||
|
) -> Result<()> { |
||||
|
let sighash = spending_tx_sighash(tx, spent_descriptor, spent_amount); |
||||
|
SECP256K1.verify(&sighash, sig, &pk.key)?; |
||||
|
Ok(()) |
||||
|
} |
||||
|
|
||||
|
fn verify_cet_encsig( |
||||
|
tx: &Transaction, |
||||
|
encsig: &AdaptorSignature, |
||||
|
msg: &[u8], |
||||
|
pk: &PublicKey, |
||||
|
(oracle_pk, nonce_pk): (&schnorrsig::PublicKey, &schnorrsig::PublicKey), |
||||
|
spent_descriptor: &Descriptor<PublicKey>, |
||||
|
spent_amount: Amount, |
||||
|
) -> Result<()> { |
||||
|
let sig_point = compute_signature_point(oracle_pk, nonce_pk, msg) |
||||
|
.context("could not calculate signature point")?; |
||||
|
verify_adaptor_signature( |
||||
|
tx, |
||||
|
spent_descriptor, |
||||
|
spent_amount, |
||||
|
encsig, |
||||
|
&PublicKey::new(sig_point), |
||||
|
pk, |
||||
|
) |
||||
|
} |
Loading…
Reference in new issue