From dbc16b1ae94ad8ea604ec60c3d0bff2573bfdce8 Mon Sep 17 00:00:00 2001 From: Lucas Soriano del Pino Date: Wed, 1 Sep 2021 15:19:36 +1000 Subject: [PATCH] Verify signed CETs using bitcoinconsensus --- cfd_protocol/src/lib.rs | 114 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 105 insertions(+), 9 deletions(-) diff --git a/cfd_protocol/src/lib.rs b/cfd_protocol/src/lib.rs index f012857..b8422c1 100644 --- a/cfd_protocol/src/lib.rs +++ b/cfd_protocol/src/lib.rs @@ -50,7 +50,7 @@ mod tests { let taker_dlc_amount = Amount::ONE_BTC; let oracle = Oracle::new(&mut rng); - let (_event, announcement) = announce(&mut rng); + let (event, announcement) = announce(&mut rng); let (maker_sk, maker_pk) = make_keypair(&mut rng); let (taker_sk, taker_pk) = make_keypair(&mut rng); @@ -144,8 +144,10 @@ mod tests { }) .collect::>(); - cets.iter() - .try_for_each(|cet| { + let cets_with_maker_encsig = cets + .clone() + .into_iter() + .map(|cet| { let encsig = maker_cet_encsigs .iter() .find_map(|(txid, encsig)| (txid == &cet.txid()).then(|| encsig)) @@ -156,8 +158,11 @@ mod tests { &maker_pk, &oracle.public_key(), &announcement.nonce_pk(), - ) + )?; + + Ok((cet, *encsig)) }) + .collect::>>() .expect("valid maker cet encsigs"); let taker_cet_encsigs = cets @@ -171,8 +176,9 @@ mod tests { }) .collect::>(); - cets.iter() - .try_for_each(|cet| { + let cets_with_taker_encsig = cets + .iter() + .map(|cet| { let encsig = taker_cet_encsigs .iter() .find_map(|(txid, encsig)| (txid == &cet.txid()).then(|| encsig)) @@ -183,8 +189,11 @@ mod tests { &taker_pk, &oracle.public_key(), &announcement.nonce_pk(), - ) + )?; + + Ok((cet, *encsig)) }) + .collect::>>() .expect("valid taker cet encsigs"); let mut signed_lock_tx = lock_tx.to_psbt(); @@ -221,7 +230,58 @@ mod tests { dlc_amount.as_sat(), bitcoin::consensus::serialize(&signed_refund_tx).as_slice(), ) + .expect("valid signed refund transaction"); + + let attestations = [Message::Win, Message::Lose] + .iter() + .map(|msg| (*msg, oracle.attest(&event, *msg))) + .collect::>(); + + // TODO: Rewrite this so that it doesn't rely on the order of + // the two zipped iterators + let cets_with_sigs: Vec<( + ContractExecutionTransaction, + EcdsaAdaptorSignature, + EcdsaAdaptorSignature, + )> = cets_with_maker_encsig + .into_iter() + .zip(cets_with_taker_encsig) + .map(|((cet, maker_encsig), (_, taker_encsig))| (cet, maker_encsig, taker_encsig)) + .collect::>(); + + let signed_cets = attestations + .iter() + .map(|(oracle_msg, oracle_sig)| { + let (cet, maker_encsig, taker_encsig) = cets_with_sigs + .iter() + .find(|(cet, _, _)| &cet.message == oracle_msg) + .expect("one cet per message"); + let (_nonce_pk, signature_scalar) = schnorrsig_decompose(oracle_sig).unwrap(); + + let maker_sig = maker_encsig.decrypt(&signature_scalar)?; + let taker_sig = taker_encsig.decrypt(&signature_scalar)?; + + cet.clone() + .add_signatures((maker_pk, maker_sig), (taker_pk, taker_sig)) + }) + .collect::>>() .unwrap(); + + signed_cets + .iter() + .try_for_each(|cet| { + lock_tx + .descriptor() + .address(Network::Regtest) + .expect("can derive address from descriptor") + .script_pubkey() + .verify( + 0, + dlc_amount.as_sat(), + bitcoin::consensus::serialize(&cet).as_slice(), + ) + }) + .expect("valid signed CETs"); } const BIP340_MIDSTATE: [u8; 32] = [ @@ -272,6 +332,7 @@ mod tests { inner: Transaction, message: Message, sighash: SigHash, + lock_output_descriptor: Descriptor, } impl ContractExecutionTransaction { @@ -306,6 +367,7 @@ mod tests { inner: tx, message: payout.message, sighash, + lock_output_descriptor: lock_tx.descriptor(), } } @@ -354,6 +416,28 @@ mod tests { // TODO: Do it properly 100 } + + pub fn add_signatures( + self, + (maker_pk, maker_sig): (PublicKey, Signature), + (taker_pk, taker_sig): (PublicKey, Signature), + ) -> Result { + let satisfier = { + let mut satisfier = HashMap::with_capacity(2); + + // The order in which these are inserted doesn't matter + satisfier.insert(maker_pk, (maker_sig, SigHashType::All)); + satisfier.insert(taker_pk, (taker_sig, SigHashType::All)); + + satisfier + }; + + let mut tx_refund = self.inner; + self.lock_output_descriptor + .satisfy(&mut tx_refund.input[0], satisfier)?; + + Ok(tx_refund) + } } #[derive(Debug, Clone)] @@ -606,7 +690,7 @@ mod tests { } } - #[derive(Debug, Clone, Copy)] + #[derive(Debug, Clone, Copy, PartialEq)] enum Message { Win, Lose, @@ -648,7 +732,7 @@ mod tests { schnorrsig::PublicKey::from_keypair(SECP256K1, &self.key_pair) } - fn attest(&self, event: Event, msg: Message) -> schnorrsig::Signature { + fn attest(&self, event: &Event, msg: Message) -> schnorrsig::Signature { secp_utils::schnorr_sign_with_nonce(&msg.into(), &self.key_pair, &event.nonce) } } @@ -742,6 +826,18 @@ mod tests { (sk, pk) } + /// Decompose a BIP340 signature into R and s. + pub fn schnorrsig_decompose( + signature: &schnorrsig::Signature, + ) -> Result<(schnorrsig::PublicKey, SecretKey)> { + let bytes = signature.as_ref(); + + let nonce_pk = schnorrsig::PublicKey::from_slice(&bytes[0..32])?; + let s = SecretKey::from_slice(&bytes[32..64])?; + + Ok((nonce_pk, s)) + } + mod secp_utils { use super::*;