diff --git a/daemon/tests/happy_path.rs b/daemon/tests/happy_path.rs index 4e43be5..2098721 100644 --- a/daemon/tests/happy_path.rs +++ b/daemon/tests/happy_path.rs @@ -5,6 +5,8 @@ use crate::harness::flow::next_cfd; use crate::harness::flow::next_order; use crate::harness::flow::next_some; use crate::harness::init_tracing; +use crate::harness::maia::OliviaData; +use crate::harness::mocks::oracle::dummy_wrong_attestation; use crate::harness::start_both; use crate::harness::Maker; use crate::harness::MakerConfig; @@ -14,6 +16,7 @@ use daemon::connection::ConnectionStatus; use daemon::model::cfd::OrderId; use daemon::model::Usd; use daemon::monitor::Event; +use daemon::oracle; use daemon::projection::CfdState; use daemon::projection::Identity; use maia::secp256k1_zkp::schnorrsig; @@ -129,7 +132,8 @@ async fn taker_takes_order_and_maker_accepts_and_contract_setup() { #[tokio::test] async fn collaboratively_close_an_open_cfd() { let _guard = init_tracing(); - let (mut maker, mut taker, order_id) = start_from_open_cfd_state().await; + let (mut maker, mut taker, order_id) = + start_from_open_cfd_state(OliviaData::example_0().announcement()).await; taker.propose_settlement(order_id).await; @@ -152,6 +156,38 @@ async fn collaboratively_close_an_open_cfd() { assert_next_state!(CfdState::Closed, maker, taker, order_id); } +#[tokio::test] +async fn force_close_an_open_cfd() { + let _guard = init_tracing(); + let oracle_data = OliviaData::example_0(); + let (mut maker, mut taker, order_id) = + start_from_open_cfd_state(oracle_data.announcement()).await; + + // Taker initiates force-closing + taker.force_close(order_id).await; + + deliver_event!(maker, taker, Event::CommitFinality(order_id)); + sleep(Duration::from_secs(5)).await; // need to wait a bit until both transition + assert_next_state!(CfdState::OpenCommitted, maker, taker, order_id); + + // After CetTimelockExpired, we're only waiting for attestation + deliver_event!(maker, taker, Event::CetTimelockExpired(order_id)); + + // Delivering the wrong attestation does not move state to `PendingCet` + deliver_event!(maker, taker, dummy_wrong_attestation()); + sleep(Duration::from_secs(5)).await; // need to wait a bit until both transition + assert_next_state!(CfdState::OpenCommitted, maker, taker, order_id); + + // Delivering correct attestation moves the state `PendingCet` + deliver_event!(maker, taker, oracle_data.attestation()); + sleep(Duration::from_secs(5)).await; // need to wait a bit until both transition + assert_next_state!(CfdState::PendingCet, maker, taker, order_id); + + deliver_event!(maker, taker, Event::CetFinality(order_id)); + sleep(Duration::from_secs(5)).await; // need to wait a bit until both transition + assert_next_state!(CfdState::Closed, maker, taker, order_id); +} + #[tokio::test] async fn taker_notices_lack_of_maker() { let short_interval = Duration::from_secs(1); @@ -217,7 +253,8 @@ async fn maker_notices_lack_of_taker() { /// Hide the implementation detail of arriving at the Cfd open state. /// Useful when reading tests that should start at this point. /// For convenience, returns also OrderId of the opened Cfd. -async fn start_from_open_cfd_state() -> (Maker, Taker, OrderId) { +/// `announcement` is used during Cfd's creation. +async fn start_from_open_cfd_state(announcement: oracle::Announcement) -> (Maker, Taker, OrderId) { let heartbeat_interval = Duration::from_secs(60); let maker_listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); let mut maker = Maker::start( @@ -240,8 +277,14 @@ async fn start_from_open_cfd_state() -> (Maker, Taker, OrderId) { .await .unwrap(); - taker.mocks.mock_oracle_announcement().await; - maker.mocks.mock_oracle_announcement().await; + taker + .mocks + .mock_oracle_announcement_with(announcement.clone()) + .await; + maker + .mocks + .mock_oracle_announcement_with(announcement) + .await; taker.take_order(received.clone(), Usd::new(dec!(5))).await; let (_, _) = next_cfd(taker.cfd_feed(), maker.cfd_feed()).await.unwrap(); diff --git a/daemon/tests/harness/maia.rs b/daemon/tests/harness/maia.rs index 797f0e8..9fd997b 100644 --- a/daemon/tests/harness/maia.rs +++ b/daemon/tests/harness/maia.rs @@ -1,4 +1,5 @@ use daemon::model::BitMexPriceEventId; +use daemon::oracle::Attestation; use daemon::oracle::{self}; use maia::secp256k1_zkp::schnorrsig; use maia::secp256k1_zkp::SecretKey; @@ -59,6 +60,14 @@ impl OliviaData { } } + pub fn attestation(&self) -> Attestation { + Attestation { + id: self.id, + price: self.price, + scalars: self.attestations.clone(), + } + } + const OLIVIA_PK: &'static str = "ddd4636845a90185991826be5a494cde9f4a6947b1727217afedc6292fa4caf7"; diff --git a/daemon/tests/harness/mocks/mod.rs b/daemon/tests/harness/mocks/mod.rs index c8d0bab..1340106 100644 --- a/daemon/tests/harness/mocks/mod.rs +++ b/daemon/tests/harness/mocks/mod.rs @@ -7,6 +7,8 @@ use self::monitor::MonitorActor; use self::oracle::OracleActor; use self::wallet::WalletActor; +use super::maia::OliviaData; + pub mod monitor; pub mod oracle; pub mod wallet; @@ -49,10 +51,18 @@ impl Mocks { } pub async fn mock_oracle_announcement(&mut self) { + self.mock_oracle_announcement_with(OliviaData::example_0().announcement()) + .await; + } + + pub async fn mock_oracle_announcement_with( + &mut self, + announcement: daemon::oracle::Announcement, + ) { self.oracle() .await .expect_get_announcement() - .return_const(Ok(oracle::dummy_announcement())); + .return_const(Ok(announcement)); } pub async fn mock_oracle_monitor_attestation(&mut self) { diff --git a/daemon/tests/harness/mocks/oracle.rs b/daemon/tests/harness/mocks/oracle.rs index a55dc9a..4f94ab6 100644 --- a/daemon/tests/harness/mocks/oracle.rs +++ b/daemon/tests/harness/mocks/oracle.rs @@ -1,10 +1,13 @@ -use crate::harness::maia::OliviaData; +use daemon::model::BitMexPriceEventId; use daemon::oracle; use mockall::*; use std::sync::Arc; +use time::OffsetDateTime; use tokio::sync::Mutex; use xtra_productivity::xtra_productivity; +use crate::harness::maia::OliviaData; + /// Test Stub simulating the Oracle actor. /// Serves as an entrypoint for injected mock handlers. pub struct OracleActor { @@ -50,6 +53,18 @@ pub trait Oracle { } } -pub fn dummy_announcement() -> oracle::Announcement { - OliviaData::example_0().announcement() +/// We do *not* depend on the current time in our tests, the valid combination of +/// announcement/attestation is hard-coded in OliviaData struct (along with event id's). +/// Therefore, an attestation based on current utc time will always be wrong. +pub fn dummy_wrong_attestation() -> oracle::Attestation { + let oracle::Attestation { + id: _, + price, + scalars, + } = OliviaData::example_0().attestation(); + oracle::Attestation { + id: BitMexPriceEventId::with_20_digits(OffsetDateTime::now_utc()), + price, + scalars, + } } diff --git a/daemon/tests/harness/mod.rs b/daemon/tests/harness/mod.rs index 3c4295b..c448dd2 100644 --- a/daemon/tests/harness/mod.rs +++ b/daemon/tests/harness/mod.rs @@ -352,6 +352,15 @@ impl Taker { .unwrap() .unwrap(); } + + pub async fn force_close(&self, order_id: OrderId) { + self.system + .cfd_actor_addr + .send(taker_cfd::Commit { order_id }) + .await + .unwrap() + .unwrap(); + } } /// Deliver monitor event to both actor systems