Skip to content

Commit b4859e5

Browse files
committed
Open LN channel from incoming Payjoin tx
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.
1 parent dd890bc commit b4859e5

10 files changed

+641
-39
lines changed

Cargo.toml

+19-19
Original file line numberDiff line numberDiff line change
@@ -28,23 +28,23 @@ panic = 'abort' # Abort on panic
2828
default = []
2929

3030
[dependencies]
31-
lightning = { version = "0.0.123", features = ["std"] }
32-
lightning-invoice = { version = "0.31.0" }
33-
lightning-net-tokio = { version = "0.0.123" }
34-
lightning-persister = { version = "0.0.123" }
35-
lightning-background-processor = { version = "0.0.123", features = ["futures"] }
36-
lightning-rapid-gossip-sync = { version = "0.0.123" }
37-
lightning-transaction-sync = { version = "0.0.123", features = ["esplora-async-https", "time"] }
38-
lightning-liquidity = { version = "0.1.0-alpha.4", features = ["std"] }
39-
40-
#lightning = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main", features = ["std"] }
41-
#lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main" }
42-
#lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main" }
43-
#lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main" }
44-
#lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main", features = ["futures"] }
45-
#lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main" }
46-
#lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main", features = ["esplora-async"] }
47-
#lightning-liquidity = { git = "https://github.com/lightningdevkit/lightning-liquidity", branch="main", features = ["std"] }
31+
# lightning = { version = "0.0.123", features = ["std"] }
32+
# lightning-invoice = { version = "0.31.0" }
33+
# lightning-net-tokio = { version = "0.0.123" }
34+
# lightning-persister = { version = "0.0.123" }
35+
# lightning-background-processor = { version = "0.0.123", features = ["futures"] }
36+
# lightning-rapid-gossip-sync = { version = "0.0.123" }
37+
# lightning-transaction-sync = { version = "0.0.123", features = ["esplora-async-https", "time"] }
38+
# lightning-liquidity = { version = "0.1.0-alpha.4", features = ["std"] }
39+
40+
lightning = { git = "https://github.com/jbesraa/rust-lightning.git", branch="0.0.123-with-funding-brodsafe-event", features = ["std"] }
41+
lightning-invoice = { git = "https://github.com/jbesraa/rust-lightning.git", branch="0.0.123-with-funding-brodsafe-event" }
42+
lightning-net-tokio = { git = "https://github.com/jbesraa/rust-lightning.git", branch="0.0.123-with-funding-brodsafe-event" }
43+
lightning-persister = { git = "https://github.com/jbesraa/rust-lightning.git", branch="0.0.123-with-funding-brodsafe-event" }
44+
lightning-background-processor = { git = "https://github.com/jbesraa/rust-lightning.git", branch="0.0.123-with-funding-brodsafe-event", features = ["futures"] }
45+
lightning-rapid-gossip-sync = { git = "https://github.com/jbesraa/rust-lightning.git", branch="0.0.123-with-funding-brodsafe-event" }
46+
lightning-transaction-sync = { git = "https://github.com/jbesraa/rust-lightning.git", branch="0.0.123-with-funding-brodsafe-event", features = ["esplora-async"] }
47+
lightning-liquidity = { git = "https://github.com/jbesraa/lightning-liquidity", branch="pj-fixes", features = ["std"] }
4848

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

8080
[dev-dependencies]
81-
lightning = { version = "0.0.123", features = ["std", "_test_utils"] }
82-
#lightning = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main", features = ["std", "_test_utils"] }
81+
# lightning = { version = "0.0.123", features = ["std", "_test_utils"] }
82+
lightning = { git = "https://github.com/jbesraa/rust-lightning.git", branch="0.0.123-with-funding-brodsafe-event", features = ["std", "_test_utils"] }
8383
electrum-client = { version = "0.15.1", default-features = true }
8484
bitcoincore-rpc = { version = "0.17.0", default-features = false }
8585
proptest = "1.0.0"

src/builder.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1019,12 +1019,13 @@ fn build_with_store_internal(
10191019
payjoin_receiver = Some(Arc::new(PayjoinReceiver::new(
10201020
Arc::clone(&logger),
10211021
Arc::clone(&wallet),
1022+
Arc::clone(&channel_manager),
1023+
Arc::clone(&config),
10221024
pj_config.payjoin_directory.clone(),
10231025
pj_config.payjoin_relay.clone(),
10241026
pj_config.ohttp_keys.clone(),
10251027
)));
10261028
}
1027-
10281029
let is_listening = Arc::new(AtomicBool::new(false));
10291030
let latest_wallet_sync_timestamp = Arc::new(RwLock::new(None));
10301031
let latest_onchain_wallet_sync_timestamp = Arc::new(RwLock::new(None));

src/event.rs

+58-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::payjoin_receiver::PayjoinReceiver;
12
use crate::types::{DynStore, Sweeper, Wallet};
23

34
use crate::{
@@ -385,6 +386,7 @@ where
385386
network_graph: Arc<Graph>,
386387
payment_store: Arc<PaymentStore<L>>,
387388
peer_store: Arc<PeerStore<L>>,
389+
payjoin_receiver: Option<Arc<PayjoinReceiver>>,
388390
runtime: Arc<RwLock<Option<tokio::runtime::Runtime>>>,
389391
logger: L,
390392
config: Arc<Config>,
@@ -399,8 +401,9 @@ where
399401
bump_tx_event_handler: Arc<BumpTransactionEventHandler>,
400402
channel_manager: Arc<ChannelManager>, connection_manager: Arc<ConnectionManager<L>>,
401403
output_sweeper: Arc<Sweeper>, network_graph: Arc<Graph>,
402-
payment_store: Arc<PaymentStore<L>>, peer_store: Arc<PeerStore<L>>,
403-
runtime: Arc<RwLock<Option<tokio::runtime::Runtime>>>, logger: L, config: Arc<Config>,
404+
payment_store: Arc<PaymentStore<L>>, payjoin_receiver: Option<Arc<PayjoinReceiver>>,
405+
peer_store: Arc<PeerStore<L>>, runtime: Arc<RwLock<Option<tokio::runtime::Runtime>>>,
406+
logger: L, config: Arc<Config>,
404407
) -> Self {
405408
Self {
406409
event_queue,
@@ -411,6 +414,7 @@ where
411414
output_sweeper,
412415
network_graph,
413416
payment_store,
417+
payjoin_receiver,
414418
peer_store,
415419
logger,
416420
runtime,
@@ -425,6 +429,7 @@ where
425429
counterparty_node_id,
426430
channel_value_satoshis,
427431
output_script,
432+
user_channel_id,
428433
..
429434
} => {
430435
// Construct the raw transaction with the output that is paid the amount of the
@@ -435,6 +440,18 @@ where
435440
let cur_height = self.channel_manager.current_best_block().height;
436441
let locktime = LockTime::from_height(cur_height).unwrap_or(LockTime::ZERO);
437442

443+
if let Some(payjoin_receiver) = self.payjoin_receiver.clone() {
444+
if payjoin_receiver
445+
.set_channel_accepted(
446+
user_channel_id,
447+
&output_script,
448+
temporary_channel_id.0,
449+
)
450+
.await
451+
{
452+
return;
453+
}
454+
}
438455
// Sign the final funding transaction and broadcast it.
439456
match self.wallet.create_funding_transaction(
440457
output_script,
@@ -1097,6 +1114,45 @@ where
10971114
);
10981115
}
10991116
},
1117+
LdkEvent::FundingTxBroadcastSafe { funding_tx, .. } => {
1118+
use crate::io::utils::ohttp_headers;
1119+
if let Some(payjoin_receiver) = self.payjoin_receiver.clone() {
1120+
let is_payjoin_channel =
1121+
payjoin_receiver.set_funding_tx_signed(funding_tx.clone()).await;
1122+
if let Some((url, body)) = is_payjoin_channel {
1123+
log_info!(
1124+
self.logger,
1125+
"Detected payjoin channel transaction. Sending payjoin sender request for transaction {}",
1126+
funding_tx.txid()
1127+
);
1128+
let headers = ohttp_headers();
1129+
let client = reqwest::Client::builder().build().unwrap();
1130+
match client.post(url).body(body).headers(headers).send().await {
1131+
Ok(response) => {
1132+
if response.status().is_success() {
1133+
log_info!(
1134+
self.logger,
1135+
"Responded to 'Payjoin Sender' successfuly"
1136+
);
1137+
} else {
1138+
log_info!(
1139+
self.logger,
1140+
"Got unsuccessful response from 'Payjoin Sender': {}",
1141+
response.status()
1142+
);
1143+
}
1144+
},
1145+
Err(e) => {
1146+
log_error!(
1147+
self.logger,
1148+
"Failed to send a response to 'Payjoin Sender': {}",
1149+
e
1150+
);
1151+
},
1152+
};
1153+
}
1154+
}
1155+
},
11001156
LdkEvent::ChannelPending {
11011157
channel_id,
11021158
user_channel_id,

src/lib.rs

+8
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ pub mod io;
8989
mod liquidity;
9090
mod logger;
9191
mod message_handler;
92+
mod payjoin_channel_scheduler;
9293
mod payjoin_receiver;
9394
pub mod payment;
9495
mod peer_store;
@@ -726,6 +727,7 @@ impl Node {
726727
Arc::clone(&self.output_sweeper),
727728
Arc::clone(&self.network_graph),
728729
Arc::clone(&self.payment_store),
730+
self.payjoin_receiver.clone(),
729731
Arc::clone(&self.peer_store),
730732
Arc::clone(&self.runtime),
731733
Arc::clone(&self.logger),
@@ -1114,6 +1116,9 @@ impl Node {
11141116
Arc::clone(&self.logger),
11151117
Arc::clone(&self.wallet),
11161118
Arc::clone(&self.tx_broadcaster),
1119+
Arc::clone(&self.peer_store),
1120+
Arc::clone(&self.channel_manager),
1121+
Arc::clone(&self.connection_manager),
11171122
)
11181123
}
11191124

@@ -1136,6 +1141,9 @@ impl Node {
11361141
Arc::clone(&self.logger),
11371142
Arc::clone(&self.wallet),
11381143
Arc::clone(&self.tx_broadcaster),
1144+
Arc::clone(&self.peer_store),
1145+
Arc::clone(&self.channel_manager),
1146+
Arc::clone(&self.connection_manager),
11391147
))
11401148
}
11411149

0 commit comments

Comments
 (0)