Browse Source

Successful `ContractSetup` phase

no-contract-setup-message
Daniel Karzel 3 years ago
parent
commit
d647a69795
No known key found for this signature in database GPG Key ID: 30C3FC2E438ADB6E
  1. 2
      daemon/src/db.rs
  2. 79
      daemon/src/maker_cfd_actor.rs
  3. 60
      daemon/src/model/cfd.rs
  4. 13
      daemon/src/setup_contract_actor.rs
  5. 42
      daemon/src/taker_cfd_actor.rs

2
daemon/src/db.rs

@ -497,7 +497,7 @@ mod tests {
transition_timestamp: SystemTime::now(),
},
};
insert_new_cfd_state_by_order_id(cfd.order.id, cfd.state, &mut conn)
insert_new_cfd_state_by_order_id(cfd.order.id, cfd.state.clone(), &mut conn)
.await
.unwrap();

79
daemon/src/maker_cfd_actor.rs

@ -1,5 +1,8 @@
use crate::db::{insert_cfd, insert_order, load_all_cfds, load_cfd_by_order_id, load_order_by_id};
use crate::model::cfd::{Cfd, CfdState, CfdStateCommon, FinalizedCfd, Order, OrderId};
use crate::db::{
insert_cfd, insert_new_cfd_state_by_order_id, insert_order, load_all_cfds,
load_cfd_by_order_id, load_order_by_id,
};
use crate::model::cfd::{Cfd, CfdState, CfdStateCommon, Dlc, Order, OrderId};
use crate::model::{TakerId, Usd, WalletInfo};
use crate::wallet::Wallet;
use crate::wire::SetupMsg;
@ -27,7 +30,10 @@ pub enum Command {
id: TakerId,
},
IncProtocolMsg(SetupMsg),
CfdSetupCompleted(FinalizedCfd),
CfdSetupCompleted {
order_id: OrderId,
dlc: Dlc,
},
}
pub fn new(
@ -43,7 +49,11 @@ pub fn new(
mpsc::UnboundedSender<maker_cfd_actor::Command>,
) {
let (sender, mut receiver) = mpsc::unbounded_channel();
// TODO: Move the contract setup into a dedicated actor and send messages to that actor that
// manages the state instead of this ugly buffer
let mut current_contract_setup = None;
let mut contract_setup_message_buffer = vec![];
let mut current_order_id = None;
@ -157,6 +167,8 @@ pub fn new(
.unwrap();
}
maker_cfd_actor::Command::StartContractSetup { taker_id, order_id } => {
println!("CONTRACT SETUP");
// Kick-off the CFD protocol
let (sk, pk) = crate::keypair::new(&mut rand::thread_rng());
@ -185,27 +197,78 @@ pub fn new(
cfd,
);
current_contract_setup = Some(inbox.clone());
for msg in contract_setup_message_buffer.drain(..) {
inbox.send(msg).unwrap();
}
// TODO: Should we do this here or already earlier or after the spawn?
insert_new_cfd_state_by_order_id(
order_id,
CfdState::ContractSetup {
common: CfdStateCommon {
transition_timestamp: SystemTime::now(),
},
},
&mut conn,
)
.await
.unwrap();
cfd_feed_actor_inbox
.send(load_all_cfds(&mut conn).await.unwrap())
.unwrap();
tokio::spawn({
let sender = sender.clone();
async move {
sender
.send(Command::CfdSetupCompleted(actor.await))
.send(Command::CfdSetupCompleted {
order_id,
dlc: actor.await,
})
.unwrap()
}
});
current_contract_setup = Some(inbox);
}
maker_cfd_actor::Command::IncProtocolMsg(msg) => {
let inbox = match &current_contract_setup {
None => panic!("whoops"),
None => {
contract_setup_message_buffer.push(msg);
continue;
}
Some(inbox) => inbox,
};
inbox.send(msg).unwrap();
}
maker_cfd_actor::Command::CfdSetupCompleted(_finalized_cfd) => {
todo!("but what?")
maker_cfd_actor::Command::CfdSetupCompleted { order_id, dlc } => {
println!("Setup complete, publishing on chain now...");
current_contract_setup = None;
contract_setup_message_buffer = vec![];
insert_new_cfd_state_by_order_id(
order_id,
CfdState::PendingOpen {
common: CfdStateCommon {
transition_timestamp: SystemTime::now(),
},
dlc,
},
&mut conn,
)
.await
.unwrap();
cfd_feed_actor_inbox
.send(load_all_cfds(&mut conn).await.unwrap())
.unwrap();
// TODO: Publish on chain and only then transition to open - this might
// require saving some internal state to make sure we are able to monitor
// the publication after a restart
}
}
}

60
daemon/src/model/cfd.rs

@ -3,12 +3,12 @@ use anyhow::Result;
use bdk::bitcoin::secp256k1::{SecretKey, Signature};
use bdk::bitcoin::util::psbt::PartiallySignedTransaction;
use bdk::bitcoin::{Amount, Transaction};
use cfd_protocol::interval;
use cfd_protocol::secp256k1_zkp::EcdsaAdaptorSignature;
use rust_decimal::Decimal;
use rust_decimal_macros::dec;
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::ops::RangeInclusive;
use std::time::{Duration, SystemTime};
use uuid::Uuid;
@ -131,61 +131,83 @@ pub struct CfdStateCommon {
}
// Note: De-/Serialize with type tag to make handling on UI easier
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "type", content = "payload")]
pub enum CfdState {
/// The taker has requested to take a CFD, but has not messaged the maker yet.
///
/// This state only applies to the taker.
TakeRequested { common: CfdStateCommon },
TakeRequested {
common: CfdStateCommon,
},
/// The taker sent an open request to the maker to open the CFD but don't have a response yet.
///
/// This state applies to taker and maker.
/// Initial state for the maker.
PendingTakeRequest { common: CfdStateCommon },
PendingTakeRequest {
common: CfdStateCommon,
},
/// The maker has accepted the CFD take request, but the contract is not set up on chain yet.
///
/// This state applies to taker and maker.
Accepted { common: CfdStateCommon },
Accepted {
common: CfdStateCommon,
},
/// The maker rejected the CFD take request.
///
/// This state applies to taker and maker.
Rejected { common: CfdStateCommon },
Rejected {
common: CfdStateCommon,
},
/// State used during contract setup.
///
/// This state applies to taker and maker.
/// All contract setup messages between taker and maker are expected to be sent in on scope.
ContractSetup { common: CfdStateCommon },
ContractSetup {
common: CfdStateCommon,
},
PendingOpen {
common: CfdStateCommon,
dlc: Dlc,
},
/// The CFD contract is set up on chain.
///
/// This state applies to taker and maker.
Open {
common: CfdStateCommon,
settlement_timestamp: SystemTime,
#[serde(with = "::bdk::bitcoin::util::amount::serde::as_sat")]
margin: Amount,
dlc: Dlc,
},
/// Requested close the position, but we have not passed that on to the blockchain yet.
///
/// This state applies to taker and maker.
CloseRequested { common: CfdStateCommon },
CloseRequested {
common: CfdStateCommon,
},
/// The close transaction (CET) was published on the Bitcoin blockchain but we don't have a
/// confirmation yet.
///
/// This state applies to taker and maker.
PendingClose { common: CfdStateCommon },
PendingClose {
common: CfdStateCommon,
},
/// The close transaction is confirmed with at least one block.
///
/// This state applies to taker and maker.
Closed { common: CfdStateCommon },
Closed {
common: CfdStateCommon,
},
/// Error state
///
/// This state applies to taker and maker.
Error { common: CfdStateCommon },
Error {
common: CfdStateCommon,
},
}
impl CfdState {
@ -196,6 +218,7 @@ impl CfdState {
CfdState::Accepted { common } => common,
CfdState::Rejected { common } => common,
CfdState::ContractSetup { common } => common,
CfdState::PendingOpen { common, .. } => common,
CfdState::Open { common, .. } => common,
CfdState::CloseRequested { common } => common,
CfdState::PendingClose { common } => common,
@ -229,6 +252,9 @@ impl Display for CfdState {
CfdState::ContractSetup { .. } => {
write!(f, "Contract Setup")
}
CfdState::PendingOpen { .. } => {
write!(f, "Pending Open")
}
CfdState::Open { .. } => {
write!(f, "Open")
}
@ -479,14 +505,14 @@ mod tests {
///
/// All contained signatures are the signatures of THE OTHER PARTY.
/// To use any of these transactions, we need to re-sign them with the correct secret key.
#[derive(Debug)]
pub struct FinalizedCfd {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Dlc {
pub identity: SecretKey,
pub revocation: SecretKey,
pub publish: SecretKey,
pub lock: PartiallySignedTransaction,
pub commit: (Transaction, EcdsaAdaptorSignature),
pub cets: Vec<(Transaction, EcdsaAdaptorSignature, interval::Digits)>,
pub cets: Vec<(Transaction, EcdsaAdaptorSignature, RangeInclusive<u64>)>,
pub refund: (Transaction, Signature),
}

13
daemon/src/setup_contract_actor.rs

@ -1,4 +1,4 @@
use crate::model::cfd::{Cfd, FinalizedCfd};
use crate::model::cfd::{Cfd, Dlc};
use crate::wire::{Msg0, Msg1, SetupMsg};
use anyhow::{Context, Result};
use bdk::bitcoin::secp256k1::{schnorrsig, SecretKey, Signature, SECP256K1};
@ -17,7 +17,7 @@ use tokio::sync::mpsc;
/// Given an initial set of parameters, sets up the CFD contract with the other party.
/// Passing OwnParams identifies whether caller is the maker or the taker.
///
/// Returns the [`FinalizedCfd`] which contains the lock transaction, ready to be signed and sent to
/// Returns the [`Dlc`] which contains the lock transaction, ready to be signed and sent to
/// the other party. Signing of the lock transaction is not included in this function because we
/// want the Cfd actor to own the wallet.
pub fn new(
@ -26,10 +26,7 @@ pub fn new(
sk: SecretKey,
oracle_pk: schnorrsig::PublicKey,
cfd: Cfd,
) -> (
impl Future<Output = FinalizedCfd>,
mpsc::UnboundedSender<SetupMsg>,
) {
) -> (impl Future<Output = Dlc>, mpsc::UnboundedSender<SetupMsg>) {
let (sender, mut receiver) = mpsc::unbounded_channel::<SetupMsg>();
let actor = async move {
@ -130,7 +127,7 @@ pub fn new(
.map(|(tx, _, digits)| (digits.range(), (tx, digits)))
.collect::<HashMap<_, _>>();
FinalizedCfd {
Dlc {
identity: sk,
revocation: rev_sk,
publish: publish_sk,
@ -142,7 +139,7 @@ pub fn new(
.map(|(range, sig)| {
let (cet, digits) = cet_by_digits.remove(&range).expect("unknown CET");
(cet, sig, digits)
(cet, sig, digits.range())
})
.collect::<Vec<_>>(),
refund: (refund_tx, msg1.refund),

42
daemon/src/taker_cfd_actor.rs

@ -2,7 +2,7 @@ use crate::db::{
insert_cfd, insert_new_cfd_state_by_order_id, insert_order, load_all_cfds,
load_cfd_by_order_id, load_order_by_id,
};
use crate::model::cfd::{Cfd, CfdState, CfdStateCommon, FinalizedCfd, Order, OrderId};
use crate::model::cfd::{Cfd, CfdState, CfdStateCommon, Dlc, Order, OrderId};
use crate::model::{Usd, WalletInfo};
use crate::wallet::Wallet;
use crate::wire::SetupMsg;
@ -21,7 +21,7 @@ pub enum Command {
NewOrder(Option<Order>),
OrderAccepted(OrderId),
IncProtocolMsg(SetupMsg),
CfdSetupCompleted(FinalizedCfd),
CfdSetupCompleted { order_id: OrderId, dlc: Dlc },
}
pub fn new(
@ -101,6 +101,10 @@ pub fn new(
.await
.unwrap();
out_msg_maker_inbox
.send(wire::TakerToMaker::StartContractSetup(order_id))
.unwrap();
cfd_feed_actor_inbox
.send(load_all_cfds(&mut conn).await.unwrap())
.unwrap();
@ -128,7 +132,10 @@ pub fn new(
async move {
sender
.send(Command::CfdSetupCompleted(actor.await))
.send(Command::CfdSetupCompleted {
order_id,
dlc: actor.await,
})
.unwrap()
}
});
@ -142,10 +149,33 @@ pub fn new(
inbox.send(msg).unwrap();
}
Command::CfdSetupCompleted(_finalized_cfd) => {
todo!("but what?")
Command::CfdSetupCompleted { order_id, dlc } => {
println!("Setup complete, publishing on chain now...");
current_contract_setup = None;
insert_new_cfd_state_by_order_id(
order_id,
CfdState::PendingOpen {
common: CfdStateCommon {
transition_timestamp: SystemTime::now(),
},
dlc,
},
&mut conn,
)
.await
.unwrap();
cfd_feed_actor_inbox
.send(load_all_cfds(&mut conn).await.unwrap())
.unwrap();
// TODO: Some code duplication with maker in this block
// Assumption: The maker publishes the CFD on chain
// TODO: Publish on chain and only then transition to open - this might
// require saving some internal state to make sure we are able to monitor
// the publication after a restart
}
}
}

Loading…
Cancel
Save