diff --git a/cfd_protocol/src/lib.rs b/cfd_protocol/src/lib.rs index 152d484..77a4ec0 100644 --- a/cfd_protocol/src/lib.rs +++ b/cfd_protocol/src/lib.rs @@ -13,6 +13,7 @@ use bdk::miniscript::DescriptorTrait; use bdk::wallet::AddressIndex; use bdk::FeeRate; use itertools::Itertools; +use secp256k1_zkp::bitcoin_hashes::sha256; use secp256k1_zkp::{self, schnorrsig, EcdsaAdaptorSignature, SecretKey, Signature, SECP256K1}; use std::collections::HashMap; use std::iter::FromIterator; @@ -118,8 +119,9 @@ pub fn build_cfd_transactions( }; let cets = payouts - .iter() + .into_iter() .map(|payout| { + let message = payout.message.clone(); let cet = ContractExecutionTransaction::new( &commit_tx, payout, @@ -130,7 +132,7 @@ pub fn build_cfd_transactions( let encsig = cet.encsign(identity_sk, &oracle_params.pk, &oracle_params.nonce_pk)?; - Ok((cet.inner, encsig, payout.message)) + Ok((cet.inner, encsig, message)) }) .collect::>>() .context("cannot build and sign all cets")?; @@ -355,21 +357,19 @@ pub struct OracleParams { pub struct CfdTransactions { pub lock: PartiallySignedTransaction, pub commit: (Transaction, EcdsaAdaptorSignature), - pub cets: Vec<(Transaction, EcdsaAdaptorSignature, Message)>, + pub cets: Vec<(Transaction, EcdsaAdaptorSignature, Vec)>, pub refund: (Transaction, Signature), } -// NOTE: This is a simplification. Our use-case will not work with -// a simple enumeration of possible messages -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] pub struct Payout { - message: Message, + message: Vec, maker_amount: Amount, taker_amount: Amount, } impl Payout { - pub fn new(message: Message, maker_amount: Amount, taker_amount: Amount) -> Self { + pub fn new(message: Vec, maker_amount: Amount, taker_amount: Amount) -> Self { Self { message, maker_amount, @@ -377,7 +377,7 @@ impl Payout { } } - fn to_txouts(self, maker_address: &Address, taker_address: &Address) -> Vec { + fn into_txouts(self, maker_address: &Address, taker_address: &Address) -> Vec { let txouts = [ (self.maker_amount, maker_address), (self.taker_amount, taker_address), @@ -407,14 +407,16 @@ impl Payout { dust_limit_maker: Amount, dust_limit_taker: Amount, ) -> Result { - let mut updated = self; + let maker_amount = self.maker_amount; + let taker_amount = self.taker_amount; + let mut updated = self; match ( - self.maker_amount + maker_amount .checked_sub(fee / 2) .map(|a| a > dust_limit_maker) .unwrap_or(false), - self.taker_amount + taker_amount .checked_sub(fee / 2) .map(|a| a > dust_limit_taker) .unwrap_or(false), @@ -425,10 +427,10 @@ impl Payout { } (false, true) => { updated.maker_amount = Amount::ZERO; - updated.taker_amount = self.taker_amount - (fee + self.maker_amount); + updated.taker_amount = taker_amount - (fee + maker_amount); } (true, false) => { - updated.maker_amount = self.maker_amount - (fee + self.taker_amount); + updated.maker_amount = maker_amount - (fee + taker_amount); updated.taker_amount = Amount::ZERO; } (false, false) => bail!("Amounts are too small, could not subtract fee."), @@ -437,30 +439,6 @@ impl Payout { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum Message { - Win, - Lose, -} - -impl From for secp256k1_zkp::Message { - fn from(msg: Message) -> Self { - // TODO: Tag hash with prefix and other public data - secp256k1_zkp::Message::from_hashed_data::( - msg.to_string().as_bytes(), - ) - } -} - -impl ToString for Message { - fn to_string(&self) -> String { - match self { - Message::Win => "win".to_string(), - Message::Lose => "lose".to_string(), - } - } -} - const BIP340_MIDSTATE: [u8; 32] = [ 0x9c, 0xec, 0xba, 0x11, 0x23, 0x92, 0x53, 0x81, 0x11, 0x67, 0x91, 0x12, 0xd1, 0x62, 0x7e, 0x0f, 0x97, 0xc8, 0x75, 0x50, 0x00, 0x3c, 0xc7, 0x65, 0x90, 0xf6, 0x11, 0x64, 0x33, 0xe9, 0xb6, 0x6a, @@ -479,7 +457,7 @@ sha256t_hash_newtype!( pub fn compute_signature_point( oracle_pk: &schnorrsig::PublicKey, nonce_pk: &schnorrsig::PublicKey, - message: Message, + msg: &[u8], ) -> Result { fn schnorr_pubkey_to_pubkey(pk: &schnorrsig::PublicKey) -> Result { let mut buf = Vec::::with_capacity(33); @@ -492,7 +470,11 @@ pub fn compute_signature_point( let mut buf = Vec::::new(); buf.extend(&nonce_pk.serialize()); buf.extend(&oracle_pk.serialize()); - buf.extend(secp256k1_zkp::Message::from(message).as_ref().to_vec()); + buf.extend( + secp256k1_zkp::Message::from_hashed_data::(msg) + .as_ref() + .to_vec(), + ); BIP340Hash::hash(&buf).into_inner().to_vec() }; let mut oracle_pk = schnorr_pubkey_to_pubkey(oracle_pk)?; @@ -504,7 +486,7 @@ pub fn compute_signature_point( #[derive(Debug, Clone)] struct ContractExecutionTransaction { inner: Transaction, - message: Message, + message: Vec, sighash: SigHash, commit_descriptor: Descriptor, } @@ -516,34 +498,35 @@ impl ContractExecutionTransaction { fn new( commit_tx: &CommitTransaction, - payout: &Payout, + payout: Payout, maker_address: &Address, taker_address: &Address, relative_timelock_in_blocks: u32, ) -> Result { + let message = payout.message.clone(); + let commit_input = TxIn { previous_output: commit_tx.outpoint(), sequence: relative_timelock_in_blocks, ..Default::default() }; - let mut tx = Transaction { - version: 2, - lock_time: 0, - input: vec![commit_input], - output: payout.to_txouts(maker_address, taker_address), - }; - let mut fee = Self::SIGNED_VBYTES * SATS_PER_VBYTE; fee += commit_tx.fee() as f64; - - tx.output = payout + let output = payout .with_updated_fee( Amount::from_sat(fee as u64), maker_address.script_pubkey().dust_value(), taker_address.script_pubkey().dust_value(), )? - .to_txouts(maker_address, taker_address); + .into_txouts(maker_address, taker_address); + + let tx = Transaction { + version: 2, + lock_time: 0, + input: vec![commit_input], + output, + }; let sighash = SigHashCache::new(&tx).signature_hash( 0, @@ -554,7 +537,7 @@ impl ContractExecutionTransaction { Ok(Self { inner: tx, - message: payout.message, + message, sighash, commit_descriptor: commit_tx.descriptor(), }) @@ -566,7 +549,7 @@ impl ContractExecutionTransaction { oracle_pk: &schnorrsig::PublicKey, nonce_pk: &schnorrsig::PublicKey, ) -> Result { - let signature_point = compute_signature_point(oracle_pk, nonce_pk, self.message)?; + let signature_point = compute_signature_point(oracle_pk, nonce_pk, &self.message)?; Ok(EcdsaAdaptorSignature::encrypt( SECP256K1, @@ -882,7 +865,7 @@ mod tests { let orig_maker_amount = 1000; let orig_taker_amount = 1000; let payout = Payout::new( - Message::Win, + "win".to_string().into_bytes(), Amount::from_sat(orig_maker_amount), Amount::from_sat(orig_taker_amount), ); @@ -912,7 +895,7 @@ mod tests { let orig_maker_amount = dummy_dust_limit.as_sat() - 1; let orig_taker_amount = 1000; let payout = Payout::new( - Message::Win, + "win".to_string().into_bytes(), Amount::from_sat(orig_maker_amount), Amount::from_sat(orig_taker_amount), ); diff --git a/cfd_protocol/tests/dlc_protocol.rs b/cfd_protocol/tests/dlc_protocol.rs index c66dd50..12e1504 100644 --- a/cfd_protocol/tests/dlc_protocol.rs +++ b/cfd_protocol/tests/dlc_protocol.rs @@ -6,11 +6,12 @@ use bdk::wallet::AddressIndex; use bdk::SignOptions; use cfd_protocol::{ build_cfd_transactions, commit_descriptor, compute_signature_point, finalize_spend_transaction, - lock_descriptor, punish_transaction, spending_tx_sighash, Message, OracleParams, Payout, - PunishParams, TransactionExt, WalletExt, + lock_descriptor, punish_transaction, spending_tx_sighash, OracleParams, Payout, PunishParams, + TransactionExt, WalletExt, }; use rand::{CryptoRng, RngCore, SeedableRng}; use rand_chacha::ChaChaRng; +use secp256k1_zkp::bitcoin_hashes::sha256; use secp256k1_zkp::{schnorrsig, SecretKey, SECP256K1}; use std::collections::HashMap; @@ -28,8 +29,16 @@ fn run_cfd_protocol() { let (taker_sk, taker_pk) = make_keypair(&mut rng); let payouts = vec![ - Payout::new(Message::Win, Amount::from_btc(2.0).unwrap(), Amount::ZERO), - Payout::new(Message::Lose, Amount::ZERO, Amount::from_btc(2.0).unwrap()), + Payout::new( + "win".to_string().into_bytes(), + Amount::from_btc(2.0).unwrap(), + Amount::ZERO, + ), + Payout::new( + "lose".to_string().into_bytes(), + Amount::ZERO, + Amount::from_btc(2.0).unwrap(), + ), ]; let refund_timelock = 0; @@ -147,13 +156,13 @@ fn run_cfd_protocol() { let maker_encryption_point = compute_signature_point( &oracle.public_key(), &announcement.nonce_pk(), - maker_cet.2, + &maker_cet.2, ) .unwrap(); let taker_encryption_point = compute_signature_point( &oracle.public_key(), &announcement.nonce_pk(), - taker_cet.2, + &taker_cet.2, ) .unwrap(); @@ -280,10 +289,10 @@ fn run_cfd_protocol() { // verify cets - let attestations = [Message::Win, Message::Lose] + let attestations = ["win".as_bytes(), "lose".as_bytes()] .iter() - .map(|msg| (*msg, oracle.attest(&event, *msg))) - .collect::>(); + .map(|msg| (*msg, oracle.attest(&event, msg))) + .collect::>(); maker_cfd_txs .cets @@ -291,7 +300,7 @@ fn run_cfd_protocol() { .zip(taker_cfd_txs.cets) .try_for_each(|((cet, maker_encsig, msg), (_, taker_encsig, _))| { let oracle_sig = attestations - .get(&msg) + .get(msg.as_slice()) .expect("oracle to sign all messages in test"); let (_nonce_pk, signature_scalar) = schnorrsig_decompose(oracle_sig); @@ -460,8 +469,10 @@ impl Oracle { schnorrsig::PublicKey::from_keypair(SECP256K1, &self.key_pair) } - fn attest(&self, event: &Event, msg: Message) -> schnorrsig::Signature { - secp_utils::schnorr_sign_with_nonce(&msg.into(), &self.key_pair, &event.nonce) + fn attest(&self, event: &Event, msg: &[u8]) -> schnorrsig::Signature { + let msg = secp256k1_zkp::Message::from_hashed_data::(msg); + + secp_utils::schnorr_sign_with_nonce(&msg, &self.key_pair, &event.nonce) } } @@ -540,9 +551,7 @@ where } /// Decompose a BIP340 signature into R and s. -pub fn schnorrsig_decompose( - signature: &schnorrsig::Signature, -) -> (schnorrsig::PublicKey, SecretKey) { +fn schnorrsig_decompose(signature: &schnorrsig::Signature) -> (schnorrsig::PublicKey, SecretKey) { let bytes = signature.as_ref(); let nonce_pk = schnorrsig::PublicKey::from_slice(&bytes[0..32]).expect("R value in sig");