Browse Source

Prove that signing a price works

And fix bugs along the way.
no-contract-setup-message
Lucas Soriano del Pino 3 years ago
parent
commit
8122cd7b79
No known key found for this signature in database GPG Key ID: EE611E973A1530E7
  1. 19
      cfd_protocol/src/interval.rs
  2. 8
      cfd_protocol/src/lib.rs
  3. 179
      cfd_protocol/tests/cfds.rs

19
cfd_protocol/src/interval.rs

@ -12,7 +12,7 @@ const MAX_DIGITS: usize = 20;
const MAX_PRICE_DEC: u64 = (BASE as u64).pow(MAX_DIGITS as u32);
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Interval(RangeInclusive<u64>);
impl Interval {
@ -44,6 +44,12 @@ impl Interval {
}
}
impl From<Interval> for RangeInclusive<u64> {
fn from(interval: Interval) -> Self {
interval.0
}
}
#[derive(Clone, Debug)]
pub struct Digits(BitVec);
@ -61,13 +67,12 @@ impl Digits {
start..=end
}
}
impl std::iter::Iterator for Digits {
type Item = Vec<u8>;
fn next(&mut self) -> Option<Self::Item> {
self.0.iter().map(|bit| vec![bit as u8]).next()
pub fn to_bytes(&self) -> Vec<Vec<u8>> {
self.0
.iter()
.map(|bit| vec![if bit { 1u8 } else { 0u8 }])
.collect()
}
}

8
cfd_protocol/src/lib.rs

@ -458,7 +458,11 @@ impl Payout {
.as_digits()
.into_iter()
.map(|digits| {
let msg_nonce_pairs = digits.zip(nonce_pks.clone()).collect();
let msg_nonce_pairs = digits
.to_bytes()
.into_iter()
.zip(nonce_pks.clone())
.collect();
Self {
msg_nonce_pairs,
maker_amount,
@ -532,7 +536,7 @@ impl Payout {
/// Compute a signature point for the given oracle public key, announcement nonce public key and
/// message.
fn compute_signature_point(
pub fn compute_signature_point(
oracle_pk: &schnorrsig::PublicKey,
nonce_pk: &schnorrsig::PublicKey,
msg: &[u8],

179
cfd_protocol/tests/cfds.rs

@ -5,16 +5,18 @@ use bdk::descriptor::Descriptor;
use bdk::miniscript::DescriptorTrait;
use bdk::wallet::AddressIndex;
use bdk::SignOptions;
use bit_vec::BitVec;
use bitcoin::util::psbt::PartiallySignedTransaction;
use bitcoin::Txid;
use cfd_protocol::interval::Interval;
use cfd_protocol::{
commit_descriptor, compute_adaptor_point, create_cfd_transactions, finalize_spend_transaction,
lock_descriptor, oracle, punish_transaction, renew_cfd_transactions, spending_tx_sighash,
CfdTransactions, Payout, PunishParams, TransactionExt, WalletExt,
};
use rand::{thread_rng, CryptoRng, RngCore};
use rand::{thread_rng, CryptoRng, Rng, RngCore};
use secp256k1_zkp::{schnorrsig, EcdsaAdaptorSignature, SecretKey, Signature, SECP256K1};
use std::convert::TryInto;
use std::ops::RangeInclusive;
#[test]
fn create_cfd() {
@ -264,6 +266,67 @@ 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 = Interval::new(5_000, 10_000).unwrap();
let payouts = Payout::new(
interval.clone(),
announcement.nonce_pks(),
Amount::from_btc(1.5).unwrap(),
Amount::from_btc(0.5).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 interval = RangeInclusive::<u64>::from(interval);
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),
@ -397,13 +460,12 @@ fn verify_cfd_sigs(
)
.expect("valid taker refund sig");
for (tx, _, msg_nonce_pairs) in taker_cfd_txs.cets.iter() {
let maker_encsig = maker_cfd_txs
maker_cfd_txs
.cets
.iter()
.find_map(|(maker_tx, encsig, _)| (maker_tx.txid() == tx.txid()).then(|| encsig))
.expect("one encsig per cet, per party");
verify_cet_encsig(
.find(|(maker_tx, maker_encsig, _)| {
maker_tx.txid() == tx.txid()
&& verify_cet_encsig(
tx,
maker_encsig,
msg_nonce_pairs,
@ -412,16 +474,17 @@ fn verify_cfd_sigs(
commit_desc,
commit_amount,
)
.expect("valid maker cet encsig")
.is_ok()
})
.expect("one valid maker cet encsig per cet");
}
for (tx, _, msg_nonce_pairs) in maker_cfd_txs.cets.iter() {
let taker_encsig = taker_cfd_txs
taker_cfd_txs
.cets
.iter()
.find_map(|(taker_tx, encsig, _)| (taker_tx.txid() == tx.txid()).then(|| encsig))
.expect("one encsig per cet, per party");
verify_cet_encsig(
.find(|(taker_tx, taker_encsig, _)| {
taker_tx.txid() == tx.txid()
&& verify_cet_encsig(
tx,
taker_encsig,
msg_nonce_pairs,
@ -430,7 +493,9 @@ fn verify_cfd_sigs(
commit_desc,
commit_amount,
)
.expect("valid taker cet encsig")
.is_ok()
})
.expect("one valid taker cet encsig per cet");
}
encverify_spend(
&taker_cfd_txs.commit.0,
@ -539,41 +604,39 @@ fn check_cfd_txs(
// CETs:
for (tx, _, msg_nonce_pairs) in maker_cfd_txs.cets.clone().into_iter() {
build_and_check_cet(
tx,
event
let oracle_sigs = event
.nonces
.iter()
.zip(msg_nonce_pairs)
.map(|(nonce, (msg, _))| oracle.attest(msg.as_slice(), nonce))
.collect::<Vec<_>>()
.as_slice(),
taker_cfd_txs
.cets
.iter()
.map(|(tx, encsig, _)| (tx.txid(), *encsig)),
.zip(&msg_nonce_pairs)
.map(|(nonce, (msg, _))| oracle.attest(&msg, nonce))
.collect::<Vec<_>>();
build_and_check_cet(
tx,
&msg_nonce_pairs,
&taker_cfd_txs.cets,
(&maker_sk, &maker_pk),
&taker_pk,
&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() {
build_and_check_cet(
tx,
event
let oracle_sigs = event
.nonces
.iter()
.zip(msg_nonce_pairs)
.map(|(nonce, (msg, _))| oracle.attest(msg.as_slice(), nonce))
.collect::<Vec<_>>()
.as_slice(),
maker_cfd_txs
.cets
.iter()
.map(|(tx, encsig, _)| (tx.txid(), *encsig)),
.zip(&msg_nonce_pairs)
.map(|(nonce, (msg, _))| oracle.attest(&msg, nonce))
.collect::<Vec<_>>();
build_and_check_cet(
tx,
&msg_nonce_pairs,
maker_cfd_txs.cets.as_slice(),
(&taker_sk, &taker_pk),
&maker_pk,
&oracle_sigs,
(&signed_commit_tx_maker, &commit_desc, commit_amount),
)
.expect("valid taker cet");
@ -609,12 +672,20 @@ fn check_cfd_txs(
fn build_and_check_cet(
cet: Transaction,
msg_nonce_pairs: &[(Vec<u8>, schnorrsig::PublicKey)],
cets_other: &[(
Transaction,
EcdsaAdaptorSignature,
Vec<(Vec<u8>, schnorrsig::PublicKey)>,
)],
(sk, pk): (&SecretKey, &PublicKey),
pk_other: &PublicKey,
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 n_bits = msg_nonce_pairs.len();
let (oracle_sigs, _) = oracle_sigs.split_at(n_bits);
let (_nonce_pk, signature_scalar) = schnorrsig_decompose(&oracle_sigs[0]);
let mut decryption_sk = signature_scalar;
@ -623,15 +694,18 @@ fn build_and_check_cet(
decryption_sk.add_assign(signature_scalar.as_ref())?;
}
let taker_encsig = cets_other
.find_map(|(txid, encsig)| (txid == cet.txid()).then(|| encsig))
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,
(maker_sk, maker_pk),
(sk, pk),
&decryption_sk,
taker_pk,
&taker_encsig,
pk_other,
&encsig_other,
commit_desc,
commit_amount,
)
@ -827,6 +901,9 @@ struct Oracle {
}
impl Oracle {
/// Maximum number of binary digits for BTC price in whole USD.
const MAX_DIGITS: usize = 20;
fn new<R>(rng: &mut R) -> Self
where
R: RngCore + CryptoRng,
@ -843,6 +920,20 @@ impl Oracle {
fn attest(&self, msg: &[u8], nonce: &SecretKey) -> schnorrsig::Signature {
oracle::attest(&self.key_pair, nonce, msg)
}
fn attest_price(
&self,
price: u64,
nonces: &[SecretKey; Self::MAX_DIGITS],
) -> Vec<schnorrsig::Signature> {
let mut bits = BitVec::from_bytes(&price.to_be_bytes());
let bits = bits.split_off(bits.len() - 20);
bits.iter()
.zip(nonces)
.map(|(msg, nonce)| self.attest(&[msg as u8], nonce))
.collect()
}
}
fn announce<R>(rng: &mut R) -> (Event, Announcement)

Loading…
Cancel
Save