Skip to content

Commit

Permalink
chore(tests-e2e): Reintroduce collaborative revert test
Browse files Browse the repository at this point in the history
  • Loading branch information
luckysori committed Jan 23, 2024
1 parent 1395a8b commit db1a539
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 5 deletions.
8 changes: 8 additions & 0 deletions crates/tests-e2e/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ pub fn force_close_dlc_channel() {
block_in_place(move || api::force_close_channel().unwrap());
}

/// Get the ID of the currently open DLC channel, if there is one.
///
/// To call this make sure that you are either outside of a runtime or in a multi-threaded runtime
/// (i.e. use `flavor = "multi_thread"` in a `tokio::test`).
pub fn get_dlc_channel_id() -> Option<String> {
block_in_place(move || api::get_dlc_channel_id().unwrap())
}

// Values mostly taken from `environment.dart`
fn test_config() -> native::config::api::Config {
native::config::api::Config {
Expand Down
41 changes: 36 additions & 5 deletions crates/tests-e2e/src/coordinator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use anyhow::Context;
use anyhow::Result;
use bitcoin::Address;
use reqwest::Client;
use rust_decimal::Decimal;
use serde::Deserialize;
use serde::Serialize;

/// A wrapper over the coordinator HTTP API.
///
Expand Down Expand Up @@ -30,7 +32,7 @@ impl Coordinator {
}

pub async fn sync_node(&self) -> Result<()> {
self.post("/api/admin/sync").await?;
self.post::<()>("/api/admin/sync", None).await?;
Ok(())
}

Expand All @@ -43,10 +45,20 @@ impl Coordinator {
}

pub async fn rollover(&self, dlc_channel_id: &str) -> Result<reqwest::Response> {
self.post(format!("/api/rollover/{dlc_channel_id}").as_str())
self.post::<()>(format!("/api/rollover/{dlc_channel_id}").as_str(), None)
.await
}

pub async fn collaborative_revert(
&self,
request: CollaborativeRevertCoordinatorRequest,
) -> Result<()> {
self.post("/api/admin/channels/revert", Some(request))
.await?;

Ok(())
}

async fn get(&self, path: &str) -> Result<reqwest::Response> {
self.client
.get(format!("{0}{path}", self.host))
Expand All @@ -57,9 +69,20 @@ impl Coordinator {
.context("Coordinator did not return 200 OK")
}

async fn post(&self, path: &str) -> Result<reqwest::Response> {
self.client
.post(format!("{0}{path}", self.host))
async fn post<T: Serialize>(&self, path: &str, body: Option<T>) -> Result<reqwest::Response> {
let request = self.client.post(format!("{0}{path}", self.host));

let request = match body {
Some(ref body) => {
let body = serde_json::to_string(body)?;
request
.header("Content-Type", "application/json")
.body(body)
}
None => request,
};

request
.send()
.await
.context("Could not send POST request to coordinator")?
Expand Down Expand Up @@ -112,3 +135,11 @@ pub enum SignedChannelState {
Closing,
CollaborativeCloseOffered,
}

#[derive(Serialize)]
pub struct CollaborativeRevertCoordinatorRequest {
pub channel_id: String,
pub fee_rate_sats_vb: u64,
pub counter_payout: u64,
pub price: Decimal,
}
2 changes: 2 additions & 0 deletions crates/tests-e2e/src/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ impl TestSetup {
tokio::time::sleep(std::time::Duration::from_secs(10)).await;

sync_dlc_channels();
refresh_wallet_info();

setup.coordinator.sync_node().await.unwrap();

setup
Expand Down
87 changes: 87 additions & 0 deletions crates/tests-e2e/tests/collaborative_revert.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#![allow(clippy::unwrap_used)]

use native::api::PaymentFlow;
use native::api::WalletHistoryItemType;
use rust_decimal_macros::dec;
use tests_e2e::app::get_dlc_channel_id;
use tests_e2e::app::refresh_wallet_info;
use tests_e2e::app::sync_dlc_channels;
use tests_e2e::coordinator::CollaborativeRevertCoordinatorRequest;
use tests_e2e::setup;
use tests_e2e::wait_until;

// Use `flavor = "multi_thread"` to be able to call `block_in_place`.
#[tokio::test(flavor = "multi_thread")]
#[ignore = "need to be run with 'just e2e' command"]
async fn can_revert_channel() {
// Arrange

let test = setup::TestSetup::new_with_open_position().await;
let coordinator = &test.coordinator;
let bitcoin = &test.bitcoind;
let app = &test.app;

let position = app.rx.position().unwrap();
let app_margin = position.collateral;

let dlc_channel_id = get_dlc_channel_id().unwrap();

// let coordinator_balance_before = coordinator.get_balance().await.unwrap();
let app_balance_before = app.rx.wallet_info().unwrap().balances.on_chain;

app.rx.channel_status();

// Act

let collaborative_revert_app_payout = app_margin / 2;

coordinator
.collaborative_revert(CollaborativeRevertCoordinatorRequest {
channel_id: dlc_channel_id,
counter_payout: collaborative_revert_app_payout,
price: dec!(40_000),
fee_rate_sats_vb: 1,
})
.await
.unwrap();

// Assert

wait_until!({
bitcoin.mine(1).await.unwrap();

sync_dlc_channels();
refresh_wallet_info();

app.rx.wallet_info().unwrap().balances.on_chain > app_balance_before
});

let wallet_info = app.rx.wallet_info().unwrap();
let collab_revert_entry = wallet_info
.history
.iter()
.filter(|entry| {
matches!(entry.flow, PaymentFlow::Inbound)
&& matches!(entry.wallet_type, WalletHistoryItemType::OnChain { .. })
})
.max_by(|a, b| a.timestamp.cmp(&b.timestamp))
.unwrap();

let total_tx_fee = match collab_revert_entry.wallet_type {
WalletHistoryItemType::OnChain {
fee_sats: Some(fee_sats),
..
} => fee_sats,
_ => unreachable!(),
};

// The transaction fee for the collaborative revert transaction is split evenly among the two
// parties.
let tx_fee = total_tx_fee / 2;

let expected_payout = collaborative_revert_app_payout - tx_fee;

assert_eq!(collab_revert_entry.amount_sats, expected_payout);

// TODO: Check coordinator balance too.
}
8 changes: 8 additions & 0 deletions mobile/native/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use anyhow::ensure;
use anyhow::Context;
use anyhow::Result;
use bdk::FeeRate;
use bitcoin::hashes::hex::ToHex;
use bitcoin::Amount;
use commons::order_matching_fee_taker;
use commons::OrderbookRequest;
Expand Down Expand Up @@ -707,3 +708,10 @@ pub fn get_expiry_timestamp(network: String) -> SyncReturn<i64> {
let network = config::api::parse_network(&network);
SyncReturn(commons::calculate_next_expiry(OffsetDateTime::now_utc(), network).unix_timestamp())
}

pub fn get_dlc_channel_id() -> Result<Option<String>> {
let dlc_channel_id =
ln_dlc::get_signed_dlc_channel()?.map(|channel| channel.channel_id.to_hex());

Ok(dlc_channel_id)
}

0 comments on commit db1a539

Please sign in to comment.