Browse Source

Split `Cfd#handle` function into multiple

We always constructed the specific enum variant at the call site.
Might as well call a particular function which is simpler.
reconnect-to-maker
Thomas Eizinger 3 years ago
parent
commit
a1dd85f481
No known key found for this signature in database GPG Key ID: 651AC83A6C6C8B96
  1. 11
      daemon/src/cfd_actors.rs
  2. 13
      daemon/src/maker_cfd.rs
  3. 508
      daemon/src/model/cfd.rs
  4. 17
      daemon/src/taker_cfd.rs

11
daemon/src/cfd_actors.rs

@ -1,5 +1,5 @@
use crate::db::load_cfd_by_order_id; 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 crate::{db, monitor, oracle, try_continue, wallet};
use anyhow::{bail, Context, Result}; use anyhow::{bail, Context, Result};
use sqlx::pool::PoolConnection; use sqlx::pool::PoolConnection;
@ -50,7 +50,7 @@ where
.context("Failed to send transaction")?; .context("Failed to send transaction")?;
tracing::info!("CET published with txid {}", txid); 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") 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?; 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 // early exit if there was not state change
// this is for cases where we are already in a final state // this is for cases where we are already in a final state
return Ok(()); return Ok(());
@ -122,7 +122,7 @@ where
.await? .await?
.context("Failed to publish commit tx")?; .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") bail!("If we can get the commit tx we should be able to transition")
} }
@ -160,8 +160,7 @@ where
cfd.role(), cfd.role(),
)); ));
let new_state = let new_state = try_continue!(cfd.handle_oracle_attestation(attestation));
try_continue!(cfd.handle(CfdStateChangeEvent::OracleAttestation(attestation)));
if new_state.is_none() { if new_state.is_none() {
// if we don't transition to a new state after oracle attestation we ignore the cfd // if we don't transition to a new state after oracle attestation we ignore the cfd

13
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::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::db::{insert_order, load_cfd_by_order_id, load_order_by_id};
use crate::model::cfd::{ use crate::model::cfd::{
Cfd, CfdState, CfdStateChangeEvent, CfdStateCommon, CollaborativeSettlement, Dlc, Order, Cfd, CfdState, CfdStateCommon, CollaborativeSettlement, Dlc, Order, OrderId, Origin, Role,
OrderId, Origin, Role, RollOverProposal, SettlementKind, SettlementProposal, UpdateCfdProposal, RollOverProposal, SettlementKind, SettlementProposal, UpdateCfdProposal, UpdateCfdProposals,
UpdateCfdProposals,
}; };
use crate::model::{Price, TakerId, Timestamp, Usd}; use crate::model::{Price, TakerId, Timestamp, Usd};
use crate::monitor::MonitorParams; use crate::monitor::MonitorParams;
@ -875,9 +874,11 @@ where
let (tx, sig_maker) = dlc.close_transaction(proposal)?; let (tx, sig_maker) = dlc.close_transaction(proposal)?;
let own_script_pubkey = dlc.script_pubkey_for(cfd.role()); let own_script_pubkey = dlc.script_pubkey_for(cfd.role());
cfd.handle(CfdStateChangeEvent::ProposalSigned( cfd.handle_proposal_signed(CollaborativeSettlement::new(
CollaborativeSettlement::new(tx.clone(), own_script_pubkey.clone(), proposal.price)?, tx.clone(),
))?; own_script_pubkey.clone(),
proposal.price,
)?)?;
append_cfd_state(&cfd, &mut conn, &self.cfd_feed_actor_inbox).await?; append_cfd_state(&cfd, &mut conn, &self.cfd_feed_actor_inbox).await?;
let spend_tx = dlc.finalize_spend_transaction((tx, sig_maker), sig_taker)?; let spend_tx = dlc.finalize_spend_transaction((tx, sig_maker), sig_taker)?;

508
daemon/src/model/cfd.rs

@ -698,18 +698,15 @@ impl Cfd {
pub const CET_TIMELOCK: u32 = 12; pub const CET_TIMELOCK: u32 = 12;
pub fn handle(&mut self, event: CfdStateChangeEvent) -> Result<Option<CfdState>> { pub fn handle_monitoring_event(&mut self, event: monitor::Event) -> Result<Option<CfdState>> {
use CfdState::*; use CfdState::*;
// TODO: Display impl
tracing::info!("Cfd state change event {:?}", event);
let order_id = self.order.id; let order_id = self.order.id;
// early exit if already final // early exit if already final
if let SetupFailed { .. } | Closed { .. } | Refunded { .. } = self.state.clone() { if let SetupFailed { .. } | Closed { .. } | Refunded { .. } = self.state.clone() {
tracing::trace!( tracing::trace!(
"Ignoring event {:?} because cfd already in state {}", "Ignoring monitoring event {:?} because cfd already in state {}",
event, event,
self.state self.state
); );
@ -717,213 +714,80 @@ impl Cfd {
} }
let new_state = match event { let new_state = match event {
CfdStateChangeEvent::Monitor(event) => match event { monitor::Event::LockFinality(_) => {
monitor::Event::LockFinality(_) => { if let PendingOpen { dlc, .. } = self.state.clone() {
if let PendingOpen { dlc, .. } = self.state.clone() { CfdState::Open {
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 {
common: CfdStateCommon { common: CfdStateCommon {
transition_timestamp: Timestamp::now()?, transition_timestamp: Timestamp::now()?,
}, },
dlc, dlc,
cet_status: if let Some(attestation) = attestation { attestation: None,
CetStatus::OracleSigned(attestation) collaborative_close: None,
} else {
CetStatus::Unprepared
},
} }
} } else if let Open {
monitor::Event::CloseFinality(_) => { dlc,
let collaborative_close = self.collaborative_close().context("No collaborative close after reaching collaborative close finality")?; attestation,
collaborative_close,
CfdState::closed(Payout::CollaborativeClose(collaborative_close)) ..
} = self.state.clone()
}, {
monitor::Event::CetTimelockExpired(_) => match self.state.clone() { CfdState::Open {
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 {
common: CfdStateCommon { common: CfdStateCommon {
transition_timestamp: Timestamp::now()?, transition_timestamp: Timestamp::now()?,
}, },
dlc, dlc,
cet_status: CetStatus::Ready(attestation), attestation,
}, collaborative_close,
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!( } else {
"Cannot transition to OpenCommitted because of unexpected state {}", bail!(
"Cannot transition to Open because of unexpected state {}",
self.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 monitor::Event::CommitFinality(_) => {
.attestation() let (dlc, attestation) = if let PendingCommit {
.context("No attestation available when reaching CET finality")?; dlc, attestation, ..
} = self.state.clone()
CfdState::closed(Payout::Cet(attestation)) {
} (dlc, attestation)
monitor::Event::RevokedTransactionFound(_) => { } else if let PendingOpen {
todo!("Punish bad counterparty") dlc, attestation, ..
} }
}, | Open {
CfdStateChangeEvent::CommitTxSent => { dlc, attestation, ..
let (dlc, attestation ) = match self.state.clone() { } = self.state.clone()
PendingOpen { {
dlc, attestation, .. tracing::debug!(%order_id, "Was in unexpected state {}, jumping ahead to OpenCommitted", self.state);
} => (dlc, attestation), (dlc, attestation)
Open { } else {
dlc, bail!(
attestation, "Cannot transition to OpenCommitted because of unexpected state {}",
.. self.state
} => (dlc, attestation), )
_ => {
bail!(
"Cannot transition to PendingCommit because of unexpected state {}",
self.state
)
}
}; };
PendingCommit { OpenCommitted {
common: CfdStateCommon { common: CfdStateCommon {
transition_timestamp: Timestamp::now()?, transition_timestamp: Timestamp::now()?,
}, },
dlc, dlc,
attestation, cet_status: if let Some(attestation) = attestation {
CetStatus::OracleSigned(attestation)
} else {
CetStatus::Unprepared
},
} }
} }
CfdStateChangeEvent::OracleAttestation(attestation) => match self.state.clone() { monitor::Event::CloseFinality(_) => {
CfdState::PendingOpen { dlc, .. } => CfdState::PendingOpen { let collaborative_close = self.collaborative_close().context(
common: CfdStateCommon { "No collaborative close after reaching collaborative close finality",
transition_timestamp: Timestamp::now()?, )?;
},
dlc, CfdState::closed(Payout::CollaborativeClose(collaborative_close))
attestation: Some(attestation), }
}, monitor::Event::CetTimelockExpired(_) => match self.state.clone() {
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 { CfdState::OpenCommitted {
dlc, dlc,
cet_status: CetStatus::Unprepared, cet_status: CetStatus::Unprepared,
@ -933,11 +797,11 @@ impl Cfd {
transition_timestamp: Timestamp::now()?, transition_timestamp: Timestamp::now()?,
}, },
dlc, dlc,
cet_status: CetStatus::OracleSigned(attestation), cet_status: CetStatus::TimelockExpired,
}, },
CfdState::OpenCommitted { CfdState::OpenCommitted {
dlc, dlc,
cet_status: CetStatus::TimelockExpired, cet_status: CetStatus::OracleSigned(attestation),
.. ..
} => CfdState::OpenCommitted { } => CfdState::OpenCommitted {
common: CfdStateCommon { common: CfdStateCommon {
@ -946,43 +810,239 @@ impl Cfd {
dlc, dlc,
cet_status: CetStatus::Ready(attestation), 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!( _ => bail!(
"Cannot transition to OpenCommitted because of unexpected state {}", "Cannot transition to OpenCommitted because of unexpected state {}",
self.state self.state
), ),
}, },
CfdStateChangeEvent::CetSent => { monitor::Event::RefundTimelockExpired(_) => {
let dlc = self.dlc().context("No DLC available after CET was sent")?; 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 let attestation = self
.attestation() .attestation()
.context("No attestation available after CET was sent")?; .context("No attestation available when reaching CET finality")?;
CfdState::PendingCet { CfdState::closed(Payout::Cet(attestation))
common: CfdStateCommon { }
transition_timestamp: Timestamp::now()?, monitor::Event::RevokedTransactionFound(_) => {
}, todo!("Punish bad counterparty")
dlc, }
attestation, };
}
self.state = new_state.clone();
Ok(Some(new_state))
}
pub fn handle_commit_tx_sent(&mut self) -> Result<Option<CfdState>> {
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() { dlc,
CfdState::Open { attestation,
common, };
dlc,
attestation, Ok(Some(self.state.clone()))
.. }
} => CfdState::Open {
common, pub fn handle_oracle_attestation(
dlc, &mut self,
attestation, attestation: Attestation,
collaborative_close: Some(collaborative_close), ) -> Result<Option<CfdState>> {
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!( dlc,
"Cannot add proposed settlement details to state because of unexpected state {}", attestation: Some(attestation),
self.state
),
}, },
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<Option<CfdState>> {
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<Option<CfdState>> {
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(); self.state = new_state.clone();
@ -1282,18 +1342,6 @@ pub struct NotReadyYet {
cet_status: CetStatus, 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 { pub trait AsBlocks {
/// Calculates the duration in Bitcoin blocks. /// Calculates the duration in Bitcoin blocks.
/// ///

17
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::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::db::{insert_order, load_cfd_by_order_id, load_order_by_id};
use crate::model::cfd::{ use crate::model::cfd::{
Cfd, CfdState, CfdStateChangeEvent, CfdStateCommon, CollaborativeSettlement, Dlc, Order, Cfd, CfdState, CfdStateCommon, CollaborativeSettlement, Dlc, Order, OrderId, Origin, Role,
OrderId, Origin, Role, RollOverProposal, SettlementKind, SettlementProposal, UpdateCfdProposal, RollOverProposal, SettlementKind, SettlementProposal, UpdateCfdProposal, UpdateCfdProposals,
UpdateCfdProposals,
}; };
use crate::model::{BitMexPriceEventId, Price, Timestamp, Usd}; use crate::model::{BitMexPriceEventId, Price, Timestamp, Usd};
use crate::monitor::{self, MonitorParams}; use crate::monitor::{self, MonitorParams};
@ -660,13 +659,11 @@ where
sig_taker, sig_taker,
})?; })?;
cfd.handle(CfdStateChangeEvent::ProposalSigned( cfd.handle_proposal_signed(CollaborativeSettlement::new(
CollaborativeSettlement::new( tx.clone(),
tx.clone(), dlc.script_pubkey_for(cfd.role()),
dlc.script_pubkey_for(cfd.role()), proposal.price,
proposal.price, )?)?;
)?,
))?;
append_cfd_state(&cfd, &mut conn, &self.cfd_feed_actor_inbox).await?; append_cfd_state(&cfd, &mut conn, &self.cfd_feed_actor_inbox).await?;
self.remove_pending_proposal(&order_id)?; self.remove_pending_proposal(&order_id)?;

Loading…
Cancel
Save