diff --git a/crates/ln-dlc-node/src/tests/dlc/collaborative_settlement.rs b/crates/ln-dlc-node/src/tests/dlc/collaborative_settlement.rs deleted file mode 100644 index 5ed10ef4b..000000000 --- a/crates/ln-dlc-node/src/tests/dlc/collaborative_settlement.rs +++ /dev/null @@ -1,200 +0,0 @@ -use crate::node::InMemoryStore; -use crate::node::Node; -use crate::storage::TenTenOneInMemoryStorage; -use crate::tests::dlc::create::create_dlc_channel; -use crate::tests::init_tracing; -use crate::tests::wait_until_sub_channel_state; -use crate::tests::SubChannelStateName; -use anyhow::Context; -use anyhow::Result; -use bitcoin::Amount; -use std::time::Duration; - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn dlc_collaborative_settlement_test() { - init_tracing(); - - // Arrange - - let app_dlc_collateral = 50_000; - let coordinator_dlc_collateral = 25_000; - - let app_ln_balance = app_dlc_collateral * 2; - let coordinator_ln_balance = coordinator_dlc_collateral * 2; - - let fund_amount = (app_ln_balance + coordinator_ln_balance) * 2; - - let (app, _running_app) = Node::start_test_app("app").unwrap(); - let (coordinator, _running_coord) = Node::start_test_coordinator("coordinator").unwrap(); - - app.connect(coordinator.info).await.unwrap(); - - coordinator - .fund(Amount::from_sat(fund_amount)) - .await - .unwrap(); - - coordinator - .open_private_channel(&app, coordinator_ln_balance, app_ln_balance) - .await - .unwrap(); - - let app_balance_channel_creation = app.get_ldk_balance().available(); - let coordinator_balance_channel_creation = coordinator.get_ldk_balance().available(); - - create_dlc_channel( - &app, - &coordinator, - app_dlc_collateral, - coordinator_dlc_collateral, - ) - .await - .unwrap(); - - // The underlying API expects the settlement amount of the party who originally _accepted_ the - // channel. Since we know in this case that the coordinator accepted the DLC channel, here we - // specify the coordinator's settlement amount. - let coordinator_settlement_amount = coordinator_dlc_collateral / 2; - - // Act - - dlc_collaborative_settlement(&app, &coordinator, coordinator_settlement_amount) - .await - .unwrap(); - - // Assert - - let coordinator_loss_amount = coordinator_dlc_collateral - coordinator_settlement_amount; - - let app_balance_after = app.get_ldk_balance().available(); - let coordinator_balance_after = coordinator.get_ldk_balance().available(); - - assert_eq!( - app_balance_channel_creation + coordinator_loss_amount, - app_balance_after - ); - - assert_eq!( - coordinator_balance_channel_creation, - coordinator_balance_after + coordinator_loss_amount - ); -} - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn open_dlc_channel_after_closing_dlc_channel() { - init_tracing(); - - // Arrange - - let app_dlc_collateral = 50_000; - let coordinator_dlc_collateral = 25_000; - - let app_ln_balance = app_dlc_collateral * 2; - let coordinator_ln_balance = coordinator_dlc_collateral * 2; - - let fund_amount = (app_ln_balance + coordinator_ln_balance) * 2; - - let (app, _running_app) = Node::start_test_app("app").unwrap(); - let (coordinator, _running_coord) = Node::start_test_coordinator("coordinator").unwrap(); - - app.connect(coordinator.info).await.unwrap(); - - coordinator - .fund(Amount::from_sat(fund_amount)) - .await - .unwrap(); - - coordinator - .open_private_channel(&app, coordinator_ln_balance, app_ln_balance) - .await - .unwrap(); - - create_dlc_channel( - &app, - &coordinator, - app_dlc_collateral, - coordinator_dlc_collateral, - ) - .await - .unwrap(); - - let coordinator_settlement_amount = coordinator_dlc_collateral / 2; - dlc_collaborative_settlement(&app, &coordinator, coordinator_settlement_amount) - .await - .unwrap(); - - // Act and assert - - create_dlc_channel( - &app, - &coordinator, - app_dlc_collateral, - coordinator_dlc_collateral, - ) - .await - .unwrap(); -} - -async fn dlc_collaborative_settlement( - app: &Node, - coordinator: &Node, - coordinator_settlement_amount: u64, -) -> Result<()> { - let channel_details = app - .channel_manager - .list_usable_channels() - .iter() - .find(|c| c.counterparty.node_id == coordinator.info.pubkey) - .context("Could not find usable channel with peer")? - .clone(); - - app.propose_sub_channel_collaborative_settlement( - channel_details.channel_id, - coordinator_settlement_amount, - ) - .await?; - - // Process the app's `CloseOffer` - let sub_channel = wait_until_sub_channel_state( - Duration::from_secs(30), - coordinator, - app.info.pubkey, - SubChannelStateName::CloseOffered, - ) - .await?; - - coordinator.accept_sub_channel_collaborative_settlement(&sub_channel.channel_id)?; - - // Process the coordinator's `CloseAccept` and send `CloseConfirm` - wait_until_sub_channel_state( - Duration::from_secs(30), - app, - coordinator.info.pubkey, - SubChannelStateName::CloseConfirmed, - ) - .await?; - - // Assert - - // Process the app's `CloseConfirm` and send `CloseFinalize` - wait_until_sub_channel_state( - Duration::from_secs(30), - coordinator, - app.info.pubkey, - SubChannelStateName::OffChainClosed, - ) - .await?; - - // Process the coordinator's `CloseFinalize` - wait_until_sub_channel_state( - Duration::from_secs(30), - app, - coordinator.info.pubkey, - SubChannelStateName::OffChainClosed, - ) - .await?; - - Ok(()) -} diff --git a/crates/ln-dlc-node/src/tests/dlc/create.rs b/crates/ln-dlc-node/src/tests/dlc/create.rs deleted file mode 100644 index 917f319cc..000000000 --- a/crates/ln-dlc-node/src/tests/dlc/create.rs +++ /dev/null @@ -1,131 +0,0 @@ -use crate::node::InMemoryStore; -use crate::node::Node; -use crate::storage::TenTenOneInMemoryStorage; -use crate::tests::dummy_contract_input; -use crate::tests::init_tracing; -use crate::tests::wait_for_n_usable_channels; -use crate::tests::wait_until_sub_channel_state; -use crate::tests::SubChannelStateName; -use anyhow::Context; -use anyhow::Result; -use bitcoin::Amount; -use std::time::Duration; - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn given_lightning_channel_then_can_add_dlc_channel() { - init_tracing(); - - // Arrange - - let app_dlc_collateral = 50_000; - let coordinator_dlc_collateral = 25_000; - - let app_ln_balance = app_dlc_collateral * 2; - let coordinator_ln_balance = coordinator_dlc_collateral * 2; - - let fund_amount = (app_ln_balance + coordinator_ln_balance) * 2; - - let (app, _running_app) = Node::start_test_app("app").unwrap(); - let (coordinator, _running_coord) = Node::start_test_coordinator("coordinator").unwrap(); - - app.connect(coordinator.info).await.unwrap(); - - coordinator - .fund(Amount::from_sat(fund_amount)) - .await - .unwrap(); - - coordinator - .open_private_channel(&app, coordinator_ln_balance, app_ln_balance) - .await - .unwrap(); - - // Act and assert - - create_dlc_channel( - &app, - &coordinator, - app_dlc_collateral, - coordinator_dlc_collateral, - ) - .await - .unwrap(); -} - -pub async fn create_dlc_channel( - offer_node: &Node, - accept_node: &Node, - app_dlc_collateral: u64, - coordinator_dlc_collateral: u64, -) -> Result<()> { - // Act - - let oracle_pk = *offer_node.oracle_pk().first().unwrap(); - let contract_input = - dummy_contract_input(app_dlc_collateral, coordinator_dlc_collateral, oracle_pk); - - wait_for_n_usable_channels(1, offer_node).await?; - let channel_details = offer_node - .channel_manager - .list_usable_channels() - .iter() - .find(|c| c.counterparty.node_id == accept_node.info.pubkey) - .context("Could not find usable channel with peer")? - .clone(); - - offer_node - .propose_sub_channel(channel_details.clone(), contract_input) - .await?; - - // Process the app's `Offer` - let sub_channel = wait_until_sub_channel_state( - Duration::from_secs(30), - accept_node, - offer_node.info.pubkey, - SubChannelStateName::Offered, - ) - .await?; - - accept_node.accept_sub_channel_offer(&sub_channel.channel_id)?; - - // Process the coordinator's `Accept` and send `Confirm` - wait_until_sub_channel_state( - Duration::from_secs(30), - offer_node, - accept_node.info.pubkey, - SubChannelStateName::Confirmed, - ) - .await?; - - // Process the app's `Confirm` and send `Finalize` - wait_until_sub_channel_state( - Duration::from_secs(30), - accept_node, - offer_node.info.pubkey, - SubChannelStateName::Finalized, - ) - .await?; - - // Assert - - // Process the coordinator's `Finalize` and send `Revoke` - wait_until_sub_channel_state( - Duration::from_secs(30), - offer_node, - accept_node.info.pubkey, - SubChannelStateName::Signed, - ) - .await?; - - // Process the app's `Revoke` - wait_until_sub_channel_state( - Duration::from_secs(30), - accept_node, - offer_node.info.pubkey, - SubChannelStateName::Signed, - ) - .await?; - - Ok(()) -} diff --git a/crates/ln-dlc-node/src/tests/dlc/dlc_setup_with_reconnects.rs b/crates/ln-dlc-node/src/tests/dlc/dlc_setup_with_reconnects.rs deleted file mode 100644 index 0a35ad3f1..000000000 --- a/crates/ln-dlc-node/src/tests/dlc/dlc_setup_with_reconnects.rs +++ /dev/null @@ -1,630 +0,0 @@ -use crate::node::sub_channel::sub_channel_manager_periodic_check; -use crate::node::Node; -use crate::tests::dlc::create::create_dlc_channel; -use crate::tests::dummy_contract_input; -use crate::tests::init_tracing; -use crate::tests::wait_for_n_usable_channels; -use crate::tests::wait_until; -use crate::tests::wait_until_sub_channel_state; -use crate::tests::SubChannelStateName; -use anyhow::Context; -use bitcoin::Amount; -use dlc_manager::subchannel::SubChannelState; -use dlc_manager::Storage; -use std::time::Duration; - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn reconnecting_during_dlc_channel_setup() { - init_tracing(); - - // Arrange - - let (app, _running_app) = Node::start_test_app("app").unwrap(); - let (coordinator, _running_coord) = Node::start_test_coordinator("coordinator").unwrap(); - - let coordinator_info = coordinator.info; - - app.connect(coordinator_info).await.unwrap(); - - coordinator - .fund(Amount::from_sat(10_000_000)) - .await - .unwrap(); - - coordinator - .open_private_channel(&app, 50_000, 50_000) - .await - .unwrap(); - - wait_for_n_usable_channels(1, &coordinator).await.unwrap(); - let channel_details = app - .channel_manager - .list_usable_channels() - .iter() - .find(|c| c.counterparty.node_id == coordinator_info.pubkey) - .context("No usable channels for app") - .unwrap() - .clone(); - - // Act - - let oracle_pk = *app.oracle_pk().first().unwrap(); - let contract_input = dummy_contract_input(20_000, 20_000, oracle_pk); - - app.propose_sub_channel(channel_details.clone(), contract_input) - .await - .unwrap(); - - // Process the app's `Offer`. - let sub_channel = wait_until_sub_channel_state( - Duration::from_secs(30), - &coordinator, - app.info.pubkey, - SubChannelStateName::Offered, - ) - .await - .unwrap(); - - app.disconnect(coordinator.info); - - // We need to wait for the channel to not be usable after the disconnect. - wait_until(Duration::from_secs(5), || async { - Ok(coordinator.list_usable_channels().is_empty().then_some(())) - }) - .await - .unwrap(); - - // Assert that `accept_dlc_channel_offer` fails if the peer is disconnected (do not panic as in - // https://github.com/get10101/10101/issues/760). - assert!(coordinator - .accept_sub_channel_offer(&sub_channel.channel_id) - .is_err()); - - app.connect(coordinator.info).await.unwrap(); - - coordinator - .accept_sub_channel_offer(&sub_channel.channel_id) - .unwrap(); - - // Process the coordinator's `Accept` and send `Confirm` - wait_until_sub_channel_state( - Duration::from_secs(30), - &app, - coordinator.info.pubkey, - SubChannelStateName::Confirmed, - ) - .await - .unwrap(); - - // Wait for the `Confirm` message to be delivered. - wait_until(Duration::from_secs(5), || async { - Ok(coordinator - .dlc_message_handler - .has_pending_messages_to_process() - .then_some(())) - }) - .await - .unwrap(); - - app.reconnect(coordinator_info).await.unwrap(); - - // Process the app's first `Confirm` message. This should fail because of an invalid commitment - // transaction number. - coordinator - .process_incoming_messages() - .expect_err("should have invalid commitment transaction number"); - - // Create second `Accept` message from pending `ReAccept` action. - sub_channel_manager_periodic_check( - coordinator.sub_channel_manager.clone(), - &coordinator.dlc_message_handler, - &coordinator.peer_manager, - ) - .await - .unwrap(); - - // Process the coordinator's second `Accept` and send `Confirm`. - wait_until_sub_channel_state( - Duration::from_secs(30), - &app, - coordinator.info.pubkey, - SubChannelStateName::Confirmed, - ) - .await - .unwrap(); - - // Process the app's second `Confirm` message and send `Finalize`. - wait_until_sub_channel_state( - Duration::from_secs(30), - &coordinator, - app.info.pubkey, - SubChannelStateName::Finalized, - ) - .await - .unwrap(); - - // Process the coordinator's `Finalize` message and send `Revoke`. - wait_until_sub_channel_state( - Duration::from_secs(30), - &app, - coordinator.info.pubkey, - SubChannelStateName::Signed, - ) - .await - .unwrap(); - - // Process the app's `Revoke` message. - wait_until_sub_channel_state( - Duration::from_secs(30), - &coordinator, - app.info.pubkey, - SubChannelStateName::Signed, - ) - .await - .unwrap(); - - let coordinator_settlement_amount = 12_500; - app.propose_sub_channel_collaborative_settlement( - channel_details.channel_id, - coordinator_settlement_amount, - ) - .await - .unwrap(); - - // Process the app's `CloseOffer`. - let sub_channel = wait_until_sub_channel_state( - Duration::from_secs(30), - &coordinator, - app.info.pubkey, - SubChannelStateName::CloseOffered, - ) - .await - .unwrap(); - - coordinator - .accept_sub_channel_collaborative_settlement(&sub_channel.channel_id) - .unwrap(); - - // Process the coordinator's `CloseAccept` and send `CloseConfirm`. - wait_until_sub_channel_state( - Duration::from_secs(30), - &app, - coordinator.info.pubkey, - SubChannelStateName::CloseConfirmed, - ) - .await - .unwrap(); - - // Wait for the `CloseConfirm` message to be delivered. - wait_until(Duration::from_secs(5), || async { - Ok(coordinator - .dlc_message_handler - .has_pending_messages_to_process() - .then_some(())) - }) - .await - .unwrap(); - - app.reconnect(coordinator_info).await.unwrap(); - - // Process the app's first `CloseConfirm` message. This should fail because of an invalid - // commitment transaction number. - coordinator - .process_incoming_messages() - .expect_err("should have invalid commitment transaction number"); - - // Create second `CloseAccept` message from pending `ReAcceptCloseOffer` action. - sub_channel_manager_periodic_check( - coordinator.sub_channel_manager.clone(), - &coordinator.dlc_message_handler, - &coordinator.peer_manager, - ) - .await - .unwrap(); - - // Process the coordinator's second `CloseAccept` and send `CloseConfirm`. - wait_until_sub_channel_state( - Duration::from_secs(30), - &app, - coordinator.info.pubkey, - SubChannelStateName::CloseConfirmed, - ) - .await - .unwrap(); - - // Process the app's `CloseConfirm` and send `CloseFinalize`. - wait_until_sub_channel_state( - Duration::from_secs(30), - &coordinator, - app.info.pubkey, - SubChannelStateName::OffChainClosed, - ) - .await - .unwrap(); - - // Process the coordinator's `CloseFinalize`. - wait_until_sub_channel_state( - Duration::from_secs(30), - &app, - coordinator.info.pubkey, - SubChannelStateName::OffChainClosed, - ) - .await - .unwrap(); -} - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn can_lose_connection_before_processing_subchannel_close_finalize() { - init_tracing(); - - // Arrange - - let app_dlc_collateral = 25_000; - let coordinator_dlc_collateral = 50_000; - - let app_ln_balance = app_dlc_collateral * 2; - let coordinator_ln_balance = coordinator_dlc_collateral * 2; - - let fund_amount = (app_ln_balance + coordinator_ln_balance) * 2; - - let (app, _running_app) = Node::start_test_app("app").unwrap(); - let (coordinator, _running_coord) = Node::start_test_coordinator("coordinator").unwrap(); - - app.connect(coordinator.info).await.unwrap(); - - coordinator - .fund(Amount::from_sat(fund_amount)) - .await - .unwrap(); - - coordinator - .open_private_channel(&app, coordinator_ln_balance, app_ln_balance) - .await - .unwrap(); - - create_dlc_channel( - &coordinator, - &app, - coordinator_dlc_collateral, - app_dlc_collateral, - ) - .await - .unwrap(); - - let channel_details = coordinator - .channel_manager - .list_usable_channels() - .iter() - .find(|c| c.counterparty.node_id == app.info.pubkey) - .unwrap() - .clone(); - - coordinator - .propose_sub_channel_collaborative_settlement( - channel_details.channel_id, - app_dlc_collateral / 2, - ) - .await - .unwrap(); - - // Process `CloseOffer` - let sub_channel = wait_until_sub_channel_state( - Duration::from_secs(30), - &app, - coordinator.info.pubkey, - SubChannelStateName::CloseOffered, - ) - .await - .unwrap(); - - app.accept_sub_channel_collaborative_settlement(&sub_channel.channel_id) - .unwrap(); - - // Process `CloseAccept` and send `CloseConfirm` - wait_until_sub_channel_state( - Duration::from_secs(30), - &coordinator, - app.info.pubkey, - SubChannelStateName::CloseConfirmed, - ) - .await - .unwrap(); - - // Act - - // Process `CloseConfirm` and send `CloseFinalize` - wait_until_sub_channel_state( - Duration::from_secs(30), - &app, - coordinator.info.pubkey, - SubChannelStateName::OffChainClosed, - ) - .await - .unwrap(); - - wait_until(Duration::from_secs(5), || async { - Ok(coordinator - .dlc_message_handler - .has_pending_messages_to_process() - .then_some(())) - }) - .await - .unwrap(); - - app.reconnect(coordinator.info).await.unwrap(); - - coordinator.process_incoming_messages().unwrap(); - - // Assert - - let state = coordinator - .dlc_manager - .get_store() - .get_sub_channels() - .unwrap() - .first() - .unwrap() - .state - .clone(); - - assert!(matches!(state, SubChannelState::OffChainClosed)); -} - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn can_lose_connection_before_processing_subchannel_accept() { - init_tracing(); - - // Arrange - - let app_dlc_collateral = 25_000; - let coordinator_dlc_collateral = 50_000; - - let app_ln_balance = app_dlc_collateral * 2; - let coordinator_ln_balance = coordinator_dlc_collateral * 2; - - let fund_amount = (app_ln_balance + coordinator_ln_balance) * 2; - - let (app, _running_app) = Node::start_test_app("app").unwrap(); - let (coordinator, _running_coord) = Node::start_test_coordinator("coordinator").unwrap(); - - app.connect(coordinator.info).await.unwrap(); - - coordinator - .fund(Amount::from_sat(fund_amount)) - .await - .unwrap(); - - coordinator - .open_private_channel(&app, coordinator_ln_balance, app_ln_balance) - .await - .unwrap(); - - let oracle_pk = *app.oracle_pk().first().unwrap(); - let contract_input = - dummy_contract_input(coordinator_dlc_collateral, app_dlc_collateral, oracle_pk); - - wait_for_n_usable_channels(1, &coordinator).await.unwrap(); - let channel_details = coordinator - .channel_manager - .list_usable_channels() - .iter() - .find(|c| c.counterparty.node_id == app.info.pubkey) - .context("Could not find usable channel with peer") - .unwrap() - .clone(); - - coordinator - .propose_sub_channel(channel_details.clone(), contract_input) - .await - .unwrap(); - - // Process the coordinator's `Offer`. - let sub_channel = wait_until_sub_channel_state( - Duration::from_secs(30), - &app, - coordinator.info.pubkey, - SubChannelStateName::Offered, - ) - .await - .unwrap(); - - app.accept_sub_channel_offer(&sub_channel.channel_id) - .unwrap(); - - // Give time to deliver the `Accept` message to the coordinator. - wait_until(Duration::from_secs(5), || async { - Ok(coordinator - .dlc_message_handler - .has_pending_messages_to_process() - .then_some(())) - }) - .await - .unwrap(); - - // Lose the connection, triggering the coordinator's rollback to the `Offered` state. - app.reconnect(coordinator.info).await.unwrap(); - - // Process the app's first `Accept` message. This should fail because of an invalid commitment - // transaction number. - coordinator - .process_incoming_messages() - .expect_err("should have invalid commitment transaction number"); - - // Create second `Accept` message from pending `ReAccept` action. - sub_channel_manager_periodic_check( - app.sub_channel_manager.clone(), - &app.dlc_message_handler, - &app.peer_manager, - ) - .await - .unwrap(); - - // Process the app's second `Accept` and send `Confirm`. - wait_until_sub_channel_state( - Duration::from_secs(30), - &coordinator, - app.info.pubkey, - SubChannelStateName::Confirmed, - ) - .await - .unwrap(); - - // Process the coordinator's `Confirm` and send `Finalize`. - wait_until_sub_channel_state( - Duration::from_secs(30), - &app, - coordinator.info.pubkey, - SubChannelStateName::Finalized, - ) - .await - .unwrap(); - - // Process the app's `Finalize` and send `Revoke`. - wait_until_sub_channel_state( - Duration::from_secs(30), - &coordinator, - app.info.pubkey, - SubChannelStateName::Signed, - ) - .await - .unwrap(); - - // Process the coordinator's `Revoke`. - wait_until_sub_channel_state( - Duration::from_secs(30), - &app, - coordinator.info.pubkey, - SubChannelStateName::Signed, - ) - .await - .unwrap(); -} - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn can_lose_connection_before_processing_subchannel_close_accept() { - init_tracing(); - // Arrange - - let app_dlc_collateral = 25_000; - let coordinator_dlc_collateral = 50_000; - - let app_ln_balance = app_dlc_collateral * 2; - let coordinator_ln_balance = coordinator_dlc_collateral * 2; - - let fund_amount = (app_ln_balance + coordinator_ln_balance) * 2; - - let (app, _running_app) = Node::start_test_app("app").unwrap(); - let (coordinator, _running_coord) = Node::start_test_coordinator("coordinator").unwrap(); - - app.connect(coordinator.info).await.unwrap(); - - coordinator - .fund(Amount::from_sat(fund_amount)) - .await - .unwrap(); - - coordinator - .open_private_channel(&app, coordinator_ln_balance, app_ln_balance) - .await - .unwrap(); - - create_dlc_channel( - &coordinator, - &app, - coordinator_dlc_collateral, - app_dlc_collateral, - ) - .await - .unwrap(); - - let channel_details = coordinator - .channel_manager - .list_usable_channels() - .iter() - .find(|c| c.counterparty.node_id == app.info.pubkey) - .unwrap() - .clone(); - - coordinator - .propose_sub_channel_collaborative_settlement( - channel_details.channel_id, - app_dlc_collateral / 2, - ) - .await - .unwrap(); - - // Process `CloseOffer`. - let sub_channel = wait_until_sub_channel_state( - Duration::from_secs(30), - &app, - coordinator.info.pubkey, - SubChannelStateName::CloseOffered, - ) - .await - .unwrap(); - - app.accept_sub_channel_collaborative_settlement(&sub_channel.channel_id) - .unwrap(); - - // Give time to deliver the `CloseAccept` message to the coordinator. - wait_until(Duration::from_secs(5), || async { - Ok(coordinator - .dlc_message_handler - .has_pending_messages_to_process() - .then_some(())) - }) - .await - .unwrap(); - - // Lose the connection, triggering re-establishing the channel. - app.reconnect(coordinator.info).await.unwrap(); - - // Process the app's first `CloseAccept` message. This should fail because of an invalid - // commitment transaction number. - coordinator - .process_incoming_messages() - .expect_err("should have invalid commitment transaction number"); - - // Create second `CloseAccept` message from pending `ReCloseAccept` action. - sub_channel_manager_periodic_check( - app.sub_channel_manager.clone(), - &app.dlc_message_handler, - &app.peer_manager, - ) - .await - .unwrap(); - - // Process second `CloseAccept` and send `CloseConfirm`. - wait_until_sub_channel_state( - Duration::from_secs(30), - &coordinator, - app.info.pubkey, - SubChannelStateName::CloseConfirmed, - ) - .await - .unwrap(); - - // Process the coordinator's `CloseConfirm` and send `CloseFinalize` - wait_until_sub_channel_state( - Duration::from_secs(30), - &app, - coordinator.info.pubkey, - SubChannelStateName::OffChainClosed, - ) - .await - .unwrap(); - - // Process the coordinator's `CloseFinalize` - wait_until_sub_channel_state( - Duration::from_secs(30), - &coordinator, - app.info.pubkey, - SubChannelStateName::OffChainClosed, - ) - .await - .unwrap(); -} diff --git a/crates/ln-dlc-node/src/tests/dlc/mod.rs b/crates/ln-dlc-node/src/tests/dlc/mod.rs deleted file mode 100644 index 6a39e13c4..000000000 --- a/crates/ln-dlc-node/src/tests/dlc/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod collaborative_settlement; -mod create; -mod dlc_setup_with_reconnects; -mod non_collaborative_settlement; diff --git a/crates/ln-dlc-node/src/tests/dlc/non_collaborative_settlement.rs b/crates/ln-dlc-node/src/tests/dlc/non_collaborative_settlement.rs deleted file mode 100644 index 4491194d7..000000000 --- a/crates/ln-dlc-node/src/tests/dlc/non_collaborative_settlement.rs +++ /dev/null @@ -1,124 +0,0 @@ -use crate::node::sub_channel::sub_channel_manager_periodic_check; -use crate::node::Node; -use crate::tests::bitcoind::mine; -use crate::tests::dlc::create::create_dlc_channel; -use crate::tests::init_tracing; -use bitcoin::Amount; - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn force_close_ln_dlc_channel() { - init_tracing(); - - // Arrange - - let app_dlc_collateral = 50_000; - let coordinator_dlc_collateral = 25_000; - - let app_ln_balance = app_dlc_collateral * 2; - let coordinator_ln_balance = coordinator_dlc_collateral * 2; - - let fund_amount = (app_ln_balance + coordinator_ln_balance) * 2; - - let (app, _running_app) = Node::start_test_app("app").unwrap(); - let (coordinator, _running_coord) = Node::start_test_coordinator("coordinator").unwrap(); - - app.connect(coordinator.info).await.unwrap(); - - coordinator - .fund(Amount::from_sat(fund_amount)) - .await - .unwrap(); - - let channel_details = coordinator - .open_private_channel(&app, coordinator_ln_balance, app_ln_balance) - .await - .unwrap(); - - create_dlc_channel( - &app, - &coordinator, - app_dlc_collateral, - coordinator_dlc_collateral, - ) - .await - .unwrap(); - - coordinator.sync_wallets().await.unwrap(); - app.sync_wallets().await.unwrap(); - - // Act - - coordinator.force_close_channel(&channel_details).unwrap(); - - // Need 288 confirmations on the split transaction to be able to publish the glue and buffer - // transactions - mine(288).await.unwrap(); - - coordinator.sync_wallets().await.unwrap(); - app.sync_wallets().await.unwrap(); - - // Ensure publication of the glue and buffer transactions (otherwise we need to wait for the - // periodic task) - sub_channel_manager_periodic_check( - coordinator.sub_channel_manager.clone(), - &coordinator.dlc_message_handler, - &coordinator.peer_manager, - ) - .await - .unwrap(); - - // Assert - - coordinator.sync_wallets().await.unwrap(); - app.sync_wallets().await.unwrap(); - - // Mining 288 blocks ensures that we get: - // - 144 required confirmations for the delayed output on the LN commitment transaction to be - // spendable. - // - 288 required confirmations for the CET to be published. - mine(288).await.unwrap(); - - coordinator.sync_wallets().await.unwrap(); - app.sync_wallets().await.unwrap(); - - // Ensure publication of CET (otherwise we need to wait for the periodic task) - sub_channel_manager_periodic_check( - coordinator.sub_channel_manager.clone(), - &coordinator.dlc_message_handler, - &coordinator.peer_manager, - ) - .await - .unwrap(); - - // Confirm CET - mine(1).await.unwrap(); - tracing::info!("Mined 1 block"); - - coordinator.sync_wallets().await.unwrap(); - tracing::info!("Coordinator synced on-chain"); - app.sync_wallets().await.unwrap(); - tracing::info!("App synced on-chain"); - - let coordinator_on_chain_balance_after_force_close = - coordinator.get_on_chain_balance().unwrap().confirmed; - tracing::info!(balance = %coordinator_on_chain_balance_after_force_close, "Coordinator on-chain balance"); - let app_on_chain_balance_after_force_close = app.get_on_chain_balance().unwrap().confirmed; - tracing::info!(balance = %app_on_chain_balance_after_force_close, "App on-chain balance"); - - // Given that we have dynamic transaction fees based on the state of the regtest mempool, it's - // less error-prone to choose a conservative lower bound on the expected funds after - // force-closing the LN-DLC channel - let coordinator_on_chain_balance_after_force_close_expected_min = 245_000; - let app_on_chain_balance_after_force_close_expected_min = 45_000; - - assert!( - coordinator_on_chain_balance_after_force_close - >= coordinator_on_chain_balance_after_force_close_expected_min - ); - - assert!( - app_on_chain_balance_after_force_close - >= app_on_chain_balance_after_force_close_expected_min - ); -} diff --git a/crates/ln-dlc-node/src/tests/just_in_time_channel/channel_close.rs b/crates/ln-dlc-node/src/tests/just_in_time_channel/channel_close.rs deleted file mode 100644 index 96a4f2cfb..000000000 --- a/crates/ln-dlc-node/src/tests/just_in_time_channel/channel_close.rs +++ /dev/null @@ -1,183 +0,0 @@ -use crate::node::Node; -use crate::tests::bitcoind; -use crate::tests::init_tracing; -use crate::tests::just_in_time_channel::create::send_interceptable_payment; -use crate::tests::setup_coordinator_payer_channel; -use std::time::Duration; - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn ln_collab_close() { - init_tracing(); - - // Arrange - - let (payer, _running_payer) = Node::start_test_app("payer").unwrap(); - let (coordinator, _running_coord) = Node::start_test_coordinator("coordinator").unwrap(); - let (payee, _running_payee) = Node::start_test_app("payee").unwrap(); - - payer.connect(coordinator.info).await.unwrap(); - payee.connect(coordinator.info).await.unwrap(); - - let payer_to_payee_invoice_amount = 60_000; - let (expected_coordinator_payee_channel_value_sat, liquidity_request) = - setup_coordinator_payer_channel( - &coordinator, - &payer, - payee.info.pubkey, - payer_to_payee_invoice_amount, - ) - .await; - - send_interceptable_payment( - &payer, - &payee, - &coordinator, - payer_to_payee_invoice_amount, - liquidity_request.clone(), - expected_coordinator_payee_channel_value_sat, - ) - .await - .unwrap(); - - assert_eq!(payee.get_on_chain_balance().unwrap().confirmed, 0); - assert_eq!(payee.get_ldk_balance().pending_close(), 0); - - // Act - - let channel_id = payee - .channel_manager - .list_usable_channels() - .first() - .unwrap() - .channel_id; - - payee - .channel_manager - .close_channel(&channel_id, &coordinator.info.pubkey) - .unwrap(); - - while !payee.list_channels().is_empty() { - tokio::time::sleep(Duration::from_millis(100)).await; - } - - // Give some time for the close transaction to be broadcast before trying to include it in a - // block - tokio::time::sleep(Duration::from_secs(5)).await; - - assert_eq!(payee.get_on_chain_balance().unwrap().confirmed, 0); - - // Mine one block to confirm the close transaction - bitcoind::mine(1).await.unwrap(); - payee.sync_wallets().await.unwrap(); - - // Assert - - let ln_balance = payee.get_ldk_balance(); - assert_eq!(ln_balance.available(), 0); - assert_eq!(ln_balance.pending_close(), 0); - - assert_eq!( - payee.get_on_chain_balance().unwrap().confirmed, - payer_to_payee_invoice_amount - liquidity_request.fee_sats - ); -} - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn ln_force_close() { - init_tracing(); - - // Arrange - - let (payer, _running_payer) = Node::start_test_app("payer").unwrap(); - let (coordinator, _running_coord) = Node::start_test_coordinator("coordinator").unwrap(); - let (payee, _running_payee) = Node::start_test_app("payee").unwrap(); - - payer.connect(coordinator.info).await.unwrap(); - payee.connect(coordinator.info).await.unwrap(); - - let payer_to_payee_invoice_amount_sat = 70_000; - let (expected_coordinator_payee_channel_value, liquidity_request) = - setup_coordinator_payer_channel( - &coordinator, - &payer, - payee.info.pubkey, - payer_to_payee_invoice_amount_sat, - ) - .await; - - send_interceptable_payment( - &payer, - &payee, - &coordinator, - payer_to_payee_invoice_amount_sat, - liquidity_request.clone(), - expected_coordinator_payee_channel_value, - ) - .await - .unwrap(); - - assert_eq!(payee.get_on_chain_balance().unwrap().confirmed, 0); - assert_eq!(payee.get_ldk_balance().pending_close(), 0); - - // Act - - let channel_id = payee - .channel_manager - .list_usable_channels() - .first() - .unwrap() - .channel_id; - payee - .channel_manager - .force_close_broadcasting_latest_txn(&channel_id, &coordinator.info.pubkey) - .unwrap(); - - payee.sync_wallets().await.unwrap(); - - assert_eq!(payee.get_on_chain_balance().unwrap().confirmed, 0); - assert_eq!(payee.get_ldk_balance().available(), 0); - assert_eq!( - payee.get_ldk_balance().pending_close(), - payer_to_payee_invoice_amount_sat - liquidity_request.fee_sats - ); - - // Mine enough blocks so that the payee's revocable output in the commitment transaction - // is spendable - let our_to_self_delay = coordinator - .ldk_config - .read() - .channel_handshake_config - .our_to_self_delay; - bitcoind::mine(our_to_self_delay).await.unwrap(); - - // Syncing the payee's wallet should now trigger a `SpendableOutputs` event - // corresponding to their revocable output in the commitment transaction, which they - // will subsequently spend in a new transaction paying to their on-chain wallet - payee.sync_wallets().await.unwrap(); - - // Mine one more block to confirm the transaction spending the payee's revocable output - // in the commitment transaction - bitcoind::mine(1).await.unwrap(); - payee.sync_wallets().await.unwrap(); - - // Assert - - let ln_balance = payee.get_ldk_balance(); - assert_eq!(ln_balance.available(), 0); - assert_eq!(ln_balance.pending_close(), 0); - - let payee_txs = payee.get_on_chain_history().unwrap(); - - let claim_tx = match payee_txs.as_slice() { - [tx] => tx, - _ => panic!( - "Unexpected number of payee transactions. Expected 1, got {}", - payee_txs.len() - ), - }; - - assert_eq!(claim_tx.sent, 0); - assert!(claim_tx.received > 0); -} diff --git a/crates/ln-dlc-node/src/tests/just_in_time_channel/create.rs b/crates/ln-dlc-node/src/tests/just_in_time_channel/create.rs deleted file mode 100644 index de74e2b0f..000000000 --- a/crates/ln-dlc-node/src/tests/just_in_time_channel/create.rs +++ /dev/null @@ -1,455 +0,0 @@ -use crate::channel::Channel; -use crate::channel::ChannelState; -use crate::channel::UserChannelId; -use crate::fee_rate_estimator::EstimateFeeRate; -use crate::node::InMemoryStore; -use crate::node::LiquidityRequest; -use crate::node::LnDlcNodeSettings; -use crate::node::Node; -use crate::node::Storage; -use crate::storage::TenTenOneInMemoryStorage; -use crate::tests::calculate_routing_fee_msat; -use crate::tests::init_tracing; -use crate::tests::ln_dlc_node_settings_coordinator; -use crate::tests::setup_coordinator_payer_channel; -use crate::HTLCStatus; -use crate::WalletSettings; -use anyhow::Context; -use anyhow::Result; -use lightning::chain::chaininterface::ConfirmationTarget; -use lightning::ln::ChannelId; -use rust_decimal::Decimal; -use std::sync::Arc; -use std::time::Duration; -use uuid::Uuid; - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn open_jit_channel() { - init_tracing(); - - // Arrange - - let (payer, _running_payer) = Node::start_test_app("payer").unwrap(); - - let coordinator_storage = Arc::new(InMemoryStore::default()); - let settings = LnDlcNodeSettings { - on_chain_sync_interval: Duration::from_secs(3), - shadow_sync_interval: Duration::from_secs(3), - ..ln_dlc_node_settings_coordinator() - }; - let (coordinator, _running_coordinator) = { - // setting the on chain sync interval to 5 seconds so that we don't have to wait for so long - // before the costs for the funding transaction will be attached to the shadow channel. - - Node::start_test_coordinator_internal( - "coordinator", - coordinator_storage.clone(), - settings.clone(), - None, - ) - .unwrap() - }; - let (payee, _running_payee) = Node::start_test_app("payee").unwrap(); - - payer.connect(coordinator.info).await.unwrap(); - payee.connect(coordinator.info).await.unwrap(); - - // Testing with a large invoice amount. - let payer_to_payee_invoice_amount_sat = 3_000_000; - let (expected_coordinator_payee_channel_value, liquidity_request) = - setup_coordinator_payer_channel( - &coordinator, - &payer, - payee.info.pubkey, - payer_to_payee_invoice_amount_sat, - ) - .await; - - // Act and assert - send_interceptable_payment( - &payer, - &payee, - &coordinator, - payer_to_payee_invoice_amount_sat, - liquidity_request, - expected_coordinator_payee_channel_value, - ) - .await - .unwrap(); - - let channel_details = coordinator - .channel_manager - .list_usable_channels() - .iter() - .find(|c| c.counterparty.node_id == payee.info.pubkey) - .context("Could not find usable channel with peer") - .unwrap() - .clone(); - - let user_channel_id = Uuid::from_u128(channel_details.user_channel_id).to_string(); - - // Wait for costs getting attached to the shadow channel. We are waiting for 6 seconds to ensure - // that the shadow sync will run at least once. - tokio::time::sleep(Duration::from_secs(6)).await; - - let channel = coordinator_storage - .get_channel(&user_channel_id) - .unwrap() - .unwrap(); - assert_eq!(ChannelState::OpenUnpaid, channel.channel_state); - - let transaction = coordinator_storage - .get_transaction(&channel.funding_txid.unwrap().to_string()) - .unwrap() - .unwrap(); - assert!(transaction.fee() > 0); -} - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn fail_to_open_jit_channel_with_fee_rate_over_max() { - init_tracing(); - - // Arrange - - let (payer, _running_payer) = Node::start_test_app("payer").unwrap(); - let (coordinator, _running_coord) = Node::start_test_coordinator("coordinator").unwrap(); - let (payee, _running_payee) = Node::start_test_app("payee").unwrap(); - - payer.connect(coordinator.info).await.unwrap(); - payee.connect(coordinator.info).await.unwrap(); - - let payer_to_payee_invoice_amount = 5_000; - let _ = setup_coordinator_payer_channel( - &coordinator, - &payer, - payee.info.pubkey, - payer_to_payee_invoice_amount, - ) - .await; - - // Act - - let background_fee_rate = coordinator - .fee_rate_estimator - .estimate(ConfirmationTarget::Background) - .fee_wu(1000) as u32; - - // Set max allowed TX fee rate when opening channel to a value below the current background fee - // rate to ensure that opening the JIT channel fails - let settings = WalletSettings { - max_allowed_tx_fee_rate_when_opening_channel: Some(background_fee_rate - 1), - jit_channels_enabled: true, - }; - - coordinator.ldk_wallet().update_settings(settings).await; - - let liquidity_request = LiquidityRequest { - user_channel_id: UserChannelId::new(), - liquidity_option_id: 1, - trader_id: payee.info.pubkey, - trade_up_to_sats: 200_000, - max_deposit_sats: 200_000, - coordinator_leverage: 1.0, - fee_sats: 10_000, - }; - let final_route_hint_hop = coordinator - .prepare_onboarding_payment(liquidity_request) - .unwrap(); - let invoice = payee - .create_invoice_with_route_hint( - Some(payer_to_payee_invoice_amount), - None, - "interceptable-invoice".to_string(), - final_route_hint_hop, - ) - .unwrap(); - - payer.pay_invoice(&invoice, None).unwrap(); - - // Assert - - // We would like to assert on the payment failing, but this is not guaranteed as the payment can - // still be retried after the first payment path failure. Thus, we check that it doesn't succeed - payee - .wait_for_payment(HTLCStatus::Succeeded, invoice.payment_hash(), None) - .await - .expect_err("payment should not succeed"); -} - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn open_jit_channel_with_disconnected_payee() { - init_tracing(); - - // Arrange - - let (payer, _running_payer) = Node::start_test_app("payer").unwrap(); - let (coordinator, _running_coord) = Node::start_test_coordinator("coordinator").unwrap(); - let (payee, _running_payee) = Node::start_test_app("payee").unwrap(); - - // We purposefully do NOT connect to the payee, so that we can test the ability to open a JIT - // channel to a disconnected payee - payer.connect(coordinator.info).await.unwrap(); - - let payer_to_payee_invoice_amount = 5_000; - let _ = setup_coordinator_payer_channel( - &coordinator, - &payer, - payee.info.pubkey, - payer_to_payee_invoice_amount, - ) - .await; - - // Act - - let liquidity_request = LiquidityRequest { - user_channel_id: UserChannelId::new(), - liquidity_option_id: 1, - trader_id: payee.info.pubkey, - trade_up_to_sats: payer_to_payee_invoice_amount, - max_deposit_sats: payer_to_payee_invoice_amount, - coordinator_leverage: 1.0, - fee_sats: 0, - }; - let final_route_hint_hop = coordinator - .prepare_onboarding_payment(liquidity_request) - .unwrap(); - - let invoice = payee - .create_invoice_with_route_hint( - Some(payer_to_payee_invoice_amount), - None, - "interceptable-invoice".to_string(), - final_route_hint_hop, - ) - .unwrap(); - - payer.pay_invoice(&invoice, None).unwrap(); - - // We wait a little bit until reconnecting to simulate a pending JIT channel on the coordinator - tokio::time::sleep(Duration::from_secs(5)).await; - payee.connect(coordinator.info).await.unwrap(); - - // Assert - - payee - .wait_for_payment_claimed(invoice.payment_hash()) - .await - .unwrap(); -} - -pub(crate) async fn send_interceptable_payment( - payer: &Node, - payee: &Node, - coordinator: &Node, - invoice_amount_sat: u64, - liquidity_request: LiquidityRequest, - expected_coordinator_payee_channel_value_sat: u64, -) -> Result<()> { - let user_channel_id = liquidity_request.user_channel_id; - let jit_channel_fee = liquidity_request.fee_sats; - - payer.sync_wallets().await?; - coordinator.sync_wallets().await?; - payee.sync_wallets().await?; - - let payer_balance_before = payer.get_ldk_balance(); - let coordinator_balance_before = coordinator.get_ldk_balance(); - let payee_balance_before = payee.get_ldk_balance(); - - let interceptable_route_hint_hop = coordinator.prepare_onboarding_payment(liquidity_request)?; - - // Announce the jit channel on the app side. This is done by the app when preparing the - // onboarding invoice. But since we do not have the app available here, we need to do this - // manually. - let channel = - Channel::new_jit_channel(user_channel_id, coordinator.info.pubkey, 1, jit_channel_fee); - payee - .node_storage - .upsert_channel(channel) - .with_context(|| { - format!("Failed to insert shadow JIT channel with user channel id {user_channel_id}") - })?; - - let invoice = payee.create_invoice_with_route_hint( - Some(invoice_amount_sat), - None, - "interceptable-invoice".to_string(), - interceptable_route_hint_hop, - )?; - let invoice_amount_msat = invoice.amount_milli_satoshis().unwrap(); - - let routing_fee_msat = calculate_routing_fee_msat( - coordinator.ldk_config.read().channel_config, - invoice_amount_sat, - ); - - assert!( - does_inbound_htlc_fit_as_percent_of_channel( - coordinator, - &payer - .channel_manager - .list_channels() - .first() - .expect("payer channel should be created.") - .channel_id, - invoice_amount_sat + (routing_fee_msat / 1000) - ) - .unwrap(), - "Invoice amount larger than maximum inbound HTLC in payer-coordinator channel" - ); - - payer.pay_invoice(&invoice, None).unwrap(); - - payee - .wait_for_payment_claimed(invoice.payment_hash()) - .await - .unwrap(); - - // Assert - - // Sync LN wallet after payment is claimed to update the balances - payer.sync_wallets().await?; - coordinator.sync_wallets().await?; - payee.sync_wallets().await?; - - let payer_balance_after = payer.get_ldk_balance(); - let coordinator_balance_after = coordinator.get_ldk_balance(); - let payee_balance_after = payee.get_ldk_balance(); - - assert_eq!( - payer_balance_before.available_msat() - payer_balance_after.available_msat(), - invoice_amount_msat + routing_fee_msat - ); - - assert_eq!( - coordinator_balance_after.available_msat() - coordinator_balance_before.available_msat(), - expected_coordinator_payee_channel_value_sat * 1000 - + routing_fee_msat - + jit_channel_fee * 1000 - ); - - assert_eq!( - payee_balance_after.available() - payee_balance_before.available(), - invoice_amount_sat - jit_channel_fee - ); - - Ok(()) -} - -/// Sends a regular payment assuming all channels on the path exist. -pub(crate) async fn send_payment( - payer: &Node, - payee: &Node, - coordinator: &Node, - invoice_amount_sat: u64, - coordinator_just_in_time_channel_creation_outbound_liquidity: Option, -) -> Result<()> { - payer.sync_wallets().await?; - coordinator.sync_wallets().await?; - payee.sync_wallets().await?; - - let payer_balance_before = payer.get_ldk_balance(); - let coordinator_balance_before = coordinator.get_ldk_balance(); - let payee_balance_before = payee.get_ldk_balance(); - - let route_hint_hop = payee.prepare_payment_with_route_hint(coordinator.info.pubkey)?; - - let invoice = payee.create_invoice_with_route_hint( - Some(invoice_amount_sat), - None, - "regular invoice".to_string(), - route_hint_hop, - )?; - let invoice_amount_msat = invoice.amount_milli_satoshis().unwrap(); - - let routing_fee_msat = calculate_routing_fee_msat( - coordinator.ldk_config.read().channel_config, - invoice_amount_sat, - ); - - assert!( - does_inbound_htlc_fit_as_percent_of_channel( - coordinator, - &payer - .channel_manager - .list_channels() - .first() - .expect("payer channel should be created.") - .channel_id, - invoice_amount_sat + (routing_fee_msat / 1000) - ) - .unwrap(), - "Invoice amount larger than maximum inbound HTLC in payer-coordinator channel" - ); - - payer.pay_invoice(&invoice, None).unwrap(); - - payee - .wait_for_payment_claimed(invoice.payment_hash()) - .await - .unwrap(); - - // Assert - - // Sync LN wallet after payment is claimed to update the balances - payer.sync_wallets().await?; - coordinator.sync_wallets().await?; - payee.sync_wallets().await?; - - let payer_balance_after = payer.get_ldk_balance(); - let coordinator_balance_after = coordinator.get_ldk_balance(); - let payee_balance_after = payee.get_ldk_balance(); - - assert_eq!( - payer_balance_before.available_msat() - payer_balance_after.available_msat(), - invoice_amount_msat + routing_fee_msat - ); - - assert_eq!( - coordinator_balance_after.available_msat() - coordinator_balance_before.available_msat(), - coordinator_just_in_time_channel_creation_outbound_liquidity.unwrap_or_default() * 1000 - + routing_fee_msat - ); - - assert_eq!( - payee_balance_after.available() - payee_balance_before.available(), - invoice_amount_sat - ); - - Ok(()) -} - -/// Used to ascertain if a payment will be routed through a channel according to the -/// `max_inbound_htlc_value_in_flight_percent_of_channel` configuration flag of the receiving end of -/// the channel. -fn does_inbound_htlc_fit_as_percent_of_channel( - receiving_node: &Node, - channel_id: &ChannelId, - htlc_amount_sat: u64, -) -> Result { - let htlc_amount_sat = Decimal::from(htlc_amount_sat); - - let max_inbound_htlc_as_percent_of_channel = Decimal::from( - receiving_node - .ldk_config - .read() - .channel_handshake_config - .max_inbound_htlc_value_in_flight_percent_of_channel, - ); - - let channel_size_sat = receiving_node - .channel_manager - .list_channels() - .iter() - .find_map(|c| (&c.channel_id == channel_id).then_some(c.channel_value_satoshis)) - .context("No matching channel")?; - let channel_size_sat = Decimal::from(channel_size_sat); - - let max_inbound_htlc_sat = - channel_size_sat * (max_inbound_htlc_as_percent_of_channel / Decimal::ONE_HUNDRED); - - Ok(htlc_amount_sat <= max_inbound_htlc_sat) -} diff --git a/crates/ln-dlc-node/src/tests/just_in_time_channel/fail_intercepted_htlc.rs b/crates/ln-dlc-node/src/tests/just_in_time_channel/fail_intercepted_htlc.rs deleted file mode 100644 index a997df0f8..000000000 --- a/crates/ln-dlc-node/src/tests/just_in_time_channel/fail_intercepted_htlc.rs +++ /dev/null @@ -1,201 +0,0 @@ -use crate::channel::UserChannelId; -use crate::config::HTLC_INTERCEPTED_CONNECTION_TIMEOUT; -use crate::node::InMemoryStore; -use crate::node::LiquidityRequest; -use crate::node::Node; -use crate::tests::init_tracing; -use crate::tests::ln_dlc_node_settings_coordinator; -use crate::tests::setup_coordinator_payer_channel; -use crate::HTLCStatus; -use bitcoin::Amount; -use lightning::events::Event; -use std::ops::Add; -use std::sync::Arc; -use std::time::Duration; - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn fail_intercepted_htlc_if_coordinator_cannot_reconnect_to_payee() { - init_tracing(); - - // Arrange - - let (payer, _running_payer) = Node::start_test_app("payer").unwrap(); - let (coordinator, _running_coord) = Node::start_test_coordinator("coordinator").unwrap(); - let (payee, _running_payee) = Node::start_test_app("payee").unwrap(); - - payer.connect(coordinator.info).await.unwrap(); - payee.connect(coordinator.info).await.unwrap(); - - let invoice_amount = 10_000; - let (_, liquidity_request) = - setup_coordinator_payer_channel(&coordinator, &payer, payee.info.pubkey, invoice_amount) - .await; - - let interceptable_route_hint_hop = coordinator - .prepare_onboarding_payment(liquidity_request) - .unwrap(); - - let invoice = payee - .create_invoice_with_route_hint( - Some(invoice_amount), - None, - "interceptable-invoice".to_string(), - interceptable_route_hint_hop, - ) - .unwrap(); - - // Act - - // We wait a second for payee and coordinator to be disconnected - payee.disconnect(coordinator.info); - tokio::time::sleep(Duration::from_secs(1)).await; - - payer.pay_invoice(&invoice, None).unwrap(); - - // Assert - - payer - .wait_for_payment( - HTLCStatus::Failed, - invoice.payment_hash(), - // We wait a bit longer than what the coordinator should wait for the payee to - // reconnect - Some(HTLC_INTERCEPTED_CONNECTION_TIMEOUT.add(Duration::from_secs(5))), - ) - .await - .unwrap(); -} - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn fail_intercepted_htlc_if_connection_lost_after_funding_tx_generated() { - init_tracing(); - - // Arrange - - let (payer, _running_payer) = Node::start_test_app("payer").unwrap(); - - let (coordinator, _running_coord, mut ldk_node_event_receiver_coordinator) = { - let (sender, receiver) = tokio::sync::watch::channel(None); - let (coordinator, _running_coord) = Node::start_test_coordinator_internal( - "coordinator", - Arc::new(InMemoryStore::default()), - ln_dlc_node_settings_coordinator(), - Some(sender), - ) - .unwrap(); - - (coordinator, _running_coord, receiver) - }; - - let (payee, _running_payee) = Node::start_test_app("payee").unwrap(); - - payer.connect(coordinator.info).await.unwrap(); - payee.connect(coordinator.info).await.unwrap(); - - let invoice_amount = 10_000; - let (_, liquidity_request) = - setup_coordinator_payer_channel(&coordinator, &payer, payee.info.pubkey, invoice_amount) - .await; - - let interceptable_route_hint_hop = coordinator - .prepare_onboarding_payment(liquidity_request) - .unwrap(); - - let invoice = payee - .create_invoice_with_route_hint( - Some(invoice_amount), - None, - "interceptable-invoice".to_string(), - interceptable_route_hint_hop, - ) - .unwrap(); - - // Act - - payer.pay_invoice(&invoice, None).unwrap(); - - tokio::time::timeout(Duration::from_secs(30), async { - loop { - ldk_node_event_receiver_coordinator.changed().await.unwrap(); - let event = ldk_node_event_receiver_coordinator.borrow().clone(); - - if let Some(Event::FundingGenerationReady { .. }) = event { - // We wait a second for payee and coordinator to be disconnected - payee.disconnect(coordinator.info); - tokio::time::sleep(Duration::from_secs(1)).await; - - break; - } - } - }) - .await - .unwrap(); - - // Assert - - payer - .wait_for_payment(HTLCStatus::Failed, invoice.payment_hash(), None) - .await - .unwrap(); -} - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn fail_intercepted_htlc_if_coordinator_cannot_pay_to_open_jit_channel() { - init_tracing(); - - // Arrange - - let (payer, _running_payer) = Node::start_test_app("payer").unwrap(); - let (coordinator, _running_coord) = Node::start_test_coordinator("coordinator").unwrap(); - let (payee, _running_payee) = Node::start_test_app("payee").unwrap(); - - payer.connect(coordinator.info).await.unwrap(); - payee.connect(coordinator.info).await.unwrap(); - - let payer_outbound_liquidity = 200_000; - - payer.fund(Amount::ONE_BTC).await.unwrap(); - payer - .open_public_channel(&coordinator, payer_outbound_liquidity, 0) - .await - .unwrap(); - - // Act - - // The coordinator should not be able to open any JIT channel because we have not funded their - // on-chain wallet - let invoice_amount = 10_000; - - let liquidity_request = LiquidityRequest { - user_channel_id: UserChannelId::new(), - liquidity_option_id: 1, - trader_id: payee.info.pubkey, - trade_up_to_sats: 100_000, - max_deposit_sats: 100_000, - coordinator_leverage: 1.0, - fee_sats: 10_000, - }; - let interceptable_route_hint_hop = coordinator - .prepare_onboarding_payment(liquidity_request) - .unwrap(); - let invoice = payee - .create_invoice_with_route_hint( - Some(invoice_amount), - None, - "interceptable-invoice".to_string(), - interceptable_route_hint_hop, - ) - .unwrap(); - - payer.pay_invoice(&invoice, None).unwrap(); - - // Assert - - payer - .wait_for_payment(HTLCStatus::Failed, invoice.payment_hash(), None) - .await - .unwrap(); -} diff --git a/crates/ln-dlc-node/src/tests/just_in_time_channel/mod.rs b/crates/ln-dlc-node/src/tests/just_in_time_channel/mod.rs deleted file mode 100644 index f132eba11..000000000 --- a/crates/ln-dlc-node/src/tests/just_in_time_channel/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod channel_close; -mod create; -mod fail_intercepted_htlc; -mod payments; diff --git a/crates/ln-dlc-node/src/tests/just_in_time_channel/payments.rs b/crates/ln-dlc-node/src/tests/just_in_time_channel/payments.rs deleted file mode 100644 index b96e20c74..000000000 --- a/crates/ln-dlc-node/src/tests/just_in_time_channel/payments.rs +++ /dev/null @@ -1,159 +0,0 @@ -use crate::node::Node; -use crate::tests::calculate_routing_fee_msat; -use crate::tests::init_tracing; -use crate::tests::just_in_time_channel::create::send_interceptable_payment; -use crate::tests::just_in_time_channel::create::send_payment; -use crate::tests::setup_coordinator_payer_channel; -use crate::tests::wait_for_n_usable_channels; -use crate::tests::wait_until; -use std::time::Duration; - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn just_in_time_channel_with_multiple_payments() { - init_tracing(); - - // Arrange - - let (payer, _running_payer) = Node::start_test_app("payer").unwrap(); - let (coordinator, _running_coord) = Node::start_test_coordinator("coordinator").unwrap(); - let (payee, _running_payee) = Node::start_test_app("payee").unwrap(); - - payer.connect(coordinator.info).await.unwrap(); - payee.connect(coordinator.info).await.unwrap(); - - let payer_to_payee_invoice_amount = 25_000; - let (expected_coordinator_payee_channel_value, liquidity_request) = - setup_coordinator_payer_channel( - &coordinator, - &payer, - payee.info.pubkey, - payer_to_payee_invoice_amount, - ) - .await; - - // this creates the just in time channel between the coordinator and payee - send_interceptable_payment( - &payer, - &payee, - &coordinator, - payer_to_payee_invoice_amount, - liquidity_request, - expected_coordinator_payee_channel_value, - ) - .await - .unwrap(); - - // after creating the just-in-time channel. The coordinator should have exactly 2 usable - // channels with short channel ids. - wait_for_n_usable_channels(2, &coordinator).await.unwrap(); - - // 3 consecutive payments, we divide by 5 to account for fees - // Note: Dividing by 4 should work but leads to rounding error because of how the fees are - // calculated in the test assertions - let consecutive_payment_invoice_amount = payer_to_payee_invoice_amount / 5; - - for _ in 0..3 { - send_payment( - &payer, - &payee, - &coordinator, - consecutive_payment_invoice_amount, - None, - ) - .await - .unwrap(); - - // no additional just-in-time channel should be created. - assert_eq!(coordinator.channel_manager.list_channels().len(), 2); - } -} - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn new_config_affects_routing_fees() { - init_tracing(); - - // Arrange - - let (payer, _running_payer) = Node::start_test_app("payer").unwrap(); - let (coordinator, _running_coord) = Node::start_test_coordinator("coordinator").unwrap(); - let (payee, _running_payee) = Node::start_test_app("payee").unwrap(); - - payer.connect(coordinator.info).await.unwrap(); - payee.connect(coordinator.info).await.unwrap(); - - let opening_invoice_amount = 60_000; - let (expected_coordinator_payee_channel_value, liquidity_request) = - setup_coordinator_payer_channel( - &coordinator, - &payer, - payee.info.pubkey, - opening_invoice_amount, - ) - .await; - - send_interceptable_payment( - &payer, - &payee, - &coordinator, - opening_invoice_amount, - liquidity_request, - expected_coordinator_payee_channel_value, - ) - .await - .unwrap(); - - // after creating the just-in-time channel. The coordinator should have exactly 2 usable - // channels with short channel ids. - wait_for_n_usable_channels(2, &coordinator).await.unwrap(); - - // Act - - let coordinator_balance_before = coordinator.get_ldk_balance().available_msat(); - - let mut ldk_config_coordinator = *coordinator.ldk_config.read(); - - ldk_config_coordinator - .channel_config - .forwarding_fee_proportional_millionths *= 10; - - let fee_rate = ldk_config_coordinator - .channel_config - .forwarding_fee_proportional_millionths; - - coordinator.update_ldk_settings(ldk_config_coordinator); - - wait_until(Duration::from_secs(30), || async { - let payer_channels = payer.channel_manager.list_channels(); - Ok(payer_channels - .iter() - .any(|channel| { - channel - .counterparty - .forwarding_info - .clone() - .expect("to have forwarding info") - .fee_proportional_millionths - == fee_rate - }) - .then_some(())) - }) - .await - .expect("all channels to have updated fee"); - - let payment_amount_sat = 5_000; - send_payment(&payer, &payee, &coordinator, payment_amount_sat, None) - .await - .unwrap(); - - // Assert - - let coordinator_balance_after = coordinator.get_ldk_balance().available_msat(); - let routing_fee_charged_msat = coordinator_balance_after - coordinator_balance_before; - - let routing_fee_expected_msat = - calculate_routing_fee_msat(ldk_config_coordinator.channel_config, payment_amount_sat); - - assert_eq!(routing_fee_charged_msat, routing_fee_expected_msat); -} diff --git a/crates/ln-dlc-node/src/tests/mod.rs b/crates/ln-dlc-node/src/tests/mod.rs index 79f37768b..58ae2c1bf 100644 --- a/crates/ln-dlc-node/src/tests/mod.rs +++ b/crates/ln-dlc-node/src/tests/mod.rs @@ -1,11 +1,8 @@ -use crate::channel::UserChannelId; use crate::config::app_config; use crate::config::coordinator_config; -use crate::ln::calculate_channel_value; use crate::node::peer_manager::alias_as_bytes; use crate::node::GossipSourceConfig; use crate::node::InMemoryStore; -use crate::node::LiquidityRequest; use crate::node::LnDlcNodeSettings; use crate::node::Node; use crate::node::NodeInfo; @@ -41,14 +38,11 @@ use dlc_manager::subchannel::SubChannelState; use dlc_manager::Storage; use futures::Future; use lightning::events::Event; -use lightning::ln::channelmanager::ChannelDetails; use lightning::util::config::UserConfig; use rand::distributions::Alphanumeric; use rand::thread_rng; use rand::Rng; use rand::RngCore; -use rust_decimal::prelude::ToPrimitive; -use rust_decimal::Decimal; use std::env::temp_dir; use std::net::TcpListener; use std::path::PathBuf; @@ -61,12 +55,7 @@ use tokio::sync::watch; use tokio::task::block_in_place; mod bitcoind; -mod dlc; mod dlc_channel; -mod just_in_time_channel; -mod multi_hop_payment; -mod probe; -mod single_hop_payment; #[cfg(feature = "load_tests")] mod load; @@ -98,6 +87,37 @@ fn init_tracing() { }) } +#[cfg(test)] +async fn wait_until_sub_channel_state( + timeout: Duration, + node: &Node, + counterparty_pk: PublicKey, + target_state: SubChannelStateName, +) -> Result { + wait_until(timeout, || async { + node.process_incoming_messages()?; + + let dlc_channels = node.dlc_manager.get_store().get_sub_channels()?; + + Ok(dlc_channels + .iter() + .find(|channel| { + let current_state = SubChannelStateName::from(&channel.state); + + tracing::info!( + node_id = %node.info.pubkey, + target = ?target_state, + current = ?current_state, + "Waiting for DLC subchannel to reach state" + ); + + channel.counter_party == counterparty_pk && current_state == target_state + }) + .cloned()) + }) + .await +} + impl Node { fn start_test_app(name: &str) -> Result<(Arc, RunningNode)> { let app_event_handler = |node, event_sender| { @@ -266,90 +286,6 @@ impl Node { Ok(balance.confirmed) } - /// Initiates the opening of a private channel _and_ waits for the channel to be usable. - async fn open_private_channel( - &self, - peer: &Node, - amount_us: u64, - amount_them: u64, - ) -> Result { - self.open_channel(peer, amount_us, amount_them, false).await - } - - /// Initiates the opening of a public channel _and_ waits for the channel to be usable. - async fn open_public_channel( - &self, - peer: &Node, - amount_us: u64, - amount_them: u64, - ) -> Result { - self.open_channel(peer, amount_us, amount_them, true).await - } - - /// Initiates the opening of a channel _and_ waits for the channel to be usable. - async fn open_channel( - &self, - peer: &Node, - amount_us: u64, - amount_them: u64, - is_public: bool, - ) -> Result { - let temp_channel_id = self.initiate_open_channel( - peer.info.pubkey, - amount_us + amount_them, - amount_them, - is_public, - )?; - - let (does_manually_accept_inbound_channels, required_confirmations) = - block_in_place(|| { - let config = peer.ldk_config.read(); - - ( - config.manually_accept_inbound_channels, - config.channel_handshake_config.minimum_depth, - ) - }); - - // The config flag `channel_config.manually_accept_inbound_channels` implies that the peer - // will accept 0-conf channels - if !does_manually_accept_inbound_channels { - bitcoind::mine(required_confirmations as u16).await?; - } - - let channel_details = tokio::time::timeout(Duration::from_secs(30), async { - loop { - if let Some(details) = self - .channel_manager - .list_usable_channels() - .iter() - .find(|c| c.counterparty.node_id == peer.info.pubkey) - { - break details.clone(); - } - - // Only sync if 0-conf channels are disabled - if !does_manually_accept_inbound_channels { - // We need to sync both parties, even if - // `trust_own_funding_0conf` is true for the creator - // of the channel (`self`) - self.sync_wallets().await.unwrap(); - peer.sync_wallets().await.unwrap(); - } - - tracing::debug!( - peer = %peer.info, - temp_channel_id = %hex::encode(temp_channel_id.0), - "Waiting for channel to be usable" - ); - tokio::time::sleep(Duration::from_millis(100)).await; - } - }) - .await?; - - Ok(channel_details) - } - pub fn disconnect(&self, peer: NodeInfo) { self.peer_manager.disconnect_by_node_id(peer.pubkey) } @@ -372,69 +308,6 @@ impl Node { } } -async fn setup_coordinator_payer_channel( - coordinator: &Node, - payer: &Node, - payee_pk: PublicKey, - // How much the _payee_ should have in the coordinator-payee channel. This value corresponds to - // the value in the JIT channel-opening invoice, minus the channel-opening fee. - coordinator_payee_inbound_liquity_sat: u64, -) -> (u64, LiquidityRequest) { - // The `LiquidityRequest` associated with the JIT channel being opened. - let liquidity_request = LiquidityRequest { - user_channel_id: UserChannelId::new(), - liquidity_option_id: 1, - trader_id: payee_pk, - // Trade up to the full payee balance in the JIT channel. - trade_up_to_sats: coordinator_payee_inbound_liquity_sat, - max_deposit_sats: coordinator_payee_inbound_liquity_sat * 2, - coordinator_leverage: 1.0, - fee_sats: 10_000, - }; - - let expected_coordinator_payee_channel_value = calculate_channel_value( - coordinator_payee_inbound_liquity_sat * 1_000, - &liquidity_request, - ); - - // How much the _payer_ should have in the payer-coordinator channel. Since this channel is only - // used to fund the payee, we just need to fund it with enough coins for that. WE double this - // amount to be on the safe side. - let payer_coordinator_outbound_liquidity_sat = - (coordinator_payee_inbound_liquity_sat + liquidity_request.fee_sats) * 2; - // How much the _coordinator_ should have in the payer-coordinator channel. This value does not - // matter too much as the coordinator is just receiving in this channel. To avoid complications - // we just set it to the payer's outbound liquidity, so that the channel is balanced. - let payer_coordinator_inbound_liquidity_sat = payer_coordinator_outbound_liquidity_sat; - - // The coordinator will have to (1) open a channel with the payer, (2) push some sats to the - // payer in that channel, (3) open a JIT channel with the payee with a certain amount of - // coordinator outbound liquidity based on the `LiquidityRequest`. The coordinator will need - // sufficient in the on-chain wallet to be able to open these two channels. - let coordinator_fund_amount = payer_coordinator_inbound_liquidity_sat - + payer_coordinator_outbound_liquidity_sat - + expected_coordinator_payee_channel_value; - - // We double this computed amount to be on the safe side. - let coordinator_fund_amount = coordinator_fund_amount * 2; - - coordinator - .fund(Amount::from_sat(coordinator_fund_amount)) - .await - .unwrap(); - - coordinator - .open_private_channel( - payer, - payer_coordinator_inbound_liquidity_sat, - payer_coordinator_outbound_liquidity_sat, - ) - .await - .unwrap(); - - (expected_coordinator_payee_channel_value, liquidity_request) -} - fn random_tmp_dir() -> PathBuf { let tmp = if let Ok(tmp) = std::env::var("RUNNER_TEMP") { tracing::debug!("Running test on github actions - using temporary directory at {tmp}"); @@ -486,23 +359,6 @@ fn log_channel_id(node: &Node, index: u ); } -async fn wait_for_n_usable_channels( - channel_count: usize, - node: &Node, -) -> Result<()> { - wait_until(Duration::from_secs(20), || async { - let usable_channels_length = node.channel_manager.list_usable_channels().len(); - let scids_set = node - .channel_manager - .list_usable_channels() - .iter() - .all(|c| c.get_inbound_payment_scid().is_some()); - - Ok((usable_channels_length == channel_count && scids_set).then_some(())) - }) - .await -} - async fn wait_until(timeout: Duration, predicate_fn: P) -> Result where P: Fn() -> F, @@ -519,55 +375,6 @@ where .await? } -async fn wait_until_sub_channel_state( - timeout: Duration, - node: &Node, - counterparty_pk: PublicKey, - target_state: SubChannelStateName, -) -> Result { - wait_until(timeout, || async { - node.process_incoming_messages()?; - - let dlc_channels = node.dlc_manager.get_store().get_sub_channels()?; - - Ok(dlc_channels - .iter() - .find(|channel| { - let current_state = SubChannelStateName::from(&channel.state); - - tracing::info!( - node_id = %node.info.pubkey, - target = ?target_state, - current = ?current_state, - "Waiting for DLC subchannel to reach state" - ); - - channel.counter_party == counterparty_pk && current_state == target_state - }) - .cloned()) - }) - .await -} - -/// Calculate the fee paid to route a payment through a node, in msat. That is, the difference -/// between the inbound HTLC and the outbound HTLC. -/// -/// The `channel_config` is that of the routing node. -fn calculate_routing_fee_msat( - channel_config: lightning::util::config::ChannelConfig, - invoice_amount_sat: u64, -) -> u64 { - let flat_fee_msat = Decimal::from(channel_config.forwarding_fee_base_msat); - let forwarding_fee_millionths_of_a_sat_per_sat = - Decimal::from(channel_config.forwarding_fee_proportional_millionths); - - let proportional_fee_msat_per_sat = - forwarding_fee_millionths_of_a_sat_per_sat / Decimal::ONE_THOUSAND; - let proportional_fee_msat = Decimal::from(invoice_amount_sat) * proportional_fee_msat_per_sat; - - (flat_fee_msat + proportional_fee_msat).to_u64().unwrap() -} - #[derive(PartialEq, Debug)] enum SubChannelStateName { Offered, diff --git a/crates/ln-dlc-node/src/tests/multi_hop_payment.rs b/crates/ln-dlc-node/src/tests/multi_hop_payment.rs deleted file mode 100644 index b65c5d624..000000000 --- a/crates/ln-dlc-node/src/tests/multi_hop_payment.rs +++ /dev/null @@ -1,137 +0,0 @@ -use crate::node::InMemoryStore; -use crate::node::Node; -use crate::storage::TenTenOneInMemoryStorage; -use crate::tests::calculate_routing_fee_msat; -use crate::tests::init_tracing; -use crate::tests::wait_for_n_usable_channels; -use bitcoin::Amount; -use rust_decimal::prelude::ToPrimitive; -use rust_decimal::Decimal; - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn multi_hop_payment() { - init_tracing(); - - // Arrange - - let (payer, _running_payer) = Node::start_test_app("payer").unwrap(); - let (coordinator, _running_coord) = Node::start_test_coordinator("coordinator").unwrap(); - let (payee, _running_payee) = Node::start_test_app("payee").unwrap(); - - payer.connect(coordinator.info).await.unwrap(); - payee.connect(coordinator.info).await.unwrap(); - - coordinator.fund(Amount::from_sat(50_000)).await.unwrap(); - - let payer_outbound_liquidity_sat = 20_000; - let coordinator_outbound_liquidity_sat = - min_outbound_liquidity_channel_creator(&payer, payer_outbound_liquidity_sat); - coordinator - .open_private_channel( - &payer, - coordinator_outbound_liquidity_sat, - payer_outbound_liquidity_sat, - ) - .await - .unwrap(); - - coordinator - .open_private_channel(&payee, 20_000, 0) - .await - .unwrap(); - - wait_for_n_usable_channels(2, &coordinator).await.unwrap(); - - let payer_balance_before = payer.get_ldk_balance(); - let coordinator_balance_before = coordinator.get_ldk_balance(); - let payee_balance_before = payee.get_ldk_balance(); - - payer.sync_wallets().await.unwrap(); - coordinator.sync_wallets().await.unwrap(); - payee.sync_wallets().await.unwrap(); - - // Act - - let invoice_amount_sat = 4_000; - let invoice = payee - .create_invoice(invoice_amount_sat, "".to_string(), 180) - .unwrap(); - let invoice_amount_msat = invoice.amount_milli_satoshis().unwrap(); - - let routing_fee_msat = calculate_routing_fee_msat( - coordinator.ldk_config.read().channel_config, - invoice_amount_sat, - ); - - payer.pay_invoice(&invoice, None).unwrap(); - - payee - .wait_for_payment_claimed(invoice.payment_hash()) - .await - .unwrap(); - - // Assert - - // Sync LN wallet after payment is claimed to update the balances - payer.sync_wallets().await.unwrap(); - coordinator.sync_wallets().await.unwrap(); - payee.sync_wallets().await.unwrap(); - - let payer_balance_after = payer.get_ldk_balance(); - let coordinator_balance_after = coordinator.get_ldk_balance(); - let payee_balance_after = payee.get_ldk_balance(); - - assert_eq!( - payer_balance_before.available_msat() - payer_balance_after.available_msat(), - invoice_amount_msat + routing_fee_msat - ); - - assert_eq!( - coordinator_balance_after.available_msat() - coordinator_balance_before.available_msat(), - routing_fee_msat - ); - - assert_eq!( - payee_balance_after.available_msat() - payee_balance_before.available_msat(), - invoice_amount_msat - ); -} - -/// Calculate the "minimum" acceptable value for the outbound liquidity -/// of the channel creator. -/// -/// The value calculated is not guaranteed to be the exact minimum, -/// but it should be close enough. -/// -/// This is useful when the channel creator wants to push as many -/// coins as possible to their peer on channel creation. -fn min_outbound_liquidity_channel_creator( - peer: &Node, - peer_balance: u64, -) -> u64 { - let min_reserve_millionths_creator = Decimal::from( - peer.ldk_config - .read() - .channel_handshake_config - .their_channel_reserve_proportional_millionths, - ); - - let min_reserve_percent_creator = min_reserve_millionths_creator / Decimal::from(1_000_000); - - // This is an approximation as we assume that `channel_balance ~= - // peer_balance` - let channel_balance_estimate = Decimal::from(peer_balance); - - let min_reserve_creator = min_reserve_percent_creator * channel_balance_estimate; - let min_reserve_creator = min_reserve_creator.to_u64().unwrap(); - - // The minimum reserve for any party is actually hard-coded to - // 1_000 sats by LDK - let min_reserve_creator = min_reserve_creator.max(1_000); - - // This is just an upper bound - let commit_transaction_fee = 1_000; - - min_reserve_creator + commit_transaction_fee -} diff --git a/crates/ln-dlc-node/src/tests/probe.rs b/crates/ln-dlc-node/src/tests/probe.rs deleted file mode 100644 index 97821d82a..000000000 --- a/crates/ln-dlc-node/src/tests/probe.rs +++ /dev/null @@ -1,126 +0,0 @@ -use crate::node::Node; -use crate::tests::bitcoind::mine; -use crate::tests::init_tracing; -use bitcoin::Amount; -use lightning::ln::channelmanager::ChannelDetails; -use std::time::Duration; - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn estimate_payment_fee() { - init_tracing(); - - // Arrange - - let (payer, _running_payer) = Node::start_test_app("payer").unwrap(); - let (a, _running_coord) = Node::start_test_coordinator("a").unwrap(); - let (b, _running_coord) = Node::start_test_coordinator("b").unwrap(); - let (c, _running_coord) = Node::start_test_coordinator("c").unwrap(); - let (d, _running_coord) = Node::start_test_coordinator("d").unwrap(); - let (payee, _running_payee) = Node::start_test_app("payee").unwrap(); - - payer.connect(a.info).await.unwrap(); - a.connect(b.info).await.unwrap(); - b.connect(c.info).await.unwrap(); - c.connect(d.info).await.unwrap(); - d.connect(payee.info).await.unwrap(); - - payer.fund(Amount::from_sat(100_000)).await.unwrap(); - a.fund(Amount::from_sat(100_000)).await.unwrap(); - b.fund(Amount::from_sat(100_000)).await.unwrap(); - c.fund(Amount::from_sat(100_000)).await.unwrap(); - d.fund(Amount::from_sat(100_000)).await.unwrap(); - - payer.open_public_channel(&a, 20_000, 20_000).await.unwrap(); - let channel1 = a.open_public_channel(&b, 20_000, 20_000).await.unwrap(); - let channel2 = b.open_public_channel(&c, 20_000, 20_000).await.unwrap(); - let channel3 = c.open_public_channel(&d, 20_000, 20_000).await.unwrap(); - d.open_private_channel(&payee, 20_000, 20_000) - .await - .unwrap(); - - mine(6).await.unwrap(); - - tracing::info!("Waiting for channels to be discovered"); - - for _ in 0..5 { - payer.sync_wallets().await.unwrap(); - a.sync_wallets().await.unwrap(); - b.sync_wallets().await.unwrap(); - c.sync_wallets().await.unwrap(); - d.sync_wallets().await.unwrap(); - payee.sync_wallets().await.unwrap(); - - payer.broadcast_node_announcement(); - a.broadcast_node_announcement(); - b.broadcast_node_announcement(); - c.broadcast_node_announcement(); - d.broadcast_node_announcement(); - payee.broadcast_node_announcement(); - - tokio::time::sleep(std::time::Duration::from_secs(2)).await; - } - - tracing::info!("Done waiting for channels to be discovered"); - - // Successful fee estimate - - // Act - - let invoice_amount_sat = 5_000; - let invoice = payee - .create_invoice(invoice_amount_sat, "".to_string(), 180) - .unwrap(); - - let estimated_fee_msat = payer - .estimate_payment_fee_msat(invoice, None, Duration::from_secs(60)) - .await - .unwrap(); - - // Assert - - // We only consider the 3 intermediate hops because the `payee`-to-`a` channel does not take a - // fee and the last one is a private channel which is unannounced, meaning the probe will not - // try going all the way there. - let expected_fee_msat = calculate_fee_msat(channel1, invoice_amount_sat) - + calculate_fee_msat(channel2, invoice_amount_sat) - + calculate_fee_msat(channel3, invoice_amount_sat); - - assert_eq!(estimated_fee_msat, expected_fee_msat); - - // Failed fee estimate due to route-not-found caused by insufficient liquidity - - // Act - - let invoice_amount_sat = 50_000; - let invoice = payee - .create_invoice(invoice_amount_sat, "".to_string(), 180) - .unwrap(); - - let res = payer - .estimate_payment_fee_msat(invoice, None, Duration::from_secs(60)) - .await; - - // Assert - - assert!(res.is_err()) -} - -fn calculate_fee_msat(channel_details: ChannelDetails, invoice_amount_sat: u64) -> u64 { - let invoice_amount_msat = (invoice_amount_sat * 1_000) as f32; - - let forwarding_fee_proportional_millionths = channel_details - .config - .unwrap() - .forwarding_fee_proportional_millionths - as f32; - let fee_as_percentage_of_invoice_amount_msat = - forwarding_fee_proportional_millionths / 1_000_000.0; - - let proportional_fee_msat = - (fee_as_percentage_of_invoice_amount_msat * invoice_amount_msat) as u64; - - let flat_fee_msat = channel_details.config.unwrap().forwarding_fee_base_msat as u64; - - proportional_fee_msat + flat_fee_msat -} diff --git a/crates/ln-dlc-node/src/tests/single_hop_payment.rs b/crates/ln-dlc-node/src/tests/single_hop_payment.rs deleted file mode 100644 index fa97325d2..000000000 --- a/crates/ln-dlc-node/src/tests/single_hop_payment.rs +++ /dev/null @@ -1,64 +0,0 @@ -use crate::node::Node; -use crate::tests::init_tracing; -use crate::tests::wait_for_n_usable_channels; -use bitcoin::Amount; - -#[tokio::test(flavor = "multi_thread")] -#[ignore] -async fn single_hop_payment() { - init_tracing(); - - // Arrange - - let (payer, _running_payer) = Node::start_test_app("payer").unwrap(); - let (payee, _running_payee) = Node::start_test_app("payee").unwrap(); - - payer.connect(payee.info).await.unwrap(); - - payer.fund(Amount::from_btc(0.1).unwrap()).await.unwrap(); - - payer.open_private_channel(&payee, 30_000, 0).await.unwrap(); - - // after creating the just-in-time channel. The coordinator should have exactly 2 usable - // channels with short channel ids. - wait_for_n_usable_channels(1, &payer).await.unwrap(); - - let payer_balance_before = payer.get_ldk_balance(); - let payee_balance_before = payee.get_ldk_balance(); - - // No mining step needed because the channels are _implicitly_ - // configured to support 0-conf - - // Act - - let invoice_amount = 3_000; - let invoice = payee - .create_invoice(invoice_amount, "".to_string(), 180) - .unwrap(); - - payer.pay_invoice(&invoice, None).unwrap(); - - payee - .wait_for_payment_claimed(invoice.payment_hash()) - .await - .unwrap(); - - // Assert - - // Sync LN wallet after payment is claimed to update the balances - payer.sync_wallets().await.unwrap(); - payee.sync_wallets().await.unwrap(); - - let payer_balance_after = payer.get_ldk_balance(); - let payee_balance_after = payee.get_ldk_balance(); - - assert_eq!( - payer_balance_before.available() - payer_balance_after.available(), - invoice_amount - ); - - assert_eq!( - payee_balance_after.available() - payee_balance_before.available(), - invoice_amount - ); -}