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::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

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::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)?;

508
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<Option<CfdState>> {
pub fn handle_monitoring_event(&mut self, event: monitor::Event) -> Result<Option<CfdState>> {
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<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() {
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<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!(
"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<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();
@ -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.
///

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::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)?;

Loading…
Cancel
Save