Skip to content

Commit

Permalink
Open LN channel from incoming Payjoin tx
Browse files Browse the repository at this point in the history
This commit allows users to schedule a channel that will opened
once a Payjoin request received. This can save users 1 extra onchain
transaction fees.

The Payjoin flow is normal with the following caveats:
1. We use `Payjoin::ProvisionalProposal::substitue_output_address` to
point to the multisig output script as retrived from
`LdkEvent::FundingGeneratingReady`.
2. We dont try to preserve privacy in Payjoin channel opening
transactions.
3. We wait with our response to the Payjoin sender until a
`Ldk::Event::FundingTxBroadcastSafe` event is received.
  • Loading branch information
jbesraa committed Jun 24, 2024
1 parent 816fa47 commit 2cc6132
Show file tree
Hide file tree
Showing 10 changed files with 639 additions and 39 deletions.
38 changes: 19 additions & 19 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,23 @@ panic = 'abort' # Abort on panic
default = []

[dependencies]
lightning = { version = "0.0.123", features = ["std"] }
lightning-invoice = { version = "0.31.0" }
lightning-net-tokio = { version = "0.0.123" }
lightning-persister = { version = "0.0.123" }
lightning-background-processor = { version = "0.0.123", features = ["futures"] }
lightning-rapid-gossip-sync = { version = "0.0.123" }
lightning-transaction-sync = { version = "0.0.123", features = ["esplora-async-https", "time"] }
lightning-liquidity = { version = "0.1.0-alpha.4", features = ["std"] }

#lightning = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main", features = ["std"] }
#lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main" }
#lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main" }
#lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main" }
#lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main", features = ["futures"] }
#lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main" }
#lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main", features = ["esplora-async"] }
#lightning-liquidity = { git = "https://github.com/lightningdevkit/lightning-liquidity", branch="main", features = ["std"] }
# lightning = { version = "0.0.123", features = ["std"] }
# lightning-invoice = { version = "0.31.0" }
# lightning-net-tokio = { version = "0.0.123" }
# lightning-persister = { version = "0.0.123" }
# lightning-background-processor = { version = "0.0.123", features = ["futures"] }
# lightning-rapid-gossip-sync = { version = "0.0.123" }
# lightning-transaction-sync = { version = "0.0.123", features = ["esplora-async-https", "time"] }
# lightning-liquidity = { version = "0.1.0-alpha.4", features = ["std"] }

lightning = { git = "https://github.com/jbesraa/rust-lightning.git", branch="0.0.123-with-funding-brodsafe-event", features = ["std"] }
lightning-invoice = { git = "https://github.com/jbesraa/rust-lightning.git", branch="0.0.123-with-funding-brodsafe-event" }
lightning-net-tokio = { git = "https://github.com/jbesraa/rust-lightning.git", branch="0.0.123-with-funding-brodsafe-event" }
lightning-persister = { git = "https://github.com/jbesraa/rust-lightning.git", branch="0.0.123-with-funding-brodsafe-event" }
lightning-background-processor = { git = "https://github.com/jbesraa/rust-lightning.git", branch="0.0.123-with-funding-brodsafe-event", features = ["futures"] }
lightning-rapid-gossip-sync = { git = "https://github.com/jbesraa/rust-lightning.git", branch="0.0.123-with-funding-brodsafe-event" }
lightning-transaction-sync = { git = "https://github.com/jbesraa/rust-lightning.git", branch="0.0.123-with-funding-brodsafe-event", features = ["esplora-async"] }
lightning-liquidity = { git = "https://github.com/jbesraa/lightning-liquidity", branch="pj-fixes", features = ["std"] }

#lightning = { path = "../rust-lightning/lightning", features = ["std"] }
#lightning-invoice = { path = "../rust-lightning/lightning-invoice" }
Expand Down Expand Up @@ -78,8 +78,8 @@ prost = { version = "0.11.6", default-features = false}
winapi = { version = "0.3", features = ["winbase"] }

[dev-dependencies]
lightning = { version = "0.0.123", features = ["std", "_test_utils"] }
#lightning = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main", features = ["std", "_test_utils"] }
# lightning = { version = "0.0.123", features = ["std", "_test_utils"] }
lightning = { git = "https://github.com/jbesraa/rust-lightning.git", branch="0.0.123-with-funding-brodsafe-event", features = ["std", "_test_utils"] }
electrum-client = { version = "0.15.1", default-features = true }
bitcoincore-rpc = { version = "0.17.0", default-features = false }
proptest = "1.0.0"
Expand Down
3 changes: 2 additions & 1 deletion src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1024,12 +1024,13 @@ fn build_with_store_internal(
payjoin_receiver = Some(Arc::new(PayjoinReceiver::new(
Arc::clone(&logger),
Arc::clone(&wallet),
Arc::clone(&channel_manager),
Arc::clone(&config),
pj_config.payjoin_directory.clone(),
pj_config.payjoin_relay.clone(),
pj_config.ohttp_keys.clone(),
)));
}

let is_listening = Arc::new(AtomicBool::new(false));
let latest_wallet_sync_timestamp = Arc::new(RwLock::new(None));
let latest_onchain_wallet_sync_timestamp = Arc::new(RwLock::new(None));
Expand Down
60 changes: 58 additions & 2 deletions src/event.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::payjoin_receiver::PayjoinReceiver;
use crate::types::{DynStore, Sweeper, Wallet};

use crate::{
Expand Down Expand Up @@ -375,6 +376,7 @@ where
network_graph: Arc<Graph>,
payment_store: Arc<PaymentStore<L>>,
peer_store: Arc<PeerStore<L>>,
payjoin_receiver: Option<Arc<PayjoinReceiver>>,
runtime: Arc<RwLock<Option<tokio::runtime::Runtime>>>,
logger: L,
config: Arc<Config>,
Expand All @@ -389,8 +391,9 @@ where
bump_tx_event_handler: Arc<BumpTransactionEventHandler>,
channel_manager: Arc<ChannelManager>, connection_manager: Arc<ConnectionManager<L>>,
output_sweeper: Arc<Sweeper>, network_graph: Arc<Graph>,
payment_store: Arc<PaymentStore<L>>, peer_store: Arc<PeerStore<L>>,
runtime: Arc<RwLock<Option<tokio::runtime::Runtime>>>, logger: L, config: Arc<Config>,
payment_store: Arc<PaymentStore<L>>, payjoin_receiver: Option<Arc<PayjoinReceiver>>,
peer_store: Arc<PeerStore<L>>, runtime: Arc<RwLock<Option<tokio::runtime::Runtime>>>,
logger: L, config: Arc<Config>,
) -> Self {
Self {
event_queue,
Expand All @@ -401,6 +404,7 @@ where
output_sweeper,
network_graph,
payment_store,
payjoin_receiver,
peer_store,
logger,
runtime,
Expand All @@ -415,6 +419,7 @@ where
counterparty_node_id,
channel_value_satoshis,
output_script,
user_channel_id,
..
} => {
// Construct the raw transaction with the output that is paid the amount of the
Expand All @@ -425,6 +430,18 @@ where
let cur_height = self.channel_manager.current_best_block().height;
let locktime = LockTime::from_height(cur_height).unwrap_or(LockTime::ZERO);

if let Some(payjoin_receiver) = self.payjoin_receiver.clone() {
if payjoin_receiver
.set_channel_accepted(
user_channel_id,
&output_script,
temporary_channel_id.0,
)
.await
{
return;
}
}
// Sign the final funding transaction and broadcast it.
match self.wallet.create_funding_transaction(
output_script,
Expand Down Expand Up @@ -1087,6 +1104,45 @@ where
);
}
},
LdkEvent::FundingTxBroadcastSafe { funding_tx, .. } => {
use crate::io::utils::ohttp_headers;
if let Some(payjoin_receiver) = self.payjoin_receiver.clone() {
let is_payjoin_channel =
payjoin_receiver.set_funding_tx_signed(funding_tx.clone()).await;
if let Some((url, body)) = is_payjoin_channel {
log_info!(
self.logger,
"Detected payjoin channel transaction. Sending payjoin sender request for transaction {}",
funding_tx.txid()
);
let headers = ohttp_headers();
let client = reqwest::Client::builder().build().unwrap();
match client.post(url).body(body).headers(headers).send().await {
Ok(response) => {
if response.status().is_success() {
log_info!(
self.logger,
"Responded to 'Payjoin Sender' successfuly"
);
} else {
log_info!(
self.logger,
"Got unsuccessful response from 'Payjoin Sender': {}",
response.status()
);
}
},
Err(e) => {
log_error!(
self.logger,
"Failed to send a response to 'Payjoin Sender': {}",
e
);
},
};
}
}
},
LdkEvent::ChannelPending {
channel_id,
user_channel_id,
Expand Down
8 changes: 8 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ pub mod io;
mod liquidity;
mod logger;
mod message_handler;
mod payjoin_channel_scheduler;
mod payjoin_receiver;
mod payjoin_sender;
pub mod payment;
Expand Down Expand Up @@ -726,6 +727,7 @@ impl Node {
Arc::clone(&self.output_sweeper),
Arc::clone(&self.network_graph),
Arc::clone(&self.payment_store),
self.payjoin_receiver.clone(),
Arc::clone(&self.peer_store),
Arc::clone(&self.runtime),
Arc::clone(&self.logger),
Expand Down Expand Up @@ -1111,6 +1113,9 @@ impl Node {
payjoin_receiver.map(Arc::clone),
Arc::clone(&self.config),
Arc::clone(&self.event_queue),
Arc::clone(&self.peer_store),
Arc::clone(&self.channel_manager),
Arc::clone(&self.connection_manager),
)
}

Expand All @@ -1130,6 +1135,9 @@ impl Node {
payjoin_receiver.map(Arc::clone),
Arc::clone(&self.config),
Arc::clone(&self.event_queue),
Arc::clone(&self.peer_store),
Arc::clone(&self.channel_manager),
Arc::clone(&self.connection_manager),
))
}

Expand Down
Loading

0 comments on commit 2cc6132

Please sign in to comment.