Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Wait for outbound capacity before paying jit channel open fee #1015

Merged
merged 1 commit into from
Jul 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions crates/tests-e2e/src/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,6 @@ impl TestSetup {
.await
.expect("to be able to fund");

// FIXME: Waiting here on >= as this test run on the CI can't find a route when trying to
// pay immediately after claiming a received payment.
// See: https://github.com/get10101/10101/issues/883
let ln_balance = app
.rx
.wallet_info()
Expand All @@ -70,7 +67,7 @@ impl TestSetup {
.expect("have wallet_info")
.balances
.lightning
>= funded_amount
== funded_amount
);

Self { app, coordinator }
Expand Down
13 changes: 5 additions & 8 deletions crates/tests-e2e/tests/basic.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
use anyhow::Result;
use assertables::assert_ge;
use assertables::assert_ge_as_result;
use bitcoin::Address;
use bitcoin::Amount;
use std::str::FromStr;
Expand Down Expand Up @@ -43,11 +41,10 @@ async fn app_can_be_funded_with_lnd_faucet() -> Result<()> {

assert_eq!(app.rx.wallet_info().unwrap().balances.on_chain, 0);

// FIXME: Asserting here on >= as this test run on the CI can't find a route when trying to pay
// immediately after claiming a received payment.
// See: https://github.com/get10101/10101/issues/883
let ln_balance = app.rx.wallet_info().unwrap().balances.lightning;
tracing::info!(%funded_amount, %ln_balance, "Successfully funded app with faucet");
assert_ge!(ln_balance, funded_amount);
tracing::info!(%funded_amount, "Successfully funded app with faucet");
assert_eq!(
app.rx.wallet_info().unwrap().balances.lightning,
funded_amount
);
Ok(())
}
6 changes: 1 addition & 5 deletions crates/tests-e2e/tests/receive_payment_when_open_position.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use native::api;
use tests_e2e::fund::FUNDING_TRANSACTION_FEES;
use tests_e2e::setup;
use tests_e2e::wait_until;
use tokio::task::spawn_blocking;
Expand All @@ -26,8 +25,5 @@ async fn can_receive_payment_with_open_position() {
let ln_balance = app.rx.wallet_info().unwrap().balances.lightning;
tracing::info!(%ln_balance, %ln_balance_before, %invoice_amount, "Lightning balance increased");

// FIXME: Change this assertion when the reason why we're being charged
// transaction funding fees now is found.
// See: https://github.com/get10101/10101/issues/883
assert!(ln_balance >= ln_balance_before + invoice_amount - FUNDING_TRANSACTION_FEES);
assert_eq!(ln_balance, ln_balance_before + invoice_amount);
}
2 changes: 0 additions & 2 deletions mobile/native/src/api.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::calculations;
use crate::channel_fee::ChannelFeePaymentSubscriber;
use crate::commons::api::ChannelInfo;
use crate::commons::api::Price;
use crate::config;
Expand Down Expand Up @@ -231,7 +230,6 @@ pub fn run(
db::init_db(&app_dir, get_network())?;
let runtime = ln_dlc::get_or_create_tokio_runtime()?;
ln_dlc::run(app_dir, seed_dir, runtime)?;
event::subscribe(ChannelFeePaymentSubscriber::new());
holzeis marked this conversation as resolved.
Show resolved Hide resolved

let (_health, tx) = health::Health::new(config, runtime);

Expand Down
95 changes: 71 additions & 24 deletions mobile/native/src/channel_fee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,24 @@ use crate::event::EventInternal;
use crate::event::EventType;
use crate::ln_dlc;
use anyhow::anyhow;
use anyhow::bail;
use anyhow::Context;
use anyhow::Result;
use bitcoin::Txid;
use lightning_invoice::Invoice;
use ln_dlc_node::node::rust_dlc_manager::subchannel::LNChannelManager;
use ln_dlc_node::node::rust_dlc_manager::ChannelId;
use ln_dlc_node::node::ChannelManager;
use serde::Deserialize;
use serde::Serialize;
use std::str::FromStr;
use std::sync::Arc;
use std::sync::Mutex;
use std::time::Duration;
use tokio::runtime::Handle;

#[derive(Clone)]
pub struct ChannelFeePaymentSubscriber {
pub open_channel_tx: Arc<Mutex<Option<EsploraTransaction>>>,
pub open_channel_info: Arc<Mutex<Option<(ChannelId, EsploraTransaction)>>>,
pub channel_manager: Arc<ChannelManager>,
}

#[derive(Deserialize, Serialize, Debug, Clone)]
Expand All @@ -31,9 +34,7 @@ pub struct EsploraTransaction {
impl Subscriber for ChannelFeePaymentSubscriber {
fn notify(&self, event: &EventInternal) {
let result = match event {
EventInternal::ChannelReady(channel_id) => {
self.register_funding_transaction(channel_id)
}
EventInternal::ChannelReady(channel_id) => self.register_channel_open_info(channel_id),
EventInternal::PaymentClaimed(amount_msats) => {
self.pay_funding_transaction_fees(*amount_msats)
holzeis marked this conversation as resolved.
Show resolved Hide resolved
}
Expand All @@ -51,24 +52,39 @@ impl Subscriber for ChannelFeePaymentSubscriber {
}

impl ChannelFeePaymentSubscriber {
pub fn new() -> Self {
pub fn new(channel_manager: Arc<ChannelManager>) -> Self {
Self {
open_channel_tx: Arc::new(Mutex::new(None)),
open_channel_info: Arc::new(Mutex::new(None)),
channel_manager,
}
}

/// Attempts to pay the transaction fees for opening an inbound channel.
fn pay_funding_transaction_fees(&self, amount_msats: u64) -> Result<()> {
let transaction = match self.get_funding_transaction() {
Some(transaction) => transaction,
let (channel_id, transaction) = match self.get_open_channel_info() {
Some((channel_id, transaction)) => (channel_id, transaction),
None => {
tracing::debug!("No pending funding transaction found!");
return Ok(());
}
};

tracing::debug!("Trying to pay channel opening fees of {}", transaction.fee);
let funding_tx_fees_msats = (transaction.fee * 1000) as u64;

tokio::task::block_in_place(|| {
tracing::debug!(
channel_id = hex::encode(channel_id),
funding_tx_fees_msats,
"Waiting for outbound capacity on channel to pay jit channel opening fee.",
);
Handle::current()
.block_on(self.wait_for_outbound_capacity(channel_id, funding_tx_fees_msats))
})?;

tracing::debug!(
"Trying to pay channel opening fees of {} sats",
transaction.fee
);
let funding_txid = transaction.txid;

if funding_tx_fees_msats > amount_msats {
Expand All @@ -82,13 +98,10 @@ impl ChannelFeePaymentSubscriber {
))
})?;

let invoice = Invoice::from_str(&invoice_str).context("Could not parse Invoice string")?;
let _payment_hash = invoice.payment_hash();

match ln_dlc::send_payment(&invoice_str) {
Ok(_) => {
// unset the funding transaction marking it as being paid.
self.unset_funding_transaction();
self.unset_open_channel_info();
tracing::info!("Successfully triggered funding transaction fees payment of {funding_tx_fees_msats} msats to {}", config::get_coordinator_info().pubkey);
}
Err(e) => {
Expand All @@ -100,7 +113,7 @@ impl ChannelFeePaymentSubscriber {
}

/// Register jit channel opening transaction for fee payment
fn register_funding_transaction(&self, channel_id: &ChannelId) -> Result<()> {
fn register_channel_open_info(&self, channel_id: &ChannelId) -> Result<()> {
let channel_id_as_str = hex::encode(channel_id);
tracing::debug!("Received new inbound channel with id {channel_id_as_str}");

Expand All @@ -110,30 +123,64 @@ impl ChannelFeePaymentSubscriber {
Handle::current().block_on(fetch_funding_transaction(txid))
})?;
tracing::debug!("Successfully fetched transaction fees of {} for new inbound channel with id {channel_id_as_str}", transaction.fee);
self.set_funding_transaction(transaction);
self.set_open_channel_info(channel_id, transaction);
Ok(())
}

fn set_funding_transaction(&self, transaction: EsploraTransaction) {
fn set_open_channel_info(&self, channel_id: &ChannelId, transaction: EsploraTransaction) {
*self
.open_channel_tx
.open_channel_info
.lock()
.expect("Mutex to not be poisoned") = Some(transaction);
.expect("Mutex to not be poisoned") = Some((*channel_id, transaction));
}

fn unset_funding_transaction(&self) {
fn unset_open_channel_info(&self) {
*self
.open_channel_tx
.open_channel_info
.lock()
.expect("Mutex to not be poisoned") = None;
}

fn get_funding_transaction(&self) -> Option<EsploraTransaction> {
self.open_channel_tx
fn get_open_channel_info(&self) -> Option<(ChannelId, EsploraTransaction)> {
self.open_channel_info
.lock()
.expect("Mutex to not be poisoned")
.clone()
}

async fn wait_for_outbound_capacity(
&self,
channel_id: ChannelId,
funding_tx_fees_msats: u64,
) -> Result<()> {
tokio::time::timeout(Duration::from_secs(5), async {
loop {
let channel_details = match self
.channel_manager
.get_channel_details(&channel_id) {
Some(channel_details) => channel_details,
None => {
bail!("Could not find channel details for {}", hex::encode(channel_id));
},
};

if channel_details.outbound_capacity_msat >= funding_tx_fees_msats {
holzeis marked this conversation as resolved.
Show resolved Hide resolved
tracing::debug!(channel_details.outbound_capacity_msat, channel_id=hex::encode(channel_id),
"Channel has enough outbound capacity");
return Ok(())
} else {
tracing::debug!(channel_id = hex::encode(channel_id), outbound_capacity_msats = channel_details.outbound_capacity_msat, funding_tx_fees_msats,
"Channel does not have enough outbound capacity to pay jit channel opening fees yet. Waiting.");
tokio::time::sleep(Duration::from_millis(200)).await
}
}
})
.await?.map_err(|e| anyhow!("{e:#}"))
.with_context(||format!(
"Timed-out waiting for channel {} to become usable",
hex::encode(channel_id)
))
}
}

async fn fetch_funding_transaction(txid: Txid) -> Result<EsploraTransaction> {
Expand Down
5 changes: 5 additions & 0 deletions mobile/native/src/ln_dlc/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use self::node::WalletHistories;
use crate::api;
use crate::calculations;
use crate::channel_fee::ChannelFeePaymentSubscriber;
use crate::commons::reqwest_client;
use crate::config;
use crate::event;
Expand Down Expand Up @@ -246,6 +247,10 @@ pub fn run(data_dir: String, seed_dir: String, runtime: &Runtime) -> Result<()>
}
});

event::subscribe(ChannelFeePaymentSubscriber::new(
node.inner.channel_manager.clone(),
));

NODE.set(node);

event::publish(&EventInternal::Init("10101 is ready.".to_string()));
Expand Down