Browse Source

Refactor cfd_protocol crate

no-contract-setup-message
Lucas Soriano del Pino 3 years ago
parent
commit
4fdbe89898
No known key found for this signature in database GPG Key ID: EE611E973A1530E7
  1. 1
      Cargo.lock
  2. 1
      cfd_protocol/Cargo.toml
  3. 106
      cfd_protocol/src/interval.rs
  4. 2
      cfd_protocol/src/interval/digit_decomposition.rs
  5. 3
      cfd_protocol/src/lib.rs
  6. 56
      cfd_protocol/src/protocol.rs
  7. 8
      cfd_protocol/src/protocol/transactions.rs
  8. 224
      cfd_protocol/tests/cfds.rs
  9. 10
      daemon/src/model/cfd.rs
  10. 47
      daemon/src/setup_contract_actor.rs
  11. 7
      daemon/src/wire.rs

1
Cargo.lock

@ -268,6 +268,7 @@ dependencies = [
"proptest",
"rand 0.6.5",
"secp256k1-zkp",
"thiserror",
]
[[package]]

1
cfd_protocol/Cargo.toml

@ -10,6 +10,7 @@ bit-vec = "0.6"
itertools = "0.10"
rand = "0.6"
secp256k1-zkp = { git = "https://github.com/ElementsProject/rust-secp256k1-zkp", features = ["bitcoin_hashes", "global-context", "serde"] }
thiserror = "1"
[dev-dependencies]
bitcoin = { version = "0.27", features = ["rand", "bitcoinconsensus"] }

106
cfd_protocol/src/interval.rs

@ -1,37 +1,34 @@
use anyhow::{bail, Result};
use bit_vec::BitVec;
use std::fmt::Display;
use std::ops::RangeInclusive;
mod digit_decomposition;
const BASE: usize = 2;
/// Maximum supported BTC price in whole USD.
const MAX_PRICE_DEC: u64 = (BASE as u64).pow(MAX_DIGITS as u32);
/// Maximum number of binary digits for BTC price in whole USD.
const MAX_DIGITS: usize = 20;
const MAX_PRICE_DEC: u64 = (BASE as u64).pow(MAX_DIGITS as u32);
const BASE: usize = 2;
#[derive(Debug, Clone)]
pub struct Interval(RangeInclusive<u64>);
/// Binary representation of a price interval.
#[derive(Clone, Debug)]
pub struct Digits(BitVec);
impl Interval {
pub fn new(start: u64, end: u64) -> Result<Self> {
impl Digits {
pub fn new(start: u64, end: u64) -> Result<Vec<Self>, Error> {
if start > MAX_PRICE_DEC || end > MAX_PRICE_DEC {
bail!("price over maximum")
return Err(Error::RangeOverMax);
}
if start > end {
bail!("invalid interval: start > end")
}
Ok(Self(start..=end))
return Err(Error::DecreasingRange);
}
pub fn as_digits(&self) -> Vec<Digits> {
digit_decomposition::group_by_ignoring_digits(
*self.0.start() as usize,
*self.0.end() as usize,
let digits = digit_decomposition::group_by_ignoring_digits(
start as usize,
end as usize,
BASE,
MAX_DIGITS,
)
@ -40,20 +37,15 @@ impl Interval {
let digits = digits.iter().map(|n| *n != 0).collect::<BitVec>();
Digits(digits)
})
.collect()
}
}
.collect();
impl From<Interval> for RangeInclusive<u64> {
fn from(interval: Interval) -> Self {
interval.0
Ok(digits)
}
}
#[derive(Clone, Debug)]
pub struct Digits(BitVec);
impl Digits {
/// Calculate the range of prices expressed by these digits.
///
/// With the resulting range one can assess wether a particular
/// price corresponds to the described interval.
pub fn range(&self) -> RangeInclusive<u64> {
let missing_bits = MAX_DIGITS - self.0.len();
@ -68,26 +60,31 @@ impl Digits {
start..=end
}
/// Convert underlying bit representation into bytes.
///
/// Useful for signing each digit.
pub fn to_bytes(&self) -> Vec<Vec<u8>> {
self.0
.iter()
.map(|bit| vec![if bit { 1u8 } else { 0u8 }])
.collect()
}
}
trait BitVecExt {
fn as_u64(&self) -> u64;
pub fn len(&self) -> usize {
self.0.len()
}
impl BitVecExt for BitVec {
fn as_u64(&self) -> u64 {
let len = self.len();
self.iter().enumerate().fold(0, |acc, (i, x)| {
acc + ((x as u64) * (BASE.pow((len - i - 1) as u32) as u64))
})
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Interval would generate values over maximum price of {MAX_PRICE_DEC}.")]
RangeOverMax,
#[error("Invalid decreasing interval.")]
DecreasingRange,
}
impl Display for Digits {
@ -100,11 +97,42 @@ impl Display for Digits {
}
}
trait BitVecExt {
fn as_u64(&self) -> u64;
}
impl BitVecExt for BitVec {
fn as_u64(&self) -> u64 {
let len = self.len();
self.iter().enumerate().fold(0, |acc, (i, x)| {
acc + ((x as u64) * (BASE.pow((len - i - 1) as u32) as u64))
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use anyhow::Result;
use proptest::prelude::*;
#[derive(Debug, Clone)]
struct Interval(RangeInclusive<u64>);
impl Interval {
fn new(range: RangeInclusive<u64>) -> Self {
Self(range)
}
fn to_digits(&self) -> Result<Vec<Digits>> {
let digits = Digits::new(*self.0.start(), *self.0.end())?;
Ok(digits)
}
}
impl PartialEq<Vec<Digits>> for Interval {
fn eq(&self, other: &Vec<Digits>) -> bool {
let sub_intervals = other.iter().flat_map(|i| i.range());
@ -116,14 +144,14 @@ mod tests {
fn interval()(x in 0u64..=MAX_PRICE_DEC, y in 0u64..=MAX_PRICE_DEC) -> Interval {
let (start, end) = if x < y { (x, y) } else { (y, x) };
Interval::new(start, end).unwrap()
Interval::new(start..=end)
}
}
proptest! {
#[test]
fn interval_equal_to_sum_of_sub_intervals_described_by_digits(interval in interval()) {
prop_assert!(interval == interval.as_digits())
prop_assert!(interval == interval.to_digits().unwrap())
}
}
}

2
cfd_protocol/src/interval/digit_decomposition.rs

@ -122,7 +122,7 @@ fn middle_grouping(first_digit_start: usize, first_digit_end: usize) -> Vec<Vec<
}
/// Returns the set of decomposed prefixes that cover the range [start, end].
pub(crate) fn group_by_ignoring_digits(
pub(super) fn group_by_ignoring_digits(
start: usize,
end: usize,
base: usize,

3
cfd_protocol/src/lib.rs

@ -1,7 +1,8 @@
mod interval;
mod oracle;
mod protocol;
pub mod interval;
pub use oracle::{attest, nonce};
pub use protocol::{
commit_descriptor, compute_adaptor_point, create_cfd_transactions, finalize_spend_transaction,

56
cfd_protocol/src/protocol.rs

@ -1,4 +1,7 @@
use crate::interval::Interval;
pub use transaction_ext::TransactionExt;
pub use transactions::punish_transaction;
use crate::interval;
use crate::protocol::sighash_ext::SigHashExt;
use crate::protocol::transactions::{
lock_transaction, CommitTransaction, ContractExecutionTransaction as ContractExecutionTx,
@ -20,13 +23,11 @@ use itertools::Itertools;
use secp256k1_zkp::{self, schnorrsig, EcdsaAdaptorSignature, SecretKey, Signature, SECP256K1};
use std::collections::HashMap;
use std::iter::FromIterator;
mod sighash_ext;
mod transaction_ext;
mod transactions;
pub use transaction_ext::TransactionExt;
pub use transactions::punish_transaction;
/// Static script to be used to create lock tx
const DUMMY_2OF2_MULTISIG: &str =
"0020b5aa99ed7e0fa92483eb045ab8b7a59146d4d9f6653f21ba729b4331895a5b46";
@ -62,7 +63,7 @@ where
pub fn create_cfd_transactions(
(maker, maker_punish_params): (PartyParams, PunishParams),
(taker, taker_punish_params): (PartyParams, PunishParams),
oracle_pk: schnorrsig::PublicKey,
(oracle_pk, nonce_pks): (schnorrsig::PublicKey, &[schnorrsig::PublicKey]),
refund_timelock: u32,
payouts: Vec<Payout>,
identity_sk: SecretKey,
@ -89,7 +90,7 @@ pub fn create_cfd_transactions(
taker.address,
taker_punish_params,
),
oracle_pk,
(oracle_pk, nonce_pks),
refund_timelock,
payouts,
identity_sk,
@ -110,7 +111,7 @@ pub fn renew_cfd_transactions(
Address,
PunishParams,
),
oracle_pk: schnorrsig::PublicKey,
(oracle_pk, nonce_pks): (schnorrsig::PublicKey, &[schnorrsig::PublicKey]),
refund_timelock: u32,
payouts: Vec<Payout>,
identity_sk: SecretKey,
@ -129,7 +130,7 @@ pub fn renew_cfd_transactions(
taker_address,
taker_punish_params,
),
oracle_pk,
(oracle_pk, nonce_pks),
refund_timelock,
payouts,
identity_sk,
@ -150,7 +151,7 @@ fn build_cfds(
Address,
PunishParams,
),
oracle_pk: schnorrsig::PublicKey,
(oracle_pk, nonce_pks): (schnorrsig::PublicKey, &[schnorrsig::PublicKey]),
refund_timelock: u32,
payouts: Vec<Payout>,
identity_sk: SecretKey,
@ -210,12 +211,13 @@ fn build_cfds(
payout.clone(),
&maker_address,
&taker_address,
nonce_pks,
CET_TIMELOCK,
)?;
let encsig = cet.encsign(identity_sk, &oracle_pk)?;
Ok((cet.into_inner(), encsig, payout.msg_nonce_pairs))
Ok((cet.into_inner(), encsig, payout.digits))
})
.collect::<Result<Vec<_>>>()
.context("cannot build and sign all cets")?;
@ -327,18 +329,13 @@ 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<(Vec<u8>, schnorrsig::PublicKey)>,
)>,
pub cets: Vec<(Transaction, EcdsaAdaptorSignature, interval::Digits)>,
pub refund: (Transaction, Signature),
}
#[derive(Debug, Clone)]
pub struct Payout {
msg_nonce_pairs: Vec<(Vec<u8>, schnorrsig::PublicKey)>,
digits: interval::Digits,
maker_amount: Amount,
taker_amount: Amount,
}
@ -347,25 +344,16 @@ impl Payout {
pub fn new(
start: u64,
end: u64,
nonce_pks: Vec<schnorrsig::PublicKey>,
maker_amount: Amount,
taker_amount: Amount,
) -> Result<Vec<Self>> {
let interval = Interval::new(start, end).context("invalid interval")?;
Ok(interval
.as_digits()
let digits = interval::Digits::new(start, end).context("invalid interval")?;
Ok(digits
.into_iter()
.map(|digits| {
let msg_nonce_pairs = digits
.to_bytes()
.into_iter()
.zip(nonce_pks.clone())
.collect();
Self {
msg_nonce_pairs,
.map(|digits| Self {
digits,
maker_amount,
taker_amount,
}
})
.collect())
}
@ -477,9 +465,6 @@ mod tests {
#[test]
fn test_fee_subtraction_bigger_than_dust() {
let nonce_pk = "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166"
.parse()
.unwrap();
let key = "032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af"
.parse()
.unwrap();
@ -491,7 +476,6 @@ mod tests {
let payouts = Payout::new(
0,
10_000,
vec![nonce_pk; 20],
Amount::from_sat(orig_maker_amount),
Amount::from_sat(orig_taker_amount),
)
@ -516,9 +500,6 @@ mod tests {
#[test]
fn test_fee_subtraction_smaller_than_dust() {
let nonce_pk = "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166"
.parse()
.unwrap();
let key = "032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af"
.parse()
.unwrap();
@ -530,7 +511,6 @@ mod tests {
let payouts = Payout::new(
0,
10_000,
vec![nonce_pk; 20],
Amount::from_sat(orig_maker_amount),
Amount::from_sat(orig_taker_amount),
)

8
cfd_protocol/src/protocol/transactions.rs

@ -206,9 +206,15 @@ impl ContractExecutionTransaction {
payout: Payout,
maker_address: &Address,
taker_address: &Address,
nonce_pks: &[schnorrsig::PublicKey],
relative_timelock_in_blocks: u32,
) -> Result<Self> {
let msg_nonce_pairs = payout.msg_nonce_pairs.clone();
let msg_nonce_pairs = payout
.digits
.to_bytes()
.into_iter()
.zip(nonce_pks.iter().cloned())
.collect();
let commit_input = TxIn {
previous_output: commit_tx.outpoint(),
sequence: relative_timelock_in_blocks,

224
cfd_protocol/tests/cfds.rs

@ -9,8 +9,9 @@ use bit_vec::BitVec;
use bitcoin::util::psbt::PartiallySignedTransaction;
use cfd_protocol::{
attest, commit_descriptor, compute_adaptor_point, create_cfd_transactions,
finalize_spend_transaction, lock_descriptor, nonce, punish_transaction, renew_cfd_transactions,
spending_tx_sighash, CfdTransactions, Payout, PunishParams, TransactionExt, WalletExt,
finalize_spend_transaction, interval, lock_descriptor, nonce, punish_transaction,
renew_cfd_transactions, spending_tx_sighash, CfdTransactions, Payout, PunishParams,
TransactionExt, WalletExt,
};
use rand::{thread_rng, CryptoRng, Rng, RngCore};
use secp256k1_zkp::{schnorrsig, EcdsaAdaptorSignature, SecretKey, Signature, SECP256K1};
@ -33,19 +34,11 @@ fn create_cfd() {
Payout::new(
0,
10_000,
announcement.nonce_pks(),
Amount::from_btc(1.5).unwrap(),
Amount::from_btc(0.5).unwrap(),
)
.unwrap(),
Payout::new(
10,
20_000,
announcement.nonce_pks(),
Amount::ZERO,
Amount::from_btc(2.0).unwrap(),
)
.unwrap(),
Payout::new(10_001, 20_000, Amount::ZERO, Amount::from_btc(2.0).unwrap()).unwrap(),
]
.concat();
@ -55,7 +48,7 @@ fn create_cfd() {
&mut rng,
(&maker_wallet, maker_lock_amount),
(&taker_wallet, taker_lock_amount),
oracle.public_key(),
(oracle.public_key(), &announcement.nonce_pks()),
payouts,
refund_timelock,
);
@ -72,12 +65,13 @@ fn create_cfd() {
verify_cfd_sigs(
(&maker_cfd_txs, maker.pk, maker.pub_pk),
(&taker_cfd_txs, taker.pk, taker.pub_pk),
oracle.public_key(),
(oracle.public_key(), &announcement.nonce_pks()),
(&lock_desc, lock_amount),
(&commit_desc, commit_amount),
);
check_cfd_txs(
&mut rng,
(
maker_wallet,
maker_cfd_txs,
@ -118,22 +112,8 @@ fn renew_cfd() {
let (_event, announcement) = announce(&mut rng);
let payouts = vec![
Payout::new(
0,
10_000,
announcement.nonce_pks(),
Amount::from_btc(2.0).unwrap(),
Amount::ZERO,
)
.unwrap(),
Payout::new(
10,
20_000,
announcement.nonce_pks(),
Amount::ZERO,
Amount::from_btc(2.0).unwrap(),
)
.unwrap(),
Payout::new(0, 10_000, Amount::from_btc(2.0).unwrap(), Amount::ZERO).unwrap(),
Payout::new(10_001, 20_000, Amount::ZERO, Amount::from_btc(2.0).unwrap()).unwrap(),
]
.concat();
@ -143,7 +123,7 @@ fn renew_cfd() {
&mut rng,
(&maker_wallet, maker_lock_amount),
(&taker_wallet, taker_lock_amount),
oracle.public_key(),
(oracle.public_key(), &announcement.nonce_pks()),
payouts,
refund_timelock,
);
@ -162,15 +142,13 @@ fn renew_cfd() {
Payout::new(
0,
10_000,
announcement.nonce_pks(),
Amount::from_btc(1.5).unwrap(),
Amount::from_btc(0.5).unwrap(),
)
.unwrap(),
Payout::new(
10,
10_001,
20_000,
announcement.nonce_pks(),
Amount::from_btc(0.5).unwrap(),
Amount::from_btc(1.5).unwrap(),
)
@ -198,7 +176,7 @@ fn renew_cfd() {
publish_pk: taker_pub_pk,
},
),
oracle.public_key(),
(oracle.public_key(), &announcement.nonce_pks()),
refund_timelock,
payouts.clone(),
maker.sk,
@ -225,7 +203,7 @@ fn renew_cfd() {
publish_pk: taker_pub_pk,
},
),
oracle.public_key(),
(oracle.public_key(), &announcement.nonce_pks()),
refund_timelock,
payouts,
taker.sk,
@ -244,12 +222,13 @@ fn renew_cfd() {
verify_cfd_sigs(
(&maker_cfd_txs, maker.pk, maker_pub_pk),
(&taker_cfd_txs, taker.pk, taker_pub_pk),
oracle.public_key(),
(oracle.public_key(), &announcement.nonce_pks()),
(&lock_desc, lock_amount),
(&commit_desc, commit_amount),
);
check_cfd_txs(
&mut rng,
(
maker_wallet,
maker_cfd_txs,
@ -276,74 +255,11 @@ fn renew_cfd() {
)
}
#[test]
fn cet_unlocked_with_oracle_sig_on_price_in_interval() {
let mut rng = thread_rng();
let maker_lock_amount = Amount::ONE_BTC;
let taker_lock_amount = Amount::ONE_BTC;
let maker_wallet = build_wallet(&mut rng, Amount::from_btc(0.4).unwrap(), 5).unwrap();
let taker_wallet = build_wallet(&mut rng, Amount::from_btc(0.4).unwrap(), 5).unwrap();
let oracle = Oracle::new(&mut rng);
let (event, announcement) = announce(&mut rng);
let interval_start = 5_000;
let interval_end = 10_000;
let payouts = Payout::new(
interval_start,
interval_end,
announcement.nonce_pks(),
Amount::from_btc(1.5).unwrap(),
Amount::from_btc(0.5).unwrap(),
)
.unwrap();
let refund_timelock = 0;
let (maker_cfd_txs, taker_cfd_txs, maker, taker, _maker_addr, _taker_addr) = create_cfd_txs(
&mut rng,
(&maker_wallet, maker_lock_amount),
(&taker_wallet, taker_lock_amount),
oracle.public_key(),
payouts,
refund_timelock,
);
let commit_desc = commit_descriptor(
(maker.pk, maker.rev_pk, maker.pub_pk),
(taker.pk, taker.rev_pk, taker.pub_pk),
);
let commit_amount = Amount::from_sat(maker_cfd_txs.commit.0.output[0].value);
let price = rng.gen_range(interval_start, interval_end);
let oracle_sigs = oracle.attest_price(price, &event.nonces.try_into().unwrap());
maker_cfd_txs
.cets
.iter()
.find(|(tx, _, msg_nonce_pairs)| {
build_and_check_cet(
tx.clone(),
msg_nonce_pairs,
&taker_cfd_txs.cets,
(&maker.sk, &maker.pk),
&taker.pk,
&oracle_sigs,
(&maker_cfd_txs.commit.0, &commit_desc, commit_amount),
)
.is_ok()
})
.expect("to build one valid signed CET with price signatures");
}
fn create_cfd_txs<R>(
rng: &mut R,
(maker_wallet, maker_lock_amount): (&bdk::Wallet<(), bdk::database::MemoryDatabase>, Amount),
(taker_wallet, taker_lock_amount): (&bdk::Wallet<(), bdk::database::MemoryDatabase>, Amount),
oracle_pk: schnorrsig::PublicKey,
(oracle_pk, nonce_pks): (schnorrsig::PublicKey, &[schnorrsig::PublicKey]),
payouts: Vec<Payout>,
refund_timelock: u32,
) -> (
@ -388,7 +304,7 @@ where
publish_pk: taker_pub_pk,
},
),
oracle_pk,
(oracle_pk, nonce_pks),
refund_timelock,
payouts.clone(),
maker_sk,
@ -409,7 +325,7 @@ where
publish_pk: taker_pub_pk,
},
),
oracle_pk,
(oracle_pk, nonce_pks),
refund_timelock,
payouts,
taker_sk,
@ -451,7 +367,7 @@ struct CfdKeys {
fn verify_cfd_sigs(
(maker_cfd_txs, maker_pk, maker_publish_pk): (&CfdTransactions, PublicKey, PublicKey),
(taker_cfd_txs, taker_pk, taker_publish_pk): (&CfdTransactions, PublicKey, PublicKey),
oracle_pk: schnorrsig::PublicKey,
(oracle_pk, nonce_pks): (schnorrsig::PublicKey, &[schnorrsig::PublicKey]),
(lock_desc, lock_amount): (&Descriptor<PublicKey>, Amount),
(commit_desc, commit_amount): (&Descriptor<PublicKey>, Amount),
) {
@ -471,7 +387,7 @@ fn verify_cfd_sigs(
&taker_pk.key,
)
.expect("valid taker refund sig");
for (tx, _, msg_nonce_pairs) in taker_cfd_txs.cets.iter() {
for (tx, _, digits) in taker_cfd_txs.cets.iter() {
maker_cfd_txs
.cets
.iter()
@ -480,9 +396,9 @@ fn verify_cfd_sigs(
&& verify_cet_encsig(
tx,
maker_encsig,
msg_nonce_pairs,
digits,
&maker_pk.key,
&oracle_pk,
(oracle_pk, nonce_pks),
commit_desc,
commit_amount,
)
@ -501,7 +417,7 @@ fn verify_cfd_sigs(
taker_encsig,
msg_nonce_pairs,
&taker_pk.key,
&oracle_pk,
(oracle_pk, nonce_pks),
commit_desc,
commit_amount,
)
@ -529,7 +445,8 @@ fn verify_cfd_sigs(
.expect("valid taker commit encsig");
}
fn check_cfd_txs(
fn check_cfd_txs<R>(
rng: &mut R,
(
maker_wallet,
maker_cfd_txs,
@ -571,7 +488,9 @@ fn check_cfd_txs(
(oracle, event): (Oracle, Event),
(lock_desc, lock_amount): (Descriptor<PublicKey>, Amount),
(commit_desc, commit_amount): (Descriptor<PublicKey>, Amount),
) {
) where
R: RngCore + CryptoRng,
{
// Lock transaction (either party can do this):
let signed_lock_tx = sign_lock_tx(maker_cfd_txs.lock, maker_wallet, taker_wallet)
@ -615,40 +534,30 @@ fn check_cfd_txs(
// CETs:
for (tx, _, msg_nonce_pairs) in maker_cfd_txs.cets.clone().into_iter() {
let oracle_sigs = event
.nonces
.iter()
.zip(&msg_nonce_pairs)
.map(|(nonce, (msg, _))| oracle.attest(msg, nonce))
.collect::<Vec<_>>();
for (tx, _, digits) in maker_cfd_txs.cets.clone().into_iter() {
let price = gen_price(rng, &digits);
let oracle_sigs = oracle.attest_price(price, &event.nonces);
build_and_check_cet(
tx,
&msg_nonce_pairs,
&taker_cfd_txs.cets,
(&maker_sk, &maker_pk),
&taker_pk,
&oracle_sigs,
(price, &oracle_sigs),
(&signed_commit_tx_maker, &commit_desc, commit_amount),
)
.expect("valid maker cet");
}
for (tx, _, msg_nonce_pairs) in taker_cfd_txs.cets.into_iter() {
let oracle_sigs = event
.nonces
.iter()
.zip(&msg_nonce_pairs)
.map(|(nonce, (msg, _))| oracle.attest(msg, nonce))
.collect::<Vec<_>>();
for (tx, _, digits) in taker_cfd_txs.cets.into_iter() {
let price = gen_price(rng, &digits);
let oracle_sigs = oracle.attest_price(price, &event.nonces);
build_and_check_cet(
tx,
&msg_nonce_pairs,
maker_cfd_txs.cets.as_slice(),
&maker_cfd_txs.cets,
(&taker_sk, &taker_pk),
&maker_pk,
&oracle_sigs,
(price, &oracle_sigs),
(&signed_commit_tx_maker, &commit_desc, commit_amount),
)
.expect("valid taker cet");
@ -682,21 +591,21 @@ fn check_cfd_txs(
.expect("valid taker punish tx");
}
#[allow(clippy::type_complexity)]
fn build_and_check_cet(
cet: Transaction,
msg_nonce_pairs: &[(Vec<u8>, schnorrsig::PublicKey)],
cets_other: &[(
Transaction,
EcdsaAdaptorSignature,
Vec<(Vec<u8>, schnorrsig::PublicKey)>,
)],
cets_other: &[(Transaction, EcdsaAdaptorSignature, interval::Digits)],
(sk, pk): (&SecretKey, &PublicKey),
pk_other: &PublicKey,
oracle_sigs: &[schnorrsig::Signature],
(price, oracle_sigs): (u64, &[schnorrsig::Signature]),
(commit_tx, commit_desc, commit_amount): (&Transaction, &Descriptor<PublicKey>, Amount),
) -> Result<()> {
let n_bits = msg_nonce_pairs.len();
let (encsig_other, n_bits) = cets_other
.iter()
.find_map(|(_, encsig, digits)| {
(digits.range().contains(&price)).then(|| (encsig, digits.len()))
})
.expect("one encsig per cet, per party");
let (oracle_sigs, _) = oracle_sigs.split_at(n_bits);
let (_nonce_pk, signature_scalar) = schnorrsig_decompose(&oracle_sigs[0]);
@ -707,12 +616,6 @@ fn build_and_check_cet(
decryption_sk.add_assign(signature_scalar.as_ref())?;
}
let encsig_other = cets_other
.iter()
.find_map(|(_tx, encsig, msg_nonce_pairs_other)| {
(msg_nonce_pairs == msg_nonce_pairs_other).then(|| encsig)
})
.expect("one encsig per cet, per party");
let signed_cet = decrypt_and_sign(
cet,
(sk, pk),
@ -816,13 +719,19 @@ fn verify_spend(
fn verify_cet_encsig(
tx: &Transaction,
encsig: &EcdsaAdaptorSignature,
msg_nonce_pairs: &[(Vec<u8>, schnorrsig::PublicKey)],
digits: &interval::Digits,
pk: &secp256k1_zkp::PublicKey,
oracle_pk: &schnorrsig::PublicKey,
(oracle_pk, nonce_pks): (schnorrsig::PublicKey, &[schnorrsig::PublicKey]),
spent_descriptor: &Descriptor<PublicKey>,
spent_amount: Amount,
) -> Result<()> {
let sig_point = compute_adaptor_point(oracle_pk, msg_nonce_pairs)
let msg_nonce_pairs = &digits
.to_bytes()
.into_iter()
.zip(nonce_pks.iter().cloned())
.collect::<Vec<_>>();
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)
}
@ -930,10 +839,6 @@ impl Oracle {
schnorrsig::PublicKey::from_keypair(SECP256K1, &self.key_pair)
}
fn attest(&self, msg: &[u8], nonce: &SecretKey) -> schnorrsig::Signature {
attest(&self.key_pair, nonce, msg)
}
fn attest_price(
&self,
price: u64,
@ -944,7 +849,7 @@ impl Oracle {
bits.iter()
.zip(nonces)
.map(|(msg, nonce)| self.attest(&[msg as u8], nonce))
.map(|(msg, nonce)| attest(&self.key_pair, nonce, &[msg as u8]))
.collect()
}
}
@ -965,7 +870,7 @@ struct Event {
/// Nonces.
///
/// Must remain secret.
nonces: Vec<SecretKey>,
nonces: [SecretKey; Oracle::MAX_DIGITS],
nonce_pks: Vec<schnorrsig::PublicKey>,
}
@ -974,7 +879,8 @@ impl Event {
where
R: RngCore + CryptoRng,
{
let (nonces, nonce_pks) = (0..20).map(|_| nonce(rng)).unzip();
let (nonces, nonce_pks) = (0..20).map(|_| nonce(rng)).unzip::<_, _, Vec<_>, _>();
let nonces = nonces.try_into().expect("20 nonces");
Self { nonces, nonce_pks }
}
@ -1023,3 +929,15 @@ fn schnorrsig_decompose(signature: &schnorrsig::Signature) -> (schnorrsig::Publi
(nonce_pk, s)
}
fn gen_price<R>(rng: &mut R, digits: &interval::Digits) -> u64
where
R: RngCore,
{
let (start, end) = digits.range().into_inner();
if start == end {
start
} else {
rng.gen_range(start, end)
}
}

10
daemon/src/model/cfd.rs

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

47
daemon/src/setup_contract_actor.rs

@ -2,15 +2,16 @@ use crate::model::cfd::{Cfd, FinalizedCfd};
use crate::wire::{Msg0, Msg1, SetupMsg};
use anyhow::{Context, Result};
use bdk::bitcoin::secp256k1::{schnorrsig, SecretKey, Signature, SECP256K1};
use bdk::bitcoin::{Amount, PublicKey, Transaction, Txid};
use bdk::bitcoin::{Amount, PublicKey, Transaction};
use bdk::descriptor::Descriptor;
use cfd_protocol::secp256k1_zkp::EcdsaAdaptorSignature;
use cfd_protocol::{
commit_descriptor, compute_adaptor_point, create_cfd_transactions, lock_descriptor,
commit_descriptor, compute_adaptor_point, create_cfd_transactions, interval, lock_descriptor,
spending_tx_sighash, PartyParams, PunishParams,
};
use futures::Future;
use std::collections::HashMap;
use std::ops::RangeInclusive;
use tokio::sync::mpsc;
/// Given an initial set of parameters, sets up the CFD contract with the other party.
@ -60,7 +61,7 @@ pub fn new(
let own_cfd_txs = create_cfd_transactions(
(params.maker().clone(), *params.maker_punish()),
(params.taker().clone(), *params.taker_punish()),
oracle_pk,
(oracle_pk, &[]),
cfd.refund_timelock_in_blocks(),
vec![],
sk,
@ -103,7 +104,7 @@ pub fn new(
.unwrap();
verify_cets(
&oracle_pk,
(&oracle_pk, &[]),
&params.other,
&own_cets,
&msg1.cets,
@ -124,9 +125,9 @@ pub fn new(
)
.unwrap();
let mut cet_by_id = own_cets
let mut cet_by_digits = own_cets
.into_iter()
.map(|(tx, _, msg_nonce_pairs)| (tx.txid(), (tx, msg_nonce_pairs)))
.map(|(tx, _, digits)| (digits.range(), (tx, digits)))
.collect::<HashMap<_, _>>();
FinalizedCfd {
@ -138,10 +139,10 @@ pub fn new(
cets: msg1
.cets
.into_iter()
.map(|(txid, sig)| {
let (cet, msg) = cet_by_id.remove(&txid).expect("unknown CET");
.map(|(range, sig)| {
let (cet, digits) = cet_by_digits.remove(&range).expect("unknown CET");
(cet, sig, msg)
(cet, sig, digits)
})
.collect::<Vec<_>>(),
refund: (refund_tx, msg1.refund),
@ -219,31 +220,26 @@ impl AllParams {
}
}
#[allow(clippy::type_complexity)]
fn verify_cets(
oracle_pk: &schnorrsig::PublicKey,
oracle_params: (&schnorrsig::PublicKey, &[schnorrsig::PublicKey]),
other: &PartyParams,
own_cets: &[(
Transaction,
EcdsaAdaptorSignature,
Vec<(Vec<u8>, schnorrsig::PublicKey)>,
)],
cets: &[(Txid, EcdsaAdaptorSignature)],
own_cets: &[(Transaction, EcdsaAdaptorSignature, interval::Digits)],
cets: &[(RangeInclusive<u64>, EcdsaAdaptorSignature)],
commit_desc: &Descriptor<PublicKey>,
commit_amount: Amount,
) -> Result<()> {
for (tx, _, msg_nonce_pairs) in own_cets.iter() {
for (tx, _, digits) in own_cets.iter() {
let other_encsig = cets
.iter()
.find_map(|(txid, encsig)| (txid == &tx.txid()).then(|| encsig))
.find_map(|(range, encsig)| (range == &digits.range()).then(|| encsig))
.expect("one encsig per cet, per party");
verify_cet_encsig(
tx,
other_encsig,
msg_nonce_pairs,
digits,
&other.identity_pk,
oracle_pk,
oracle_params,
commit_desc,
commit_amount,
)
@ -282,12 +278,17 @@ fn verify_signature(
fn verify_cet_encsig(
tx: &Transaction,
encsig: &EcdsaAdaptorSignature,
msg_nonce_pairs: &[(Vec<u8>, schnorrsig::PublicKey)],
digits: &interval::Digits,
pk: &PublicKey,
oracle_pk: &schnorrsig::PublicKey,
(oracle_pk, nonce_pks): (&schnorrsig::PublicKey, &[schnorrsig::PublicKey]),
spent_descriptor: &Descriptor<PublicKey>,
spent_amount: Amount,
) -> Result<()> {
let msg_nonce_pairs = &digits
.to_bytes()
.into_iter()
.zip(nonce_pks.iter().cloned())
.collect::<Vec<_>>();
let sig_point = compute_adaptor_point(oracle_pk, msg_nonce_pairs)
.context("could not calculate signature point")?;
verify_adaptor_signature(

7
daemon/src/wire.rs

@ -3,10 +3,11 @@ use crate::model::Usd;
use crate::Order;
use bdk::bitcoin::secp256k1::Signature;
use bdk::bitcoin::util::psbt::PartiallySignedTransaction;
use bdk::bitcoin::{Address, Amount, PublicKey, Txid};
use bdk::bitcoin::{Address, Amount, PublicKey};
use cfd_protocol::secp256k1_zkp::EcdsaAdaptorSignature;
use cfd_protocol::{CfdTransactions, PartyParams, PunishParams};
use serde::{Deserialize, Serialize};
use std::ops::RangeInclusive;
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type", content = "payload")]
@ -119,7 +120,7 @@ impl From<Msg0> for (PartyParams, PunishParams) {
#[derive(Debug, Serialize, Deserialize)]
pub struct Msg1 {
pub commit: EcdsaAdaptorSignature,
pub cets: Vec<(Txid, EcdsaAdaptorSignature)>,
pub cets: Vec<(RangeInclusive<u64>, EcdsaAdaptorSignature)>,
pub refund: Signature,
}
@ -130,7 +131,7 @@ impl From<CfdTransactions> for Msg1 {
cets: txs
.cets
.into_iter()
.map(|(tx, sig, _)| (tx.txid(), sig))
.map(|(_, sig, digits)| (digits.range(), sig))
.collect(),
refund: txs.refund.1,
}

Loading…
Cancel
Save