Browse Source

Merge pull request #161 from comit-network/multiple-payout-points

Allow more than one attestation event per CFD
upload-correct-windows-binary
Lucas Soriano 3 years ago
committed by GitHub
parent
commit
1d90402399
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      cfd_protocol/src/lib.rs
  2. 138
      cfd_protocol/src/protocol.rs
  3. 565
      cfd_protocol/tests/cfds.rs
  4. 2
      daemon/src/model/cfd.rs
  5. 18
      daemon/src/monitor.rs
  6. 103
      daemon/src/setup_contract.rs
  7. 23
      daemon/src/wire.rs

6
cfd_protocol/src/lib.rs

@ -5,8 +5,8 @@ pub mod interval;
pub use protocol::{
close_transaction, commit_descriptor, compute_adaptor_pk, create_cfd_transactions,
finalize_spend_transaction, lock_descriptor, punish_transaction, renew_cfd_transactions,
spending_tx_sighash, CfdTransactions, PartyParams, Payout, PunishParams, TransactionExt,
WalletExt,
finalize_spend_transaction, generate_payouts, lock_descriptor, punish_transaction,
renew_cfd_transactions, spending_tx_sighash, Announcement, Cets, CfdTransactions, PartyParams,
Payout, PunishParams, TransactionExt, WalletExt,
};
pub use secp256k1_zkp;

138
cfd_protocol/src/protocol.rs

@ -21,6 +21,7 @@ use bdk::FeeRate;
use itertools::Itertools;
use secp256k1_zkp::{self, schnorrsig, EcdsaAdaptorSignature, SecretKey, Signature, SECP256K1};
use std::collections::HashMap;
use std::hash::Hasher;
use std::iter::FromIterator;
use std::num::NonZeroU8;
use std::ops::RangeInclusive;
@ -72,19 +73,19 @@ where
/// * `taker` - The initial parameters of the taker.
/// * `taker_punish_params` - The punish parameters of the taker.
/// * `oracle_pk` - The public key of the oracle.
/// * `nonce_pks` - One R-value per price digit signed by the oracle. Their order matches a big
/// endian encoding of the price.
/// * `cet_timelock` - Relative timelock of the CET transaction with respect to the commit
/// transaction.
/// * `refund_timelock` - Relative timelock of the refund transaction with respect to the commit
/// transaction.
/// * `payouts` - All the possible ways in which the contract can be settled, according to the
/// conditions of the bet.
/// * `payouts_per_event` - All the possible ways in which the contract can be settled, according to
/// the conditions of the bet. The key is the event at which the oracle will attest the price.
/// * `identity_sk` - The secret key of the caller, used to sign and encsign different transactions.
pub fn create_cfd_transactions(
(maker, maker_punish_params): (PartyParams, PunishParams),
(taker, taker_punish_params): (PartyParams, PunishParams),
(oracle_pk, nonce_pks): (schnorrsig::PublicKey, &[schnorrsig::PublicKey]),
oracle_pk: schnorrsig::PublicKey,
(cet_timelock, refund_timelock): (u32, u32),
payouts: Vec<Payout>,
payouts_per_event: HashMap<Announcement, Vec<Payout>>,
identity_sk: SecretKey,
) -> Result<CfdTransactions> {
let lock_tx = lock_transaction(
@ -109,9 +110,9 @@ pub fn create_cfd_transactions(
taker.address,
taker_punish_params,
),
(oracle_pk, nonce_pks),
oracle_pk,
(cet_timelock, refund_timelock),
payouts,
payouts_per_event,
identity_sk,
)
}
@ -130,9 +131,9 @@ pub fn renew_cfd_transactions(
Address,
PunishParams,
),
(oracle_pk, nonce_pks): (schnorrsig::PublicKey, &[schnorrsig::PublicKey]),
oracle_pk: schnorrsig::PublicKey,
(cet_timelock, refund_timelock): (u32, u32),
payouts: Vec<Payout>,
payouts_per_event: HashMap<Announcement, Vec<Payout>>,
identity_sk: SecretKey,
) -> Result<CfdTransactions> {
build_cfds(
@ -149,9 +150,9 @@ pub fn renew_cfd_transactions(
taker_address,
taker_punish_params,
),
(oracle_pk, nonce_pks),
oracle_pk,
(cet_timelock, refund_timelock),
payouts,
payouts_per_event,
identity_sk,
)
}
@ -170,9 +171,9 @@ fn build_cfds(
Address,
PunishParams,
),
(oracle_pk, nonce_pks): (schnorrsig::PublicKey, &[schnorrsig::PublicKey]),
oracle_pk: schnorrsig::PublicKey,
(cet_timelock, refund_timelock): (u32, u32),
payouts: Vec<Payout>,
payouts_per_event: HashMap<Announcement, Vec<Payout>>,
identity_sk: SecretKey,
) -> Result<CfdTransactions> {
let commit_tx = CommitTransaction::new(
@ -215,24 +216,31 @@ fn build_cfds(
(tx.into_inner(), sig)
};
let cets = payouts
let cets = payouts_per_event
.into_iter()
.map(|payout| {
let cet = ContractExecutionTx::new(
&commit_tx,
payout.clone(),
&maker_address,
&taker_address,
nonce_pks,
cet_timelock,
)?;
let encsig = cet.encsign(identity_sk, &oracle_pk)?;
Ok((cet.into_inner(), encsig, payout.digits))
.map(|(event, payouts)| {
let cets = payouts
.iter()
.map(|payout| {
let cet = ContractExecutionTx::new(
&commit_tx,
payout.clone(),
&maker_address,
&taker_address,
event.nonce_pks.as_slice(),
cet_timelock,
)?;
let encsig = cet.encsign(identity_sk, &oracle_pk)?;
Ok((cet.into_inner(), encsig, payout.digits.clone()))
})
.collect::<Result<Vec<_>>>()
.context("cannot build and sign all cets")?;
Ok(Cets { event, cets })
})
.collect::<Result<Vec<_>>>()
.context("cannot build and sign all cets")?;
.collect::<Result<_>>()?;
Ok(CfdTransactions {
lock: lock_tx,
@ -341,10 +349,42 @@ pub struct PunishParams {
pub struct CfdTransactions {
pub lock: PartiallySignedTransaction,
pub commit: (Transaction, EcdsaAdaptorSignature),
pub cets: Vec<(Transaction, EcdsaAdaptorSignature, interval::Digits)>,
pub cets: Vec<Cets>,
pub refund: (Transaction, Signature),
}
/// Group of CETs associated with a particular oracle announcement.
///
/// All of the adaptor signatures included will be _possibly_ unlocked
/// by the attestation corresponding to the announcement. In practice,
/// only one of the adaptor signatures should be unlocked if the
/// payout intervals are constructed correctly. To check if an adaptor
/// signature can be unlocked by a price attestation, verify whether
/// the price attested to lies within its interval.
#[derive(Debug, Clone)]
pub struct Cets {
pub event: Announcement,
pub cets: Vec<(Transaction, EcdsaAdaptorSignature, interval::Digits)>,
}
#[derive(Debug, Clone, Eq)]
pub struct Announcement {
pub id: String,
pub nonce_pks: Vec<schnorrsig::PublicKey>,
}
impl std::hash::Hash for Announcement {
fn hash<H: Hasher>(&self, state: &mut H) {
self.id.hash(state)
}
}
impl PartialEq for Announcement {
fn eq(&self, other: &Self) -> bool {
self.id.eq(&other.id)
}
}
#[derive(Debug, Clone)]
pub struct Payout {
digits: interval::Digits,
@ -352,23 +392,23 @@ pub struct Payout {
taker_amount: Amount,
}
impl Payout {
pub fn new(
range: RangeInclusive<u64>,
maker_amount: Amount,
taker_amount: Amount,
) -> Result<Vec<Self>> {
let digits = interval::Digits::new(range).context("invalid interval")?;
Ok(digits
.into_iter()
.map(|digits| Self {
digits,
maker_amount,
taker_amount,
})
.collect())
}
pub fn generate_payouts(
range: RangeInclusive<u64>,
maker_amount: Amount,
taker_amount: Amount,
) -> Result<Vec<Payout>> {
let digits = interval::Digits::new(range).context("invalid interval")?;
Ok(digits
.into_iter()
.map(|digits| Payout {
digits,
maker_amount,
taker_amount,
})
.collect())
}
impl Payout {
fn into_txouts(self, maker_address: &Address, taker_address: &Address) -> Vec<TxOut> {
let txouts = [
(self.maker_amount, maker_address),
@ -464,7 +504,7 @@ mod tests {
let orig_maker_amount = 1000;
let orig_taker_amount = 1000;
let payouts = Payout::new(
let payouts = generate_payouts(
0..=10_000,
Amount::from_sat(orig_maker_amount),
Amount::from_sat(orig_taker_amount),
@ -498,7 +538,7 @@ mod tests {
let orig_maker_amount = dummy_dust_limit.as_sat() - 1;
let orig_taker_amount = 1000;
let payouts = Payout::new(
let payouts = generate_payouts(
0..=10_000,
Amount::from_sat(orig_maker_amount),
Amount::from_sat(orig_taker_amount),

565
cfd_protocol/tests/cfds.rs

@ -8,12 +8,14 @@ use bdk::SignOptions;
use bitcoin::util::psbt::PartiallySignedTransaction;
use cfd_protocol::{
close_transaction, commit_descriptor, compute_adaptor_pk, create_cfd_transactions,
finalize_spend_transaction, interval, lock_descriptor, punish_transaction,
renew_cfd_transactions, spending_tx_sighash, CfdTransactions, Payout, PunishParams,
TransactionExt, WalletExt,
finalize_spend_transaction, generate_payouts, interval, lock_descriptor, punish_transaction,
renew_cfd_transactions, spending_tx_sighash, Announcement, Cets, CfdTransactions, Payout,
PunishParams, TransactionExt, WalletExt,
};
use rand::{thread_rng, CryptoRng, RngCore};
use secp256k1_zkp::{schnorrsig, EcdsaAdaptorSignature, SecretKey, Signature, SECP256K1};
use std::collections::HashMap;
use std::iter::FromIterator;
use std::str::FromStr;
#[test]
@ -26,25 +28,38 @@ fn create_cfd() {
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_data = OliviaData::example();
let oracle_pk = oracle_data.pk;
let nonce_pks = oracle_data.nonce_pks.clone();
let oracle_data_0 = OliviaData::example_0();
let oracle_data_1 = OliviaData::example_1();
let payouts = vec![
Payout::new(
0..=40_000,
Amount::from_btc(1.5).unwrap(),
Amount::from_btc(0.5).unwrap(),
)
.unwrap(),
Payout::new(
40_001..=70_000,
Amount::ZERO,
Amount::from_btc(2.0).unwrap(),
)
.unwrap(),
]
.concat();
let oracle_pk = oracle_data_0.pk;
let event_0 = oracle_data_0.announcement();
let event_1 = oracle_data_1.announcement();
let payouts_per_event = HashMap::from_iter([
(
event_0.clone(),
generate_payouts(0..=50_000, Amount::ZERO, Amount::from_btc(2.0).unwrap()).unwrap(),
),
(
event_1.clone(),
[
generate_payouts(
40_001..=70_000,
Amount::from_btc(0.5).unwrap(),
Amount::from_btc(1.5).unwrap(),
)
.unwrap(),
generate_payouts(
70_001..=100_000,
Amount::from_btc(1.5).unwrap(),
Amount::from_btc(0.5).unwrap(),
)
.unwrap(),
]
.concat(),
),
]);
let cet_timelock = 0;
let refund_timelock = 0;
@ -53,11 +68,16 @@ fn create_cfd() {
&mut rng,
(&maker_wallet, maker_lock_amount),
(&taker_wallet, taker_lock_amount),
(oracle_pk, &nonce_pks),
payouts,
oracle_pk,
payouts_per_event,
(cet_timelock, refund_timelock),
);
assert_contains_cets_for_event(&maker_cfd_txs.cets, &event_0);
assert_contains_cets_for_event(&maker_cfd_txs.cets, &event_1);
assert_contains_cets_for_event(&taker_cfd_txs.cets, &event_0);
assert_contains_cets_for_event(&taker_cfd_txs.cets, &event_1);
let lock_desc = lock_descriptor(maker.pk, taker.pk);
let lock_amount = maker_lock_amount + taker_lock_amount;
@ -70,7 +90,7 @@ fn create_cfd() {
verify_cfd_sigs(
(&maker_cfd_txs, maker.pk, maker.pub_pk),
(&taker_cfd_txs, taker.pk, taker.pub_pk),
(oracle_pk, &nonce_pks),
(oracle_pk, vec![event_0, event_1]),
(&lock_desc, lock_amount),
(&commit_desc, commit_amount),
);
@ -96,7 +116,7 @@ fn create_cfd() {
taker.rev_sk,
taker_addr,
),
oracle_data,
&[oracle_data_0, oracle_data_1],
(lock_desc, lock_amount),
(commit_desc, commit_amount),
);
@ -112,20 +132,23 @@ fn renew_cfd() {
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_data = OliviaData::example();
let oracle_data = OliviaData::example_0();
let oracle_pk = oracle_data.pk;
let nonce_pks = oracle_data.nonce_pks.clone();
let payouts = vec![
Payout::new(0..=10_000, Amount::from_btc(2.0).unwrap(), Amount::ZERO).unwrap(),
Payout::new(
10_001..=50_000,
Amount::ZERO,
Amount::from_btc(2.0).unwrap(),
)
.unwrap(),
]
.concat();
let event = oracle_data.announcement();
let payouts_per_event = HashMap::from_iter([(
event,
vec![
generate_payouts(0..=10_000, Amount::from_btc(2.0).unwrap(), Amount::ZERO).unwrap(),
generate_payouts(
10_001..=50_000,
Amount::ZERO,
Amount::from_btc(2.0).unwrap(),
)
.unwrap(),
]
.concat(),
)]);
let cet_timelock = 0;
let refund_timelock = 0;
@ -134,8 +157,8 @@ fn renew_cfd() {
&mut rng,
(&maker_wallet, maker_lock_amount),
(&taker_wallet, taker_lock_amount),
(oracle_pk, &nonce_pks),
payouts,
oracle_pk,
payouts_per_event,
(cet_timelock, refund_timelock),
);
@ -147,21 +170,28 @@ fn renew_cfd() {
let (taker_rev_sk, taker_rev_pk) = make_keypair(&mut rng);
let (taker_pub_sk, taker_pub_pk) = make_keypair(&mut rng);
let payouts = vec![
Payout::new(
0..=50_000,
Amount::from_btc(1.5).unwrap(),
Amount::from_btc(0.5).unwrap(),
)
.unwrap(),
Payout::new(
50_001..=70_000,
Amount::from_btc(0.5).unwrap(),
Amount::from_btc(1.5).unwrap(),
)
.unwrap(),
]
.concat();
let oracle_data = OliviaData::example_1();
let oracle_pk = oracle_data.pk;
let event = oracle_data.announcement();
let payouts_per_event = HashMap::from_iter([(
event.clone(),
vec![
generate_payouts(
0..=50_000,
Amount::from_btc(1.5).unwrap(),
Amount::from_btc(0.5).unwrap(),
)
.unwrap(),
generate_payouts(
50_001..=70_000,
Amount::from_btc(0.5).unwrap(),
Amount::from_btc(1.5).unwrap(),
)
.unwrap(),
]
.concat(),
)]);
let maker_cfd_txs = renew_cfd_transactions(
maker_cfd_txs.lock,
@ -183,9 +213,9 @@ fn renew_cfd() {
publish_pk: taker_pub_pk,
},
),
(oracle_pk, &nonce_pks),
oracle_pk,
(cet_timelock, refund_timelock),
payouts.clone(),
payouts_per_event.clone(),
maker.sk,
)
.unwrap();
@ -210,13 +240,16 @@ fn renew_cfd() {
publish_pk: taker_pub_pk,
},
),
(oracle_pk, &nonce_pks),
oracle_pk,
(cet_timelock, refund_timelock),
payouts,
payouts_per_event,
taker.sk,
)
.unwrap();
assert_contains_cets_for_event(&maker_cfd_txs.cets, &event);
assert_contains_cets_for_event(&taker_cfd_txs.cets, &event);
let lock_desc = lock_descriptor(maker.pk, taker.pk);
let lock_amount = maker_lock_amount + taker_lock_amount;
@ -229,7 +262,7 @@ fn renew_cfd() {
verify_cfd_sigs(
(&maker_cfd_txs, maker.pk, maker_pub_pk),
(&taker_cfd_txs, taker.pk, taker_pub_pk),
(oracle_pk, &nonce_pks),
(oracle_pk, vec![event]),
(&lock_desc, lock_amount),
(&commit_desc, commit_amount),
);
@ -255,7 +288,7 @@ fn renew_cfd() {
taker_rev_sk,
taker_addr,
),
oracle_data,
&[oracle_data],
(lock_desc, lock_amount),
(commit_desc, commit_amount),
)
@ -271,17 +304,19 @@ fn collaboratively_close_cfd() {
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_data = OliviaData::example();
let oracle_data = OliviaData::example_0();
let oracle_pk = oracle_data.pk;
let nonce_pks = oracle_data.nonce_pks;
let event = oracle_data.announcement();
let payouts = vec![Payout::new(
0..=100_000,
Amount::from_btc(1.5).unwrap(),
Amount::from_btc(0.5).unwrap(),
)
.unwrap()]
.concat();
let payouts_per_event = HashMap::from_iter([(
event,
generate_payouts(
0..=100_000,
Amount::from_btc(1.5).unwrap(),
Amount::from_btc(0.5).unwrap(),
)
.unwrap(),
)]);
let cet_timelock = 0;
let refund_timelock = 0;
@ -290,8 +325,8 @@ fn collaboratively_close_cfd() {
&mut rng,
(&maker_wallet, maker_lock_amount),
(&taker_wallet, taker_lock_amount),
(oracle_pk, &nonce_pks),
payouts,
oracle_pk,
payouts_per_event,
(cet_timelock, refund_timelock),
);
@ -335,8 +370,8 @@ fn create_cfd_txs(
rng: &mut (impl RngCore + CryptoRng),
(maker_wallet, maker_lock_amount): (&bdk::Wallet<(), bdk::database::MemoryDatabase>, Amount),
(taker_wallet, taker_lock_amount): (&bdk::Wallet<(), bdk::database::MemoryDatabase>, Amount),
(oracle_pk, nonce_pks): (schnorrsig::PublicKey, &[schnorrsig::PublicKey]),
payouts: Vec<Payout>,
oracle_pk: schnorrsig::PublicKey,
payouts_per_event: HashMap<Announcement, Vec<Payout>>,
(cet_timelock, refund_timelock): (u32, u32),
) -> (
CfdTransactions,
@ -362,6 +397,7 @@ fn create_cfd_txs(
let taker_params = taker_wallet
.build_party_params(taker_lock_amount, taker_pk)
.unwrap();
let maker_cfd_txs = create_cfd_transactions(
(
maker_params.clone(),
@ -377,9 +413,9 @@ fn create_cfd_txs(
publish_pk: taker_pub_pk,
},
),
(oracle_pk, nonce_pks),
oracle_pk,
(cet_timelock, refund_timelock),
payouts.clone(),
payouts_per_event.clone(),
maker_sk,
)
.unwrap();
@ -398,9 +434,9 @@ fn create_cfd_txs(
publish_pk: taker_pub_pk,
},
),
(oracle_pk, nonce_pks),
oracle_pk,
(cet_timelock, refund_timelock),
payouts,
payouts_per_event,
taker_sk,
)
.unwrap();
@ -440,7 +476,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, nonce_pks): (schnorrsig::PublicKey, &[schnorrsig::PublicKey]),
(oracle_pk, events): (schnorrsig::PublicKey, Vec<Announcement>),
(lock_desc, lock_amount): (&Descriptor<PublicKey>, Amount),
(commit_desc, commit_amount): (&Descriptor<PublicKey>, Amount),
) {
@ -460,44 +496,69 @@ fn verify_cfd_sigs(
&taker_pk.key,
)
.expect("valid taker refund sig");
for (tx, _, digits) in taker_cfd_txs.cets.iter() {
maker_cfd_txs
for grouped_taker_cets in taker_cfd_txs.cets.iter() {
let grouped_maker_cets = maker_cfd_txs
.cets
.iter()
.find(|(maker_tx, maker_encsig, _)| {
maker_tx.txid() == tx.txid()
&& verify_cet_encsig(
tx,
maker_encsig,
digits,
&maker_pk.key,
(oracle_pk, nonce_pks),
commit_desc,
commit_amount,
)
.is_ok()
})
.expect("one valid maker cet encsig per cet");
.find(|grouped_maker_cets| grouped_maker_cets.event == grouped_taker_cets.event)
.expect("both parties to have the same set of payouts");
let event = events
.iter()
.find(|event| event.id == grouped_maker_cets.event.id)
.expect("event to exist");
for (tx, _, digits) in grouped_taker_cets.cets.iter() {
grouped_maker_cets
.cets
.iter()
.find(|(maker_tx, maker_encsig, _)| {
maker_tx.txid() == tx.txid()
&& verify_cet_encsig(
tx,
maker_encsig,
digits,
&maker_pk.key,
(oracle_pk, event.nonce_pks.as_slice()),
commit_desc,
commit_amount,
)
.is_ok()
})
.expect("one valid maker cet encsig per cet");
}
}
for (tx, _, msg_nonce_pairs) in maker_cfd_txs.cets.iter() {
taker_cfd_txs
for grouped_maker_cets in maker_cfd_txs.cets.iter() {
let grouped_taker_cets = taker_cfd_txs
.cets
.iter()
.find(|(taker_tx, taker_encsig, _)| {
taker_tx.txid() == tx.txid()
&& verify_cet_encsig(
tx,
taker_encsig,
msg_nonce_pairs,
&taker_pk.key,
(oracle_pk, nonce_pks),
commit_desc,
commit_amount,
)
.is_ok()
})
.expect("one valid taker cet encsig per cet");
.find(|grouped_taker_cets| grouped_taker_cets.event == grouped_maker_cets.event)
.expect("both parties to have the same set of payouts");
let event = events
.iter()
.find(|event| event.id == grouped_maker_cets.event.id)
.expect("event to exist");
for (tx, _, digits) in grouped_maker_cets.cets.iter() {
grouped_taker_cets
.cets
.iter()
.find(|(taker_tx, taker_encsig, _)| {
taker_tx.txid() == tx.txid()
&& verify_cet_encsig(
tx,
taker_encsig,
digits,
&taker_pk.key,
(oracle_pk, event.nonce_pks.as_slice()),
commit_desc,
commit_amount,
)
.is_ok()
})
.expect("one valid taker cet encsig per cet");
}
}
encverify_spend(
&taker_cfd_txs.commit.0,
&maker_cfd_txs.commit.1,
@ -557,7 +618,7 @@ fn check_cfd_txs(
SecretKey,
Address,
),
oracle_data: OliviaData,
oracle_data_list: &[OliviaData],
(lock_desc, lock_amount): (Descriptor<PublicKey>, Amount),
(commit_desc, commit_amount): (Descriptor<PublicKey>, Amount),
) {
@ -604,51 +665,77 @@ fn check_cfd_txs(
// CETs:
let unlocked_cets = maker_cfd_txs.cets.clone().into_iter().filter_map({
|(tx, _, digits)| {
let price = oracle_data.price;
for Cets { event, cets } in maker_cfd_txs.cets.clone().into_iter() {
let oracle_data = oracle_data_list
.iter()
.find(|data| data.id == event.id)
.expect("every cet to correspond to an existing event");
let price = oracle_data.price;
let oracle_attestations = oracle_data.attestations.clone();
let taker_cets = taker_cfd_txs
.cets
.iter()
.find_map(
|Cets {
event: other_event,
cets,
}| (other_event.id == event.id).then(|| cets),
)
.expect("same events for taker and maker");
cets.into_iter().for_each(|(tx, _, digits)| {
if !digits.range().contains(&price) {
return None;
return;
}
let oracle_attestations = oracle_data.attestations.clone();
build_and_check_cet(
tx,
&taker_cfd_txs.cets,
taker_cets,
(&maker_sk, &maker_pk),
&taker_pk,
(price, &oracle_attestations),
(&signed_commit_tx_maker, &commit_desc, commit_amount),
)
.expect("valid maker cet");
.expect("valid unlocked maker cet");
});
}
Some(())
}
});
assert_eq!(unlocked_cets.count(), 1, "Expected to unlock only 1 CET");
for Cets { event, cets } in taker_cfd_txs.cets.clone().into_iter() {
let oracle_data = oracle_data_list
.iter()
.find(|data| data.id == event.id)
.expect("every cet to correspond to an existing event");
let price = oracle_data.price;
let oracle_attestations = oracle_data.attestations.clone();
let unlocked_cets = taker_cfd_txs
.cets
.into_iter()
.filter_map(|(tx, _, digits)| {
let price = oracle_data.price;
let maker_cets = maker_cfd_txs
.cets
.iter()
.find_map(
|Cets {
event: other_event,
cets,
}| (other_event.id == event.id).then(|| cets),
)
.expect("same events for taker and maker");
cets.into_iter().for_each(|(tx, _, digits)| {
if !digits.range().contains(&price) {
return None;
return;
}
build_and_check_cet(
tx,
&maker_cfd_txs.cets,
maker_cets,
(&taker_sk, &taker_pk),
&maker_pk,
(price, &oracle_data.attestations),
(&signed_commit_tx_maker, &commit_desc, commit_amount),
(price, &oracle_attestations),
(&signed_commit_tx_taker, &commit_desc, commit_amount),
)
.expect("valid taker cet");
Some(())
.expect("valid unlocked taker cet");
});
assert_eq!(unlocked_cets.count(), 1, "Expected to unlock only 1 CET");
}
// Punish transactions:
@ -930,6 +1017,7 @@ fn make_keypair(rng: &mut (impl RngCore + CryptoRng)) -> (SecretKey, PublicKey)
}
struct OliviaData {
id: String,
pk: schnorrsig::PublicKey,
nonce_pks: Vec<schnorrsig::PublicKey>,
price: u64,
@ -937,74 +1025,163 @@ struct OliviaData {
}
impl OliviaData {
fn example_0() -> Self {
Self::example(
Self::EVENT_ID_0,
Self::PRICE_0,
&Self::NONCE_PKS_0,
&Self::ATTESTATIONS_0,
)
}
fn example_1() -> Self {
Self::example(
Self::EVENT_ID_1,
Self::PRICE_1,
&Self::NONCE_PKS_1,
&Self::ATTESTATIONS_1,
)
}
/// An example of all the data necessary from `olivia` to test the
/// CFD protocol.
///
/// Data comes from this event:
/// https://outcome.observer/h00.ooo/x/BitMEX/BXBT/2021-10-05T02:00:00.price[n:20].
pub fn example() -> Self {
let pk = schnorrsig::PublicKey::from_str(
"ddd4636845a90185991826be5a494cde9f4a6947b1727217afedc6292fa4caf7",
)
.unwrap();
fn example(id: &str, price: u64, nonce_pks: &[&str], attestations: &[&str]) -> Self {
let oracle_pk = schnorrsig::PublicKey::from_str(Self::OLIVIA_PK).unwrap();
let nonce_pks = [
"d02d163cf9623f567c4e3faf851a9266ac1ede13da4ca4141f3a7717fba9a739",
"bc310f26aa5addbc382f653d8530aaead7c25e3546abc24639f490e36d4bdb88",
"2661375f570dcc32300d442e85b6d72dfa3232dccda45e8fb4a2d1e758d1d374",
"fcc68fbf071d391b14c0867cb4defb5a8abc12418dff3dfc2f84fd4025cb2716",
"cf5c2b7fe3851c64a7ff9635a9bfc50cdd301401d002f2da049f4c6a20e8457b",
"14f1005d8c2832a2c4666dd732dd9bb3af9c8f70ebcdaec96869b1ca0c8e0de6",
"299ee1c9c20fab8b067adf452a7d7661b5e7f5dd6bc707562805002e7cb8443e",
"bcb4e5a594346de298993a7a31762f598b5224b977e23182369e9ed3e5127f78",
"25e09a16ee5d469069abfb62cd5e1f20af50cf15241f571e64fa28b127304574",
"3ed5a1422f43299caf281123aba88bd4bc61ec863f4afb79c7ce7663ad44db5d",
"a7e0f61212735c192c4bf16e0a3e925e65f9f3feb6f1e5e8d6f5c18cf2dbb5a8",
"a36a631015d9036d0c321fea7cf12f589aa196e7279b4a290de5112c2940e540",
"b5bdd931f81970139e7301ac654b378077c3ed993ca7893ed93fee5fc6f7a782",
"00090816e256b41e042dce38bde99ab3cf9482f9b066836988d3ed54833638e8",
"3530408e93c251f5f488d3b1c608157177c459d6fab1966abebf765bcc9338d2",
"603269ce88d112ff7fcfcaab82f228be97deca37f8190084d509c71b51a30432",
"f0587414fcc6c56aef11d4a1d287ad6b55b237c5b8a5d5d93eb9ca06f6466ccf",
"763009afb0ffd99c7b835488cb3b0302f3b78f59bbfd5292bedab8ef9da8c1b7",
"3867af9048309a05004a164bdea09899f23ff1d83b6491b2b53a1b7b92e0eb2e",
"688118e6b59e27944c277513db2711a520f4283c7c53a11f58d9f6a46d82c964",
]
.iter()
.map(|pk| schnorrsig::PublicKey::from_str(pk).unwrap())
.collect();
let attestations = [
"5bc7663195971daaa1e3e6a81b4bca65882791644bc446fc060cbc118a3ace0f",
"721d0cb56a0778a1ca7907f81a0787f34385b13f854c845c4c5539f7f6267958",
"044aeef0d525c8ff48758c80939e95807bc640990cc03f53ab6fc0b262045221",
"79f5175423ec6ee69c8d0e55251db85f3015c2edfa5a03095443fbbf35eb2282",
"233b9ec549e9cc7c702109d29636db85a3ec63a66f3b53444bcc7586d36ca439",
"2961a00320b7c9a70220060019a6ca88e18c205fadd2f873c174e5ccbbed527e",
"bdb76e8f81c39ade4205ead9b68118757fc49ec22769605f26ef904b235283d6",
"6e75dafedf4ed685513ec1f5c93508de4fad2be05b46001ac00c03474f4690e1",
"cfcfc27eb9273b343b3042f0386e77efe329066be079788bb00ab47d72f26780",
"2d931ffd2963e74566365674583abc427bdb6ae571c4887d81f1920f0850665d",
"33b6f1112fa046cbc04be44c615e70519702662c1f72d8d49b3c4613614a8a46",
"19e569b15410fa9a758c1a6c211eae8c1547efbe0ac6a7709902be93415f2f09",
"d859dd5c9a58e1836d1eea3ebe7f48198a681d29e5a5cd6922532d2e94a53a1d",
"3387eb2ad5e64cd102167766bb72b447f4a2e5129d161e422f9d41cd7d1cc281",
"db35a9778a1e3abc8d8ab2f4a79346ae2154c9e0b4932d859d1f3e244f67ae76",
"c3be969e8b889cfb2ece71123e6be5538a2d3a1229637b18bccc179073c38059",
"6f73263f430e10b82d0fd06c4ddd3b8a6b58c3e756745bd0d9e71a399e517921",
"0818c9c245d7d2162cd393c562a121f80405a27d22ae465e95030c31ebb4bd24",
"b7c03f0bd6d63bd78ad4ea0f3452ff9717ba65ca42038e6e90a1aa558b7942dc",
"90c4d8ec9f408ccb62a62daa993c20f2f86799e1fdea520c6d060418e55fd216",
]
.iter()
.map(|pk| SecretKey::from_str(pk).unwrap())
.collect();
let id = id.to_string();
let nonce_pks = nonce_pks
.iter()
.map(|pk| schnorrsig::PublicKey::from_str(pk).unwrap())
.collect();
let attestations = attestations
.iter()
.map(|pk| SecretKey::from_str(pk).unwrap())
.collect();
Self {
pk,
id,
pk: oracle_pk,
nonce_pks,
attestations,
price: 49262,
price,
}
}
fn announcement(&self) -> Announcement {
Announcement {
id: self.id.clone(),
nonce_pks: self.nonce_pks.clone(),
}
}
const OLIVIA_PK: &'static str =
"ddd4636845a90185991826be5a494cde9f4a6947b1727217afedc6292fa4caf7";
const EVENT_ID_0: &'static str = "/x/BitMEX/BXBT/2021-10-05T02:00:00.price[n:20]";
const NONCE_PKS_0: [&'static str; 20] = [
"d02d163cf9623f567c4e3faf851a9266ac1ede13da4ca4141f3a7717fba9a739",
"bc310f26aa5addbc382f653d8530aaead7c25e3546abc24639f490e36d4bdb88",
"2661375f570dcc32300d442e85b6d72dfa3232dccda45e8fb4a2d1e758d1d374",
"fcc68fbf071d391b14c0867cb4defb5a8abc12418dff3dfc2f84fd4025cb2716",
"cf5c2b7fe3851c64a7ff9635a9bfc50cdd301401d002f2da049f4c6a20e8457b",
"14f1005d8c2832a2c4666dd732dd9bb3af9c8f70ebcdaec96869b1ca0c8e0de6",
"299ee1c9c20fab8b067adf452a7d7661b5e7f5dd6bc707562805002e7cb8443e",
"bcb4e5a594346de298993a7a31762f598b5224b977e23182369e9ed3e5127f78",
"25e09a16ee5d469069abfb62cd5e1f20af50cf15241f571e64fa28b127304574",
"3ed5a1422f43299caf281123aba88bd4bc61ec863f4afb79c7ce7663ad44db5d",
"a7e0f61212735c192c4bf16e0a3e925e65f9f3feb6f1e5e8d6f5c18cf2dbb5a8",
"a36a631015d9036d0c321fea7cf12f589aa196e7279b4a290de5112c2940e540",
"b5bdd931f81970139e7301ac654b378077c3ed993ca7893ed93fee5fc6f7a782",
"00090816e256b41e042dce38bde99ab3cf9482f9b066836988d3ed54833638e8",
"3530408e93c251f5f488d3b1c608157177c459d6fab1966abebf765bcc9338d2",
"603269ce88d112ff7fcfcaab82f228be97deca37f8190084d509c71b51a30432",
"f0587414fcc6c56aef11d4a1d287ad6b55b237c5b8a5d5d93eb9ca06f6466ccf",
"763009afb0ffd99c7b835488cb3b0302f3b78f59bbfd5292bedab8ef9da8c1b7",
"3867af9048309a05004a164bdea09899f23ff1d83b6491b2b53a1b7b92e0eb2e",
"688118e6b59e27944c277513db2711a520f4283c7c53a11f58d9f6a46d82c964",
];
const PRICE_0: u64 = 49262;
const ATTESTATIONS_0: [&'static str; 20] = [
"5bc7663195971daaa1e3e6a81b4bca65882791644bc446fc060cbc118a3ace0f",
"721d0cb56a0778a1ca7907f81a0787f34385b13f854c845c4c5539f7f6267958",
"044aeef0d525c8ff48758c80939e95807bc640990cc03f53ab6fc0b262045221",
"79f5175423ec6ee69c8d0e55251db85f3015c2edfa5a03095443fbbf35eb2282",
"233b9ec549e9cc7c702109d29636db85a3ec63a66f3b53444bcc7586d36ca439",
"2961a00320b7c9a70220060019a6ca88e18c205fadd2f873c174e5ccbbed527e",
"bdb76e8f81c39ade4205ead9b68118757fc49ec22769605f26ef904b235283d6",
"6e75dafedf4ed685513ec1f5c93508de4fad2be05b46001ac00c03474f4690e1",
"cfcfc27eb9273b343b3042f0386e77efe329066be079788bb00ab47d72f26780",
"2d931ffd2963e74566365674583abc427bdb6ae571c4887d81f1920f0850665d",
"33b6f1112fa046cbc04be44c615e70519702662c1f72d8d49b3c4613614a8a46",
"19e569b15410fa9a758c1a6c211eae8c1547efbe0ac6a7709902be93415f2f09",
"d859dd5c9a58e1836d1eea3ebe7f48198a681d29e5a5cd6922532d2e94a53a1d",
"3387eb2ad5e64cd102167766bb72b447f4a2e5129d161e422f9d41cd7d1cc281",
"db35a9778a1e3abc8d8ab2f4a79346ae2154c9e0b4932d859d1f3e244f67ae76",
"c3be969e8b889cfb2ece71123e6be5538a2d3a1229637b18bccc179073c38059",
"6f73263f430e10b82d0fd06c4ddd3b8a6b58c3e756745bd0d9e71a399e517921",
"0818c9c245d7d2162cd393c562a121f80405a27d22ae465e95030c31ebb4bd24",
"b7c03f0bd6d63bd78ad4ea0f3452ff9717ba65ca42038e6e90a1aa558b7942dc",
"90c4d8ec9f408ccb62a62daa993c20f2f86799e1fdea520c6d060418e55fd216",
];
const EVENT_ID_1: &'static str = "/x/BitMEX/BXBT/2021-10-05T08:00:00.price[n:20]";
const NONCE_PKS_1: [&'static str; 20] = [
"150df2e64f39706e726eaa1fe081af3edf376d9644723e135a99328fd194caca",
"b90629cedc7cb8430b4d15c84bbe1fe173e70e626d40c465e64de29d4879e20f",
"ae14ffb8701d3e224b6632a1bb7b099c8aa90979c3fb788422daa08bca25fa68",
"3717940a7e8c35b48b3596498ed93e4d54ba01a2bcbb645d30dae2fc98f087a8",
"91beb5da91cc8b4ee6ae603e7ae41cc041d5ea2c13bae9f0e630c69f6c0adfad",
"c51cafb450b01f30ec8bd2b4b5fed6f7e179f49945959f0d7609b4b9c5ab3781",
"75f2d9332aa1b2d84446a4b2aa276b4c2853659ab0ba74f0881289d3ab700f0c",
"5367de73acb53e69b0a4f777e564f87055fede5d4492ddafae876a815fa6166c",
"2087a513adb1aa2cc8506ca58306723ed13ba82e054f5bf29fcbeef1ab915c5a",
"71c980fb6adae9c121405628c91daffcc5ab52a8a0b6f53c953e8a0236b05782",
"d370d22f06751fc649f6ee930ac7f8f3b00389fdad02883a8038a81c46c33b19",
"fa6f7d37dc88b510c250dcae1023cce5009d5beb85a75f5b8b10c973b62348aa",
"a658077f9c963d1f41cf63b7ebf6e08331f5d201554b3af7814673108abe1bf3",
"8a816bf4caa2d6114b2e4d3ab9bff0d470ee0b90163c78c9b67f90238ead9319",
"c2519a4e764a65204c469062e260d8565f7730847c507b92c987e478ca91abe1",
"59cb6b5beac6511a671076530cc6cc9f1926f54c640828f38c363b110dd8a0cd",
"4625b1f3ab9ee01455fa1a98d15fc8d73a7cf41becb4ca5c6eab88db0ba7c114",
"82a4de403c604fe40aa3804c5ada6af54c425c0576980b50f259d32dc1a0fcff",
"5c4fb87b3812982759ed7264676e713e4e477a41759261515b04797db393ef62",
"f3f6b9134c0fdd670767fbf478fd0dd3430f195ce9c21cabb84f3c1dd4848a11",
];
const PRICE_1: u64 = 49493;
const ATTESTATIONS_1: [&'static str; 20] = [
"605f458e9a7bd216ff522e45f6cd14378c03ccfd4d35a69b9b6ce5c4ebfc89fa",
"edc7215277d2c24a7a4659ff8831352db609fcc467fead5e27fdada172cdfd86",
"1c2d76fcbe724b1fabd2622b991e90bbb2ea9244489de960747134c9fd695dcb",
"26b4f078c9ca2233b18b0e42c4bb9867e5de8ee35b500e30b28d9b1742322e49",
"2b59aeaacb80056b45dc12d6525d5c75343ef75730623c8d9893e2b681bf4b85",
"782e38e777d527e7cb0028a6d03e8f760c6202dbc5ac605f67f995919dee6182",
"a902f37f71a78e4bcf431a778024bd775db6d7ade0626a9e7bc4cdf0b1e52dfd",
"3927eb5ef3b56817c08709e0af1bb643ad4d95dbf5a92a49e1e9c8c811e929c4",
"9ff44fa9d8377a3531792cd6362e4a5b24b86d85602749d301f8449859065b77",
"6a2156ff0aaef174b36d5f8adc597fdcb26f306f7ef6e9a485faabc8eb29da2e",
"53445b507c0de312959fe4566b82db93987dd0b854f1a33bbad7768512bcaf69",
"793c40e0ec3a830c46658bfaed7df74e3fc6781e421e00db5b5f46b26ce4d092",
"db7f800da2f22878c8fc8368047308146e1ebd6316c389303c07ebeed7488fc9",
"73921d09e0d567a03f3a411c0f3455f9f652bbede808a694cca0fa94619f5ba9",
"3d4bd70d93f20aa6b1621ccd077c90bcdee47ce2bae15155434a77a3153a3235",
"90fc10577ab737e311b43288a266490f222a6ecb9f9667e01d7a54c0437d145f",
"51d350616c6fdf90254240b757184fc0dd226328adb42be214ec25832854950e",
"bab3a6269e172ac590fd36683724f087b4add293bb0ee4ef3d21fb5929985c75",
"d65a4c71062fc0b0210bb3e239f60d826a37d28caadfc52edd7afde6e91ff818",
"ea5dfd972784808a15543f850c7bc86bff2b51cff81ec68fc4c3977d5e7d38de",
];
}
fn assert_contains_cets_for_event(cets: &[Cets], event: &Announcement) {
assert!(!cets
.iter()
.find(|cet| cet.event.id == event.id)
.expect("cet to correspond to existing event")
.cets
.is_empty());
}

2
daemon/src/model/cfd.rs

@ -1068,6 +1068,6 @@ pub struct Dlc {
/// The fully signed lock transaction ready to be published on chain
pub lock: (Transaction, Descriptor<PublicKey>),
pub commit: (Transaction, EcdsaAdaptorSignature, Descriptor<PublicKey>),
pub cets: Vec<(Transaction, EcdsaAdaptorSignature, RangeInclusive<u64>)>,
pub cets: HashMap<String, Vec<(Transaction, EcdsaAdaptorSignature, RangeInclusive<u64>)>>,
pub refund: (Transaction, Signature),
}

18
daemon/src/monitor.rs

@ -25,7 +25,7 @@ pub struct StartMonitoring {
pub struct MonitorParams {
lock: (Txid, Descriptor<PublicKey>),
commit: (Txid, Descriptor<PublicKey>),
cets: Vec<(Txid, Script, RangeInclusive<u64>)>,
cets: HashMap<String, Vec<(Txid, Script, RangeInclusive<u64>)>>,
refund: (Txid, Script, u32),
}
@ -221,6 +221,9 @@ where
async fn handle_oracle_attestation(&mut self, attestation: oracle::Attestation) -> Result<()> {
for (order_id, MonitorParams { cets, .. }) in self.cfds.clone().into_iter() {
let cets = cets
.get(&attestation.id)
.context("No CET for oracle event found")?;
let (txid, script_pubkey) =
match cets.iter().find_map(|(txid, script_pubkey, range)| {
range
@ -468,9 +471,16 @@ impl MonitorParams {
commit: (dlc.commit.0.txid(), dlc.commit.2),
cets: dlc
.cets
.into_iter()
.map(|(tx, _, range)| (tx.txid(), script_pubkey.clone(), range))
.collect(),
.iter()
.map(|(event_id, cets)| {
(
event_id.clone(),
cets.iter()
.map(|(tx, _, range)| (tx.txid(), script_pubkey.clone(), range.clone()))
.collect::<Vec<_>>(),
)
})
.collect::<HashMap<_, _>>(),
refund: (
dlc.refund.0.txid(),
script_pubkey,

103
daemon/src/setup_contract.rs

@ -9,14 +9,20 @@ use bdk::descriptor::Descriptor;
use cfd_protocol::secp256k1_zkp::EcdsaAdaptorSignature;
use cfd_protocol::{
commit_descriptor, compute_adaptor_pk, create_cfd_transactions, interval, lock_descriptor,
spending_tx_sighash, PartyParams, PunishParams,
spending_tx_sighash, Announcement, PartyParams, PunishParams,
};
use futures::stream::FusedStream;
use futures::{Sink, SinkExt, StreamExt};
use std::collections::HashMap;
use std::iter::FromIterator;
use std::ops::RangeInclusive;
/// Given an initial set of parameters, sets up the CFD contract with the other party.
/// Given an initial set of parameters, sets up the CFD contract with
/// the other party.
///
/// TODO: Replace `nonce_pks` argument with set of
/// `daemon::oracle::Announcement`, which can be mapped into
/// `cfd_protocol::Announcement`.
pub async fn new(
mut sink: impl Sink<SetupMsg, Error = anyhow::Error> + Unpin,
mut stream: impl FusedStream<Item = SetupMsg> + Unpin,
@ -61,17 +67,23 @@ pub async fn new(
)
}
let payouts = payout_curve::calculate(
cfd.order.price,
cfd.quantity_usd,
params.maker().lock_amount,
(params.taker().lock_amount, cfd.order.leverage),
)?;
let payouts = HashMap::from_iter([(
Announcement {
id: "dummy_id_to_be_replaced".to_string(),
nonce_pks: nonce_pks.clone(),
},
payout_curve::calculate(
cfd.order.price,
cfd.quantity_usd,
params.maker().lock_amount,
(params.taker().lock_amount, cfd.order.leverage),
)?,
)]);
let own_cfd_txs = create_cfd_transactions(
(params.maker().clone(), *params.maker_punish()),
(params.taker().clone(), *params.taker_punish()),
(oracle_pk, &nonce_pks),
oracle_pk,
(
model::cfd::Cfd::CET_TIMELOCK,
cfd.refund_timelock_in_blocks(),
@ -84,6 +96,7 @@ pub async fn new(
sink.send(SetupMsg::Msg1(Msg1::from(own_cfd_txs.clone())))
.await
.context("Failed to send Msg1")?;
let msg1 = stream
.select_next_some()
.await
@ -122,15 +135,22 @@ pub async fn new(
)
.context("Punish adaptor signature does not verify")?;
verify_cets(
(&oracle_pk, &[]),
&params.other,
&own_cets,
&msg1.cets,
&commit_desc,
commit_amount,
)
.context("CET signatures don't verify")?;
for own_grouped_cets in &own_cets {
let other_cets = msg1
.cets
.get(&own_grouped_cets.event.id)
.context("Expect event to exist in msg")?;
verify_cets(
(&oracle_pk, &nonce_pks),
&params.other,
own_grouped_cets.cets.as_slice(),
other_cets.as_slice(),
&commit_desc,
commit_amount,
)
.context("CET signatures don't verify")?;
}
let lock_tx = own_cfd_txs.lock;
let refund_tx = own_cfd_txs.refund.0;
@ -144,11 +164,6 @@ pub async fn new(
)
.context("Refund signature does not verify")?;
let mut cet_by_digits = own_cets
.into_iter()
.map(|(tx, _, digits)| (digits.range(), (tx, digits)))
.collect::<HashMap<_, _>>();
let mut signed_lock_tx = wallet.sign(lock_tx).await?;
sink.send(SetupMsg::Msg2(Msg2 {
signed_lock: signed_lock_tx.clone(),
@ -168,6 +183,37 @@ pub async fn new(
// need some fallback handling (after x time) to spend the outputs in a different way so the
// other party cannot hold us hostage
let cets = own_cets
.into_iter()
.map(|grouped_cets| {
let event_id = grouped_cets.event.id;
let other_cets = msg1
.cets
.get(&event_id)
.with_context(|| format!("Counterparty CETs for event {} missing", event_id))?;
let cets = grouped_cets
.cets
.into_iter()
.map(|(tx, _, digits)| {
let other_encsig = other_cets
.iter()
.find_map(|(other_range, other_encsig)| {
(other_range == &digits.range()).then(|| other_encsig)
})
.with_context(|| {
format!(
"Missing counterparty adaptor signature for CET corresponding to
price range {:?}",
digits.range()
)
})?;
Ok((tx, *other_encsig, digits.range()))
})
.collect::<Result<Vec<_>>>()?;
Ok((event_id, cets))
})
.collect::<Result<HashMap<_, _>>>()?;
Ok(Dlc {
identity: sk,
identity_counterparty: params.other.identity_pk,
@ -176,16 +222,7 @@ pub async fn new(
address: params.own.address,
lock: (signed_lock_tx.extract_tx(), lock_desc),
commit: (commit_tx, msg1.commit, commit_desc),
cets: msg1
.cets
.into_iter()
.map(|(range, sig)| {
let (cet, digits) = cet_by_digits.remove(&range).context("unknown CET")?;
Ok((cet, sig, digits.range()))
})
.collect::<Result<Vec<_>>>()
.context("Failed to re-map CETs")?,
cets,
refund: (refund_tx, msg1.refund),
})
}

23
daemon/src/wire.rs

@ -10,6 +10,7 @@ use cfd_protocol::secp256k1_zkp::EcdsaAdaptorSignature;
use cfd_protocol::{CfdTransactions, PartyParams, PunishParams};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt;
use std::marker::PhantomData;
use std::ops::RangeInclusive;
@ -228,19 +229,29 @@ impl From<Msg0> for (PartyParams, PunishParams) {
#[derive(Debug, Serialize, Deserialize)]
pub struct Msg1 {
pub commit: EcdsaAdaptorSignature,
pub cets: Vec<(RangeInclusive<u64>, EcdsaAdaptorSignature)>,
pub cets: HashMap<String, Vec<(RangeInclusive<u64>, EcdsaAdaptorSignature)>>,
pub refund: Signature,
}
impl From<CfdTransactions> for Msg1 {
fn from(txs: CfdTransactions) -> Self {
let cets = txs
.cets
.into_iter()
.map(|grouped_cets| {
(
grouped_cets.event.id,
grouped_cets
.cets
.into_iter()
.map(|(_, encsig, digits)| (digits.range(), encsig))
.collect::<Vec<_>>(),
)
})
.collect::<HashMap<_, _>>();
Self {
commit: txs.commit.1,
cets: txs
.cets
.into_iter()
.map(|(_, sig, digits)| (digits.range(), sig))
.collect(),
cets,
refund: txs.refund.1,
}
}

Loading…
Cancel
Save