Browse Source

Handle multiple (message, nonce) pairs

no-contract-setup-message
Lucas Soriano del Pino 4 years ago
parent
commit
aedce9b4b6
No known key found for this signature in database GPG Key ID: EE611E973A1530E7
  1. 61
      cfd_protocol/src/lib.rs
  2. 155
      cfd_protocol/tests/cfds.rs
  3. 8
      daemon/src/model/cfd.rs
  4. 22
      daemon/src/setup_contract_actor.rs
  5. 5
      daemon/src/wire.rs

61
cfd_protocol/src/lib.rs

@ -1,4 +1,4 @@
pub use secp256k1_zkp::EcdsaAdaptorSignature;
pub use secp256k1_zkp;
use anyhow::{bail, Context, Result};
use bdk::bitcoin::hashes::hex::ToHex;
@ -16,7 +16,7 @@ use bdk::wallet::AddressIndex;
use bdk::FeeRate;
use itertools::Itertools;
use secp256k1_zkp::bitcoin_hashes::sha256;
use secp256k1_zkp::{self, schnorrsig, SecretKey, Signature, SECP256K1};
use secp256k1_zkp::{schnorrsig, EcdsaAdaptorSignature, SecretKey, Signature, SECP256K1};
use std::collections::HashMap;
use std::iter::FromIterator;
@ -205,19 +205,17 @@ fn build_cfds(
let cets = payouts
.into_iter()
.map(|payout| {
let nonce_pk = payout.nonce_pk;
let message = payout.message.clone();
let cet = ContractExecutionTransaction::new(
&commit_tx,
payout,
payout.clone(),
&maker_address,
&taker_address,
CET_TIMELOCK,
)?;
let encsig = cet.encsign(identity_sk, &oracle_pk, &nonce_pk)?;
let encsig = cet.encsign(identity_sk, &oracle_pk)?;
Ok((cet.inner, encsig, message, nonce_pk))
Ok((cet.inner, encsig, payout.msg_nonce_pairs))
})
.collect::<Result<Vec<_>>>()
.context("cannot build and sign all cets")?;
@ -432,35 +430,30 @@ pub struct PunishParams {
pub struct CfdTransactions {
pub lock: PartiallySignedTransaction,
pub commit: (Transaction, EcdsaAdaptorSignature),
#[allow(clippy::type_complexity)] // TODO: Introduce type
pub cets: Vec<(
Transaction,
EcdsaAdaptorSignature,
Vec<u8>,
schnorrsig::PublicKey,
Vec<(Vec<u8>, schnorrsig::PublicKey)>,
)>,
pub refund: (Transaction, Signature),
}
// TODO: We will very likely have multiple `(message, nonce_pk)` pairs
// per payout in the future
#[derive(Debug, Clone)]
pub struct Payout {
message: Vec<u8>,
nonce_pk: schnorrsig::PublicKey,
msg_nonce_pairs: Vec<(Vec<u8>, schnorrsig::PublicKey)>,
maker_amount: Amount,
taker_amount: Amount,
}
impl Payout {
pub fn new(
message: Vec<u8>,
nonce_pk: schnorrsig::PublicKey,
msg_nonce_pairs: Vec<(Vec<u8>, schnorrsig::PublicKey)>,
maker_amount: Amount,
taker_amount: Amount,
) -> Self {
Self {
message,
nonce_pk,
msg_nonce_pairs,
maker_amount,
taker_amount,
}
@ -544,7 +537,7 @@ sha256t_hash_newtype!(
/// Compute a signature point for the given oracle public key, announcement nonce public key and
/// message.
pub fn compute_signature_point(
fn compute_signature_point(
oracle_pk: &schnorrsig::PublicKey,
nonce_pk: &schnorrsig::PublicKey,
msg: &[u8],
@ -576,7 +569,7 @@ pub fn compute_signature_point(
#[derive(Debug, Clone)]
struct ContractExecutionTransaction {
inner: Transaction,
message: Vec<u8>,
msg_nonce_pairs: Vec<(Vec<u8>, schnorrsig::PublicKey)>,
sighash: SigHash,
commit_descriptor: Descriptor<PublicKey>,
}
@ -593,8 +586,7 @@ impl ContractExecutionTransaction {
taker_address: &Address,
relative_timelock_in_blocks: u32,
) -> Result<Self> {
let message = payout.message.clone();
let msg_nonce_pairs = payout.msg_nonce_pairs.clone();
let commit_input = TxIn {
previous_output: commit_tx.outpoint(),
sequence: relative_timelock_in_blocks,
@ -627,7 +619,7 @@ impl ContractExecutionTransaction {
Ok(Self {
inner: tx,
message,
msg_nonce_pairs,
sighash,
commit_descriptor: commit_tx.descriptor(),
})
@ -637,19 +629,32 @@ impl ContractExecutionTransaction {
&self,
sk: SecretKey,
oracle_pk: &schnorrsig::PublicKey,
nonce_pk: &schnorrsig::PublicKey,
) -> Result<EcdsaAdaptorSignature> {
let signature_point = compute_signature_point(oracle_pk, nonce_pk, &self.message)?;
let adaptor_point = compute_adaptor_point(oracle_pk, &self.msg_nonce_pairs)?;
Ok(EcdsaAdaptorSignature::encrypt(
SECP256K1,
&self.sighash.to_message(),
&sk,
&signature_point,
&adaptor_point,
))
}
}
pub fn compute_adaptor_point(
oracle_pk: &schnorrsig::PublicKey,
msg_nonce_pairs: &[(Vec<u8>, schnorrsig::PublicKey)],
) -> Result<secp256k1_zkp::PublicKey> {
let sig_points = msg_nonce_pairs
.iter()
.map(|(msg, nonce_pk)| compute_signature_point(oracle_pk, nonce_pk, msg))
.collect::<Result<Vec<_>>>()?;
let adaptor_point =
secp256k1_zkp::PublicKey::combine_keys(sig_points.iter().collect::<Vec<_>>().as_slice())?;
Ok(adaptor_point)
}
#[derive(Debug, Clone)]
struct RefundTransaction {
inner: Transaction,
@ -938,8 +943,7 @@ mod tests {
let orig_maker_amount = 1000;
let orig_taker_amount = 1000;
let payout = Payout::new(
b"win".to_vec(),
nonce_pk,
vec![(b"win".to_vec(), nonce_pk)],
Amount::from_sat(orig_maker_amount),
Amount::from_sat(orig_taker_amount),
);
@ -972,8 +976,7 @@ mod tests {
let orig_maker_amount = dummy_dust_limit.as_sat() - 1;
let orig_taker_amount = 1000;
let payout = Payout::new(
b"win".to_vec(),
nonce_pk,
vec![(b"win".to_vec(), nonce_pk)],
Amount::from_sat(orig_maker_amount),
Amount::from_sat(orig_taker_amount),
);

155
cfd_protocol/tests/cfds.rs

@ -8,9 +8,9 @@ use bdk::SignOptions;
use bitcoin::util::psbt::PartiallySignedTransaction;
use bitcoin::Txid;
use cfd_protocol::{
commit_descriptor, compute_signature_point, create_cfd_transactions,
finalize_spend_transaction, lock_descriptor, punish_transaction, renew_cfd_transactions,
spending_tx_sighash, CfdTransactions, Payout, PunishParams, TransactionExt, WalletExt,
commit_descriptor, compute_adaptor_point, create_cfd_transactions, finalize_spend_transaction,
lock_descriptor, punish_transaction, renew_cfd_transactions, spending_tx_sighash,
CfdTransactions, Payout, PunishParams, TransactionExt, WalletExt,
};
use rand::{CryptoRng, RngCore, SeedableRng};
use rand_chacha::ChaChaRng;
@ -32,14 +32,18 @@ fn create_cfd() {
let payouts = vec![
Payout::new(
b"win".to_vec(),
announcement.nonce_pk(),
vec![vec![0u8]; 20]
.into_iter()
.zip(announcement.nonce_pks())
.collect(),
Amount::from_btc(1.5).unwrap(),
Amount::from_btc(0.5).unwrap(),
),
Payout::new(
b"lose".to_vec(),
announcement.nonce_pk(),
vec![vec![1u8]; 20]
.into_iter()
.zip(announcement.nonce_pks())
.collect(),
Amount::ZERO,
Amount::from_btc(2.0).unwrap(),
),
@ -115,14 +119,18 @@ fn renew_cfd() {
let payouts = vec![
Payout::new(
b"win".to_vec(),
announcement.nonce_pk(),
vec![vec![0u8]; 20]
.into_iter()
.zip(announcement.nonce_pks())
.collect(),
Amount::from_btc(2.0).unwrap(),
Amount::ZERO,
),
Payout::new(
b"lose".to_vec(),
announcement.nonce_pk(),
vec![vec![1u8]; 20]
.into_iter()
.zip(announcement.nonce_pks())
.collect(),
Amount::ZERO,
Amount::from_btc(2.0).unwrap(),
),
@ -151,14 +159,18 @@ fn renew_cfd() {
let payouts = vec![
Payout::new(
b"win".to_vec(),
announcement.nonce_pk(),
vec![vec![0u8]; 20]
.into_iter()
.zip(announcement.nonce_pks())
.collect(),
Amount::from_btc(1.5).unwrap(),
Amount::from_btc(0.5).unwrap(),
),
Payout::new(
b"lose".to_vec(),
announcement.nonce_pk(),
vec![vec![1u8]; 20]
.into_iter()
.zip(announcement.nonce_pks())
.collect(),
Amount::from_btc(0.5).unwrap(),
Amount::from_btc(1.5).unwrap(),
),
@ -391,37 +403,37 @@ fn verify_cfd_sigs(
&taker_pk.key,
)
.expect("valid taker refund sig");
for (tx, _, msg, nonce_pk) in taker_cfd_txs.cets.iter() {
for (tx, _, msg_nonce_pairs) in taker_cfd_txs.cets.iter() {
let maker_encsig = maker_cfd_txs
.cets
.iter()
.find_map(|(maker_tx, encsig, _, _)| (maker_tx.txid() == tx.txid()).then(|| encsig))
.find_map(|(maker_tx, encsig, _)| (maker_tx.txid() == tx.txid()).then(|| encsig))
.expect("one encsig per cet, per party");
verify_cet_encsig(
tx,
maker_encsig,
msg,
msg_nonce_pairs,
&maker_pk.key,
(&oracle_pk, nonce_pk),
&oracle_pk,
commit_desc,
commit_amount,
)
.expect("valid maker cet encsig")
}
for (tx, _, msg, nonce_pk) in maker_cfd_txs.cets.iter() {
for (tx, _, msg_nonce_pairs) in maker_cfd_txs.cets.iter() {
let taker_encsig = taker_cfd_txs
.cets
.iter()
.find_map(|(taker_tx, encsig, _, _)| (taker_tx.txid() == tx.txid()).then(|| encsig))
.find_map(|(taker_tx, encsig, _)| (taker_tx.txid() == tx.txid()).then(|| encsig))
.expect("one encsig per cet, per party");
verify_cet_encsig(
tx,
taker_encsig,
msg,
msg_nonce_pairs,
&taker_pk.key,
(&oracle_pk, nonce_pk),
&oracle_pk,
commit_desc,
commit_amount,
)
@ -533,28 +545,42 @@ fn check_cfd_txs(
// CETs:
for (tx, _, msg, _) in maker_cfd_txs.cets.clone().into_iter() {
for (tx, _, msg_nonce_pairs) in maker_cfd_txs.cets.clone().into_iter() {
build_and_check_cet(
tx,
&oracle.attest(&event, &msg),
&oracle.attest(
&event,
msg_nonce_pairs
.iter()
.map(|(msg, _)| msg.as_slice())
.collect::<Vec<_>>()
.as_slice(),
),
taker_cfd_txs
.cets
.iter()
.map(|(tx, encsig, _, _)| (tx.txid(), *encsig)),
.map(|(tx, encsig, _)| (tx.txid(), *encsig)),
(&maker_sk, &maker_pk),
&taker_pk,
(&signed_commit_tx_maker, &commit_desc, commit_amount),
)
.expect("valid maker cet");
}
for (tx, _, msg, _) in taker_cfd_txs.cets.into_iter() {
for (tx, _, msg_nonce_pairs) in taker_cfd_txs.cets.into_iter() {
build_and_check_cet(
tx,
&oracle.attest(&event, &msg),
&oracle.attest(
&event,
msg_nonce_pairs
.iter()
.map(|(msg, _)| msg.as_slice())
.collect::<Vec<_>>()
.as_slice(),
),
maker_cfd_txs
.cets
.iter()
.map(|(tx, encsig, _, _)| (tx.txid(), *encsig)),
.map(|(tx, encsig, _)| (tx.txid(), *encsig)),
(&taker_sk, &taker_pk),
&maker_pk,
(&signed_commit_tx_maker, &commit_desc, commit_amount),
@ -592,20 +618,27 @@ fn check_cfd_txs(
fn build_and_check_cet(
cet: Transaction,
oracle_sig: &schnorrsig::Signature,
oracle_sigs: &[schnorrsig::Signature],
mut cets_other: impl Iterator<Item = (Txid, EcdsaAdaptorSignature)>,
(maker_sk, maker_pk): (&SecretKey, &PublicKey),
taker_pk: &PublicKey,
(commit_tx, commit_desc, commit_amount): (&Transaction, &Descriptor<PublicKey>, Amount),
) -> Result<()> {
let (_nonce_pk, signature_scalar) = schnorrsig_decompose(oracle_sig);
let (_nonce_pk, signature_scalar) = schnorrsig_decompose(&oracle_sigs[0]);
let mut decryption_sk = signature_scalar;
for oracle_sig in oracle_sigs[1..].iter() {
let (_nonce_pk, signature_scalar) = schnorrsig_decompose(oracle_sig);
decryption_sk.add_assign(signature_scalar.as_ref())?;
}
let taker_encsig = cets_other
.find_map(|(txid, encsig)| (txid == cet.txid()).then(|| encsig))
.expect("one encsig per cet, per party");
let signed_cet = decrypt_and_sign(
cet,
(maker_sk, maker_pk),
&signature_scalar,
&decryption_sk,
taker_pk,
&taker_encsig,
commit_desc,
@ -705,13 +738,13 @@ fn verify_spend(
fn verify_cet_encsig(
tx: &Transaction,
encsig: &EcdsaAdaptorSignature,
msg: &[u8],
msg_nonce_pairs: &[(Vec<u8>, schnorrsig::PublicKey)],
pk: &secp256k1_zkp::PublicKey,
(oracle_pk, nonce_pk): (&schnorrsig::PublicKey, &schnorrsig::PublicKey),
oracle_pk: &schnorrsig::PublicKey,
spent_descriptor: &Descriptor<PublicKey>,
spent_amount: Amount,
) -> Result<()> {
let sig_point = compute_signature_point(oracle_pk, nonce_pk, msg)
let sig_point = compute_adaptor_point(oracle_pk, msg_nonce_pairs)
.context("could not calculate signature point")?;
encverify_spend(tx, encsig, spent_descriptor, spent_amount, &sig_point, pk)
}
@ -816,10 +849,14 @@ impl Oracle {
schnorrsig::PublicKey::from_keypair(SECP256K1, &self.key_pair)
}
fn attest(&self, event: &Event, msg: &[u8]) -> schnorrsig::Signature {
let msg = secp256k1_zkp::Message::from_hashed_data::<sha256::Hash>(msg);
secp_utils::schnorr_sign_with_nonce(&msg, &self.key_pair, &event.nonce)
fn attest(&self, event: &Event, msgs: &[&[u8]]) -> Vec<schnorrsig::Signature> {
msgs.iter()
.zip(&event.nonces)
.map(|(msg, nonce)| {
let msg = secp256k1_zkp::Message::from_hashed_data::<sha256::Hash>(msg);
secp_utils::schnorr_sign_with_nonce(&msg, &self.key_pair, nonce)
})
.collect()
}
}
@ -833,14 +870,14 @@ where
(event, announcement)
}
/// Represents the oracle's commitment to a nonce that will be used to
/// sign a specific event in the future.
/// Represents the oracle's commitment to a set of nonces that will be used to
/// sign each digit of the price of BTC in USD at a specific time in the future.
struct Event {
/// Nonce.
/// Nonces.
///
/// Must remain secret.
nonce: SecretKey,
nonce_pk: schnorrsig::PublicKey,
nonces: Vec<SecretKey>,
nonce_pks: Vec<schnorrsig::PublicKey>,
}
impl Event {
@ -848,35 +885,35 @@ impl Event {
where
R: RngCore + CryptoRng,
{
let nonce = SecretKey::new(rng);
let (nonces, nonce_pks) = (0..20)
.map(|_| {
let nonce = SecretKey::new(rng);
let key_pair = schnorrsig::KeyPair::from_secret_key(SECP256K1, nonce);
let nonce_pk = schnorrsig::PublicKey::from_keypair(SECP256K1, &key_pair);
let key_pair = schnorrsig::KeyPair::from_secret_key(SECP256K1, nonce);
let nonce_pk = schnorrsig::PublicKey::from_keypair(SECP256K1, &key_pair);
(nonce, nonce_pk)
})
.unzip();
Self { nonce, nonce_pk }
Self { nonces, nonce_pks }
}
fn announcement(&self) -> Announcement {
Announcement {
nonce_pk: self.nonce_pk,
nonce_pks: self.nonce_pks.clone(),
}
}
}
/// Public message which can be used by anyone to perform a DLC
/// protocol based on a specific event.
///
/// These would normally include more information to identify the
/// specific event, but we omit this for simplicity. See:
/// https://github.com/discreetlogcontracts/dlcspecs/blob/master/Oracle.md#oracle-events
#[derive(Clone, Copy)]
#[derive(Clone)]
struct Announcement {
nonce_pk: schnorrsig::PublicKey,
nonce_pks: Vec<schnorrsig::PublicKey>,
}
impl Announcement {
fn nonce_pk(&self) -> schnorrsig::PublicKey {
self.nonce_pk
fn nonce_pks(&self) -> Vec<schnorrsig::PublicKey> {
self.nonce_pks.clone()
}
}

8
daemon/src/model/cfd.rs

@ -3,7 +3,7 @@ use anyhow::Result;
use bdk::bitcoin::secp256k1::{SecretKey, Signature};
use bdk::bitcoin::util::psbt::PartiallySignedTransaction;
use bdk::bitcoin::{Amount, Transaction};
use cfd_protocol::EcdsaAdaptorSignature;
use cfd_protocol::secp256k1_zkp::{schnorrsig, EcdsaAdaptorSignature};
use rust_decimal::Decimal;
use rust_decimal_macros::dec;
use serde::{Deserialize, Serialize};
@ -597,6 +597,10 @@ pub struct FinalizedCfd {
pub lock: PartiallySignedTransaction,
pub commit: (Transaction, EcdsaAdaptorSignature),
pub cets: Vec<(Transaction, EcdsaAdaptorSignature, Vec<u8>)>,
pub cets: Vec<(
Transaction,
EcdsaAdaptorSignature,
Vec<(Vec<u8>, schnorrsig::PublicKey)>,
)>,
pub refund: (Transaction, Signature),
}

22
daemon/src/setup_contract_actor.rs

@ -4,9 +4,10 @@ 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::secp256k1_zkp::EcdsaAdaptorSignature;
use cfd_protocol::{
commit_descriptor, compute_signature_point, create_cfd_transactions, lock_descriptor,
spending_tx_sighash, EcdsaAdaptorSignature, PartyParams, PunishParams,
commit_descriptor, compute_adaptor_point, create_cfd_transactions, lock_descriptor,
spending_tx_sighash, PartyParams, PunishParams,
};
use futures::Future;
use std::collections::HashMap;
@ -125,7 +126,7 @@ pub fn new(
let mut cet_by_id = own_cets
.into_iter()
.map(|(tx, _, msg, _)| (tx.txid(), (tx, msg)))
.map(|(tx, _, msg_nonce_pairs)| (tx.txid(), (tx, msg_nonce_pairs)))
.collect::<HashMap<_, _>>();
FinalizedCfd {
@ -224,14 +225,13 @@ fn verify_cets(
own_cets: &[(
Transaction,
EcdsaAdaptorSignature,
Vec<u8>,
schnorrsig::PublicKey,
Vec<(Vec<u8>, schnorrsig::PublicKey)>,
)],
cets: &[(Txid, EcdsaAdaptorSignature)],
commit_desc: &Descriptor<PublicKey>,
commit_amount: Amount,
) -> Result<()> {
for (tx, _, msg, nonce_pk) in own_cets.iter() {
for (tx, _, msg_nonce_pairs) in own_cets.iter() {
let other_encsig = cets
.iter()
.find_map(|(txid, encsig)| (txid == &tx.txid()).then(|| encsig))
@ -240,9 +240,9 @@ fn verify_cets(
verify_cet_encsig(
tx,
other_encsig,
msg,
&msg_nonce_pairs,
&other.identity_pk,
(oracle_pk, nonce_pk),
oracle_pk,
commit_desc,
commit_amount,
)
@ -281,13 +281,13 @@ fn verify_signature(
fn verify_cet_encsig(
tx: &Transaction,
encsig: &EcdsaAdaptorSignature,
msg: &[u8],
msg_nonce_pairs: &[(Vec<u8>, schnorrsig::PublicKey)],
pk: &PublicKey,
(oracle_pk, nonce_pk): (&schnorrsig::PublicKey, &schnorrsig::PublicKey),
oracle_pk: &schnorrsig::PublicKey,
spent_descriptor: &Descriptor<PublicKey>,
spent_amount: Amount,
) -> Result<()> {
let sig_point = compute_signature_point(oracle_pk, nonce_pk, msg)
let sig_point = compute_adaptor_point(oracle_pk, msg_nonce_pairs)
.context("could not calculate signature point")?;
verify_adaptor_signature(
tx,

5
daemon/src/wire.rs

@ -4,7 +4,8 @@ use crate::Order;
use bdk::bitcoin::secp256k1::Signature;
use bdk::bitcoin::util::psbt::PartiallySignedTransaction;
use bdk::bitcoin::{Address, Amount, PublicKey, Txid};
use cfd_protocol::{CfdTransactions, EcdsaAdaptorSignature, PartyParams, PunishParams};
use cfd_protocol::secp256k1_zkp::EcdsaAdaptorSignature;
use cfd_protocol::{CfdTransactions, PartyParams, PunishParams};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
@ -129,7 +130,7 @@ impl From<CfdTransactions> for Msg1 {
cets: txs
.cets
.into_iter()
.map(|(tx, sig, _, _)| (tx.txid(), sig))
.map(|(tx, sig, _)| (tx.txid(), sig))
.collect(),
refund: txs.refund.1,
}

Loading…
Cancel
Save