diff --git a/daemon/src/cfd_actors.rs b/daemon/src/cfd_actors.rs
index 820504a..6a4692e 100644
--- a/daemon/src/cfd_actors.rs
+++ b/daemon/src/cfd_actors.rs
@@ -1,5 +1,5 @@
use crate::db::load_cfd_by_order_id;
-use crate::model::cfd::{Attestation, Cfd, CfdState, CfdStateChangeEvent, OrderId};
+use crate::model::cfd::{Attestation, Cfd, CfdState, OrderId};
use crate::{db, monitor, oracle, try_continue, wallet};
use anyhow::{bail, Context, Result};
use sqlx::pool::PoolConnection;
@@ -50,7 +50,7 @@ where
.context("Failed to send transaction")?;
tracing::info!("CET published with txid {}", txid);
- if cfd.handle(CfdStateChangeEvent::CetSent)?.is_none() {
+ if cfd.handle_cet_sent()?.is_none() {
bail!("If we can get the CET we should be able to transition")
}
@@ -78,7 +78,7 @@ where
let mut cfd = db::load_cfd_by_order_id(order_id, conn).await?;
- if cfd.handle(CfdStateChangeEvent::Monitor(event))?.is_none() {
+ if cfd.handle_monitoring_event(event)?.is_none() {
// early exit if there was not state change
// this is for cases where we are already in a final state
return Ok(());
@@ -122,7 +122,7 @@ where
.await?
.context("Failed to publish commit tx")?;
- if cfd.handle(CfdStateChangeEvent::CommitTxSent)?.is_none() {
+ if cfd.handle_commit_tx_sent()?.is_none() {
bail!("If we can get the commit tx we should be able to transition")
}
@@ -160,8 +160,7 @@ where
cfd.role(),
));
- let new_state =
- try_continue!(cfd.handle(CfdStateChangeEvent::OracleAttestation(attestation)));
+ let new_state = try_continue!(cfd.handle_oracle_attestation(attestation));
if new_state.is_none() {
// if we don't transition to a new state after oracle attestation we ignore the cfd
diff --git a/daemon/src/maker_cfd.rs b/daemon/src/maker_cfd.rs
index 2b8673f..78f9a8f 100644
--- a/daemon/src/maker_cfd.rs
+++ b/daemon/src/maker_cfd.rs
@@ -1,9 +1,8 @@
use crate::cfd_actors::{self, append_cfd_state, insert_cfd_and_send_to_feed};
use crate::db::{insert_order, load_cfd_by_order_id, load_order_by_id};
use crate::model::cfd::{
- Cfd, CfdState, CfdStateChangeEvent, CfdStateCommon, CollaborativeSettlement, Dlc, Order,
- OrderId, Origin, Role, RollOverProposal, SettlementKind, SettlementProposal, UpdateCfdProposal,
- UpdateCfdProposals,
+ Cfd, CfdState, CfdStateCommon, CollaborativeSettlement, Dlc, Order, OrderId, Origin, Role,
+ RollOverProposal, SettlementKind, SettlementProposal, UpdateCfdProposal, UpdateCfdProposals,
};
use crate::model::{Price, TakerId, Timestamp, Usd};
use crate::monitor::MonitorParams;
@@ -875,9 +874,11 @@ where
let (tx, sig_maker) = dlc.close_transaction(proposal)?;
let own_script_pubkey = dlc.script_pubkey_for(cfd.role());
- cfd.handle(CfdStateChangeEvent::ProposalSigned(
- CollaborativeSettlement::new(tx.clone(), own_script_pubkey.clone(), proposal.price)?,
- ))?;
+ cfd.handle_proposal_signed(CollaborativeSettlement::new(
+ tx.clone(),
+ own_script_pubkey.clone(),
+ proposal.price,
+ )?)?;
append_cfd_state(&cfd, &mut conn, &self.cfd_feed_actor_inbox).await?;
let spend_tx = dlc.finalize_spend_transaction((tx, sig_maker), sig_taker)?;
diff --git a/daemon/src/model/cfd.rs b/daemon/src/model/cfd.rs
index b984987..b657c3e 100644
--- a/daemon/src/model/cfd.rs
+++ b/daemon/src/model/cfd.rs
@@ -698,18 +698,15 @@ impl Cfd {
pub const CET_TIMELOCK: u32 = 12;
- pub fn handle(&mut self, event: CfdStateChangeEvent) -> Result> {
+ pub fn handle_monitoring_event(&mut self, event: monitor::Event) -> Result > {
use CfdState::*;
- // TODO: Display impl
- tracing::info!("Cfd state change event {:?}", event);
-
let order_id = self.order.id;
// early exit if already final
if let SetupFailed { .. } | Closed { .. } | Refunded { .. } = self.state.clone() {
tracing::trace!(
- "Ignoring event {:?} because cfd already in state {}",
+ "Ignoring monitoring event {:?} because cfd already in state {}",
event,
self.state
);
@@ -717,213 +714,80 @@ impl Cfd {
}
let new_state = match event {
- CfdStateChangeEvent::Monitor(event) => match event {
- monitor::Event::LockFinality(_) => {
- if let PendingOpen { dlc, .. } = self.state.clone() {
- CfdState::Open {
- common: CfdStateCommon {
- transition_timestamp: Timestamp::now()?,
- },
- dlc,
- attestation: None,
- collaborative_close: None,
- }
- } else if let Open {
- dlc,
- attestation,
- collaborative_close,
- ..
- } = self.state.clone()
- {
- CfdState::Open {
- common: CfdStateCommon {
- transition_timestamp: Timestamp::now()?,
- },
- dlc,
- attestation,
- collaborative_close,
- }
- } else {
- bail!(
- "Cannot transition to Open because of unexpected state {}",
- self.state
- )
- }
- }
- monitor::Event::CommitFinality(_) => {
- let (dlc, attestation) = if let PendingCommit {
- dlc, attestation, ..
- } = self.state.clone()
- {
- (dlc, attestation)
- } else if let PendingOpen {
- dlc, attestation, ..
- }
- | Open {
- dlc, attestation, ..
- } = self.state.clone()
- {
- tracing::debug!(%order_id, "Was in unexpected state {}, jumping ahead to OpenCommitted", self.state);
- (dlc, attestation)
- } else {
- bail!(
- "Cannot transition to OpenCommitted because of unexpected state {}",
- self.state
- )
- };
-
- OpenCommitted {
+ monitor::Event::LockFinality(_) => {
+ if let PendingOpen { dlc, .. } = self.state.clone() {
+ CfdState::Open {
common: CfdStateCommon {
transition_timestamp: Timestamp::now()?,
},
dlc,
- cet_status: if let Some(attestation) = attestation {
- CetStatus::OracleSigned(attestation)
- } else {
- CetStatus::Unprepared
- },
+ attestation: None,
+ collaborative_close: None,
}
- }
- monitor::Event::CloseFinality(_) => {
- let collaborative_close = self.collaborative_close().context("No collaborative close after reaching collaborative close finality")?;
-
- CfdState::closed(Payout::CollaborativeClose(collaborative_close))
-
- },
- monitor::Event::CetTimelockExpired(_) => match self.state.clone() {
- CfdState::OpenCommitted {
- dlc,
- cet_status: CetStatus::Unprepared,
- ..
- } => CfdState::OpenCommitted {
- common: CfdStateCommon {
- transition_timestamp: Timestamp::now()?,
- },
- dlc,
- cet_status: CetStatus::TimelockExpired,
- },
- CfdState::OpenCommitted {
- dlc,
- cet_status: CetStatus::OracleSigned(attestation),
- ..
- } => CfdState::OpenCommitted {
+ } else if let Open {
+ dlc,
+ attestation,
+ collaborative_close,
+ ..
+ } = self.state.clone()
+ {
+ CfdState::Open {
common: CfdStateCommon {
transition_timestamp: Timestamp::now()?,
},
dlc,
- cet_status: CetStatus::Ready(attestation),
- },
- PendingOpen {
- dlc, attestation, ..
- }
- | Open {
- dlc, attestation, ..
- }
- | PendingCommit {
- dlc, attestation, ..
- } => {
- tracing::debug!(%order_id, "Was in unexpected state {}, jumping ahead to OpenCommitted", self.state);
- CfdState::OpenCommitted {
- common: CfdStateCommon {
- transition_timestamp: Timestamp::now()?,
- },
- dlc,
- cet_status: match attestation {
- None => CetStatus::TimelockExpired,
- Some(attestation) => CetStatus::Ready(attestation),
- },
- }
+ attestation,
+ collaborative_close,
}
- _ => bail!(
- "Cannot transition to OpenCommitted because of unexpected state {}",
+ } else {
+ bail!(
+ "Cannot transition to Open because of unexpected state {}",
self.state
- ),
- },
- monitor::Event::RefundTimelockExpired(_) => {
- let dlc = if let OpenCommitted { dlc, .. } = self.state.clone() {
- dlc
- } else if let Open { dlc, .. } | PendingOpen { dlc, .. } = self.state.clone() {
- tracing::debug!(%order_id, "Was in unexpected state {}, jumping ahead to PendingRefund", self.state);
- dlc
- } else {
- bail!(
- "Cannot transition to PendingRefund because of unexpected state {}",
- self.state
- )
- };
-
- CfdState::must_refund(dlc)
- }
- monitor::Event::RefundFinality(_) => {
- let dlc = self
- .dlc()
- .context("No dlc available when reaching refund finality")?;
-
- CfdState::refunded(dlc)
+ )
}
- monitor::Event::CetFinality(_) => {
- let attestation = self
- .attestation()
- .context("No attestation available when reaching CET finality")?;
-
- CfdState::closed(Payout::Cet(attestation))
- }
- monitor::Event::RevokedTransactionFound(_) => {
- todo!("Punish bad counterparty")
+ }
+ monitor::Event::CommitFinality(_) => {
+ let (dlc, attestation) = if let PendingCommit {
+ dlc, attestation, ..
+ } = self.state.clone()
+ {
+ (dlc, attestation)
+ } else if let PendingOpen {
+ dlc, attestation, ..
}
- },
- CfdStateChangeEvent::CommitTxSent => {
- let (dlc, attestation ) = match self.state.clone() {
- PendingOpen {
- dlc, attestation, ..
- } => (dlc, attestation),
- Open {
- dlc,
- attestation,
- ..
- } => (dlc, attestation),
- _ => {
- bail!(
- "Cannot transition to PendingCommit because of unexpected state {}",
- self.state
- )
- }
+ | Open {
+ dlc, attestation, ..
+ } = self.state.clone()
+ {
+ tracing::debug!(%order_id, "Was in unexpected state {}, jumping ahead to OpenCommitted", self.state);
+ (dlc, attestation)
+ } else {
+ bail!(
+ "Cannot transition to OpenCommitted because of unexpected state {}",
+ self.state
+ )
};
- PendingCommit {
+ OpenCommitted {
common: CfdStateCommon {
transition_timestamp: Timestamp::now()?,
},
dlc,
- attestation,
+ cet_status: if let Some(attestation) = attestation {
+ CetStatus::OracleSigned(attestation)
+ } else {
+ CetStatus::Unprepared
+ },
}
}
- CfdStateChangeEvent::OracleAttestation(attestation) => match self.state.clone() {
- CfdState::PendingOpen { dlc, .. } => CfdState::PendingOpen {
- common: CfdStateCommon {
- transition_timestamp: Timestamp::now()?,
- },
- dlc,
- attestation: Some(attestation),
- },
- CfdState::Open { dlc, .. } => CfdState::Open {
- common: CfdStateCommon {
- transition_timestamp: Timestamp::now()?,
- },
- dlc,
- attestation: Some(attestation),
- collaborative_close: None,
- },
- CfdState::PendingCommit {
- dlc,
- ..
- } => CfdState::PendingCommit {
- common: CfdStateCommon {
- transition_timestamp: Timestamp::now()?,
- },
- dlc,
- attestation: Some(attestation),
- },
+ monitor::Event::CloseFinality(_) => {
+ let collaborative_close = self.collaborative_close().context(
+ "No collaborative close after reaching collaborative close finality",
+ )?;
+
+ CfdState::closed(Payout::CollaborativeClose(collaborative_close))
+ }
+ monitor::Event::CetTimelockExpired(_) => match self.state.clone() {
CfdState::OpenCommitted {
dlc,
cet_status: CetStatus::Unprepared,
@@ -933,11 +797,11 @@ impl Cfd {
transition_timestamp: Timestamp::now()?,
},
dlc,
- cet_status: CetStatus::OracleSigned(attestation),
+ cet_status: CetStatus::TimelockExpired,
},
CfdState::OpenCommitted {
dlc,
- cet_status: CetStatus::TimelockExpired,
+ cet_status: CetStatus::OracleSigned(attestation),
..
} => CfdState::OpenCommitted {
common: CfdStateCommon {
@@ -946,43 +810,239 @@ impl Cfd {
dlc,
cet_status: CetStatus::Ready(attestation),
},
+ PendingOpen {
+ dlc, attestation, ..
+ }
+ | Open {
+ dlc, attestation, ..
+ }
+ | PendingCommit {
+ dlc, attestation, ..
+ } => {
+ tracing::debug!(%order_id, "Was in unexpected state {}, jumping ahead to OpenCommitted", self.state);
+ CfdState::OpenCommitted {
+ common: CfdStateCommon {
+ transition_timestamp: Timestamp::now()?,
+ },
+ dlc,
+ cet_status: match attestation {
+ None => CetStatus::TimelockExpired,
+ Some(attestation) => CetStatus::Ready(attestation),
+ },
+ }
+ }
_ => bail!(
"Cannot transition to OpenCommitted because of unexpected state {}",
self.state
),
},
- CfdStateChangeEvent::CetSent => {
- let dlc = self.dlc().context("No DLC available after CET was sent")?;
+ monitor::Event::RefundTimelockExpired(_) => {
+ let dlc = if let OpenCommitted { dlc, .. } = self.state.clone() {
+ dlc
+ } else if let Open { dlc, .. } | PendingOpen { dlc, .. } = self.state.clone() {
+ tracing::debug!(%order_id, "Was in unexpected state {}, jumping ahead to PendingRefund", self.state);
+ dlc
+ } else {
+ bail!(
+ "Cannot transition to PendingRefund because of unexpected state {}",
+ self.state
+ )
+ };
+
+ CfdState::must_refund(dlc)
+ }
+ monitor::Event::RefundFinality(_) => {
+ let dlc = self
+ .dlc()
+ .context("No dlc available when reaching refund finality")?;
+
+ CfdState::refunded(dlc)
+ }
+ monitor::Event::CetFinality(_) => {
let attestation = self
.attestation()
- .context("No attestation available after CET was sent")?;
+ .context("No attestation available when reaching CET finality")?;
- CfdState::PendingCet {
- common: CfdStateCommon {
- transition_timestamp: Timestamp::now()?,
- },
- dlc,
- attestation,
- }
+ CfdState::closed(Payout::Cet(attestation))
+ }
+ monitor::Event::RevokedTransactionFound(_) => {
+ todo!("Punish bad counterparty")
+ }
+ };
+
+ self.state = new_state.clone();
+
+ Ok(Some(new_state))
+ }
+
+ pub fn handle_commit_tx_sent(&mut self) -> Result > {
+ use CfdState::*;
+
+ // early exit if already final
+ if let SetupFailed { .. } | Closed { .. } | Refunded { .. } = self.state.clone() {
+ tracing::trace!(
+ "Ignoring sent commit transaction because cfd already in state {}",
+ self.state
+ );
+ return Ok(None);
+ }
+ let (dlc, attestation) = match self.state.clone() {
+ PendingOpen {
+ dlc, attestation, ..
+ } => (dlc, attestation),
+ Open {
+ dlc, attestation, ..
+ } => (dlc, attestation),
+ _ => {
+ bail!(
+ "Cannot transition to PendingCommit because of unexpected state {}",
+ self.state
+ )
+ }
+ };
+
+ self.state = PendingCommit {
+ common: CfdStateCommon {
+ transition_timestamp: Timestamp::now()?,
},
- CfdStateChangeEvent::ProposalSigned(collaborative_close) => match self.state.clone() {
- CfdState::Open {
- common,
- dlc,
- attestation,
- ..
- } => CfdState::Open {
- common,
- dlc,
- attestation,
- collaborative_close: Some(collaborative_close),
+ dlc,
+ attestation,
+ };
+
+ Ok(Some(self.state.clone()))
+ }
+
+ pub fn handle_oracle_attestation(
+ &mut self,
+ attestation: Attestation,
+ ) -> Result > {
+ use CfdState::*;
+
+ // early exit if already final
+ if let SetupFailed { .. } | Closed { .. } | Refunded { .. } = self.state.clone() {
+ tracing::trace!(
+ "Ignoring oracle attestation because cfd already in state {}",
+ self.state
+ );
+ return Ok(None);
+ }
+
+ let new_state = match self.state.clone() {
+ CfdState::PendingOpen { dlc, .. } => CfdState::PendingOpen {
+ common: CfdStateCommon {
+ transition_timestamp: Timestamp::now()?,
},
- _ => bail!(
- "Cannot add proposed settlement details to state because of unexpected state {}",
- self.state
- ),
+ dlc,
+ attestation: Some(attestation),
},
+ CfdState::Open { dlc, .. } => CfdState::Open {
+ common: CfdStateCommon {
+ transition_timestamp: Timestamp::now()?,
+ },
+ dlc,
+ attestation: Some(attestation),
+ collaborative_close: None,
+ },
+ CfdState::PendingCommit { dlc, .. } => CfdState::PendingCommit {
+ common: CfdStateCommon {
+ transition_timestamp: Timestamp::now()?,
+ },
+ dlc,
+ attestation: Some(attestation),
+ },
+ CfdState::OpenCommitted {
+ dlc,
+ cet_status: CetStatus::Unprepared,
+ ..
+ } => CfdState::OpenCommitted {
+ common: CfdStateCommon {
+ transition_timestamp: Timestamp::now()?,
+ },
+ dlc,
+ cet_status: CetStatus::OracleSigned(attestation),
+ },
+ CfdState::OpenCommitted {
+ dlc,
+ cet_status: CetStatus::TimelockExpired,
+ ..
+ } => CfdState::OpenCommitted {
+ common: CfdStateCommon {
+ transition_timestamp: Timestamp::now()?,
+ },
+ dlc,
+ cet_status: CetStatus::Ready(attestation),
+ },
+ _ => bail!(
+ "Cannot transition to OpenCommitted because of unexpected state {}",
+ self.state
+ ),
+ };
+
+ self.state = new_state.clone();
+
+ Ok(Some(new_state))
+ }
+
+ pub fn handle_cet_sent(&mut self) -> Result > {
+ use CfdState::*;
+
+ // early exit if already final
+ if let SetupFailed { .. } | Closed { .. } | Refunded { .. } = self.state.clone() {
+ tracing::trace!(
+ "Ignoring pending CET because cfd already in state {}",
+ self.state
+ );
+ return Ok(None);
+ }
+
+ let dlc = self.dlc().context("No DLC available after CET was sent")?;
+ let attestation = self
+ .attestation()
+ .context("No attestation available after CET was sent")?;
+
+ self.state = CfdState::PendingCet {
+ common: CfdStateCommon {
+ transition_timestamp: Timestamp::now()?,
+ },
+ dlc,
+ attestation,
+ };
+
+ Ok(Some(self.state.clone()))
+ }
+
+ pub fn handle_proposal_signed(
+ &mut self,
+ collaborative_close: CollaborativeSettlement,
+ ) -> Result > {
+ use CfdState::*;
+
+ // early exit if already final
+ if let SetupFailed { .. } | Closed { .. } | Refunded { .. } = self.state.clone() {
+ tracing::trace!(
+ "Ignoring collaborative settlement because cfd already in state {}",
+ self.state
+ );
+ return Ok(None);
+ }
+
+ let new_state = match self.state.clone() {
+ CfdState::Open {
+ common,
+ dlc,
+ attestation,
+ ..
+ } => CfdState::Open {
+ common,
+ dlc,
+ attestation,
+ collaborative_close: Some(collaborative_close),
+ },
+ _ => bail!(
+ "Cannot add proposed settlement details to state because of unexpected state {}",
+ self.state
+ ),
};
self.state = new_state.clone();
@@ -1282,18 +1342,6 @@ pub struct NotReadyYet {
cet_status: CetStatus,
}
-#[derive(Debug, Clone)]
-pub enum CfdStateChangeEvent {
- // TODO: group other events by actors into enums and add them here so we can bundle all
- // transitions into cfd.transition_to(...)
- Monitor(monitor::Event),
- CommitTxSent,
- OracleAttestation(Attestation),
- CetSent,
- /// Settlement proposal was signed by the taker and sent to the maker
- ProposalSigned(CollaborativeSettlement),
-}
-
pub trait AsBlocks {
/// Calculates the duration in Bitcoin blocks.
///
diff --git a/daemon/src/taker_cfd.rs b/daemon/src/taker_cfd.rs
index 3ac2ce1..bc7f9a8 100644
--- a/daemon/src/taker_cfd.rs
+++ b/daemon/src/taker_cfd.rs
@@ -1,9 +1,8 @@
use crate::cfd_actors::{self, append_cfd_state, insert_cfd_and_send_to_feed};
use crate::db::{insert_order, load_cfd_by_order_id, load_order_by_id};
use crate::model::cfd::{
- Cfd, CfdState, CfdStateChangeEvent, CfdStateCommon, CollaborativeSettlement, Dlc, Order,
- OrderId, Origin, Role, RollOverProposal, SettlementKind, SettlementProposal, UpdateCfdProposal,
- UpdateCfdProposals,
+ Cfd, CfdState, CfdStateCommon, CollaborativeSettlement, Dlc, Order, OrderId, Origin, Role,
+ RollOverProposal, SettlementKind, SettlementProposal, UpdateCfdProposal, UpdateCfdProposals,
};
use crate::model::{BitMexPriceEventId, Price, Timestamp, Usd};
use crate::monitor::{self, MonitorParams};
@@ -660,13 +659,11 @@ where
sig_taker,
})?;
- cfd.handle(CfdStateChangeEvent::ProposalSigned(
- CollaborativeSettlement::new(
- tx.clone(),
- dlc.script_pubkey_for(cfd.role()),
- proposal.price,
- )?,
- ))?;
+ cfd.handle_proposal_signed(CollaborativeSettlement::new(
+ tx.clone(),
+ dlc.script_pubkey_for(cfd.role()),
+ proposal.price,
+ )?)?;
append_cfd_state(&cfd, &mut conn, &self.cfd_feed_actor_inbox).await?;
self.remove_pending_proposal(&order_id)?;