Skip to content

Commit faa065f

Browse files
committed
Add PayjoinHandler
Add two new structs `PayjoinHandler` and `PayjoinReceiver`. The first is public and holds the later as a property and can be used in order to serve incoming payjoin transactions. `PayjoinReceiver` implemented `lightning_payjoin::Receiver` trait to allow us to receive payjoin transactions or open a channel.
1 parent 83103d4 commit faa065f

File tree

6 files changed

+174
-46
lines changed

6 files changed

+174
-46
lines changed

Cargo.toml

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,20 @@ panic = 'abort' # Abort on panic
2828
default = []
2929

3030
[dependencies]
31-
lightning = { version = "0.0.123-beta", features = ["std"] }
32-
lightning-invoice = { version = "0.31.0-beta" }
33-
lightning-net-tokio = { version = "0.0.123-beta" }
34-
lightning-persister = { version = "0.0.123-beta" }
35-
lightning-background-processor = { version = "0.0.123-beta", features = ["futures"] }
36-
lightning-rapid-gossip-sync = { version = "0.0.123-beta" }
37-
lightning-transaction-sync = { version = "0.0.123-beta", features = ["esplora-async-https", "time"] }
31+
lightning = { git = "https://github.com/jbesraa/rust-lightning",rev = "0cb3d57", features = ["std"] }
32+
lightning-invoice = { git = "https://github.com/jbesraa/rust-lightning",rev = "0cb3d57" }
33+
lightning-net-tokio = { git = "https://github.com/jbesraa/rust-lightning",rev = "0cb3d57" }
34+
lightning-persister = { git = "https://github.com/jbesraa/rust-lightning",rev = "0cb3d57" }
35+
lightning-background-processor = { git = "https://github.com/jbesraa/rust-lightning", rev = "0cb3d57", features = ["futures"] }
36+
lightning-rapid-gossip-sync = { git = "https://github.com/jbesraa/rust-lightning", rev = "0cb3d57" }
37+
lightning-transaction-sync = { git = "https://github.com/jbesraa/rust-lightning", rev = "0cb3d57", features = ["esplora-async-https", "time"] }
3838
#lightning-liquidity = { version = "0.1.0-alpha.1", features = ["std"] }
3939

40-
lightning-liquidity = { git = "https://github.com/tnull/lightning-liquidity", rev = "abf7088c0e03221c0f122e797f34802c9e99a3d4", features = ["std"] }
40+
# lightning-liquidity = {path = "../../lightning-liquidity" git = "https://github.com/jbesraa/lightning-liquidity", rev = "b6ac60d", features = ["std"] }
41+
lightning-liquidity = {path = "../../lightning-liquidity" , features = ["std"] }
42+
# lightning-liquidity = { git = "https://github.com/tnull/lightning-liquidity", rev = "abf7088c0e03221c0f122e797f34802c9e99a3d4", features = ["std"] }
4143

44+
lightning-payjoin = { path = "./lightning-payjoin" }
4245
#lightning = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main", features = ["std"] }
4346
#lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main" }
4447
#lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main" }
@@ -79,12 +82,14 @@ prost = { version = "0.11.6", default-features = false}
7982
winapi = { version = "0.3", features = ["winbase"] }
8083

8184
[dev-dependencies]
82-
lightning = { version = "0.0.123-beta", features = ["std", "_test_utils"] }
85+
lightning = { git = "https://github.com/jbesraa/rust-lightning",rev = "0cb3d57", features = ["std", "_test_utils"] }
86+
# lightning = { version = "0.0.123-beta", features = ["std", "_test_utils"] }
8387
#lightning = { git = "https://github.com/lightningdevkit/rust-lightning", branch="main", features = ["std", "_test_utils"] }
8488
electrum-client = { version = "0.15.1", default-features = true }
8589
bitcoincore-rpc = { version = "0.17.0", default-features = false }
8690
proptest = "1.0.0"
8791
regex = "1.5.6"
92+
reqwest = { version = "0.11", default-features = false, features = ["blocking"] }
8893

8994
[target.'cfg(not(no_download))'.dev-dependencies]
9095
electrsd = { version = "0.26.0", features = ["legacy", "esplora_a33e97e1", "bitcoind_25_0"] }

lightning-payjoin/src/error.rs

Lines changed: 34 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use lightning::util::errors::APIError;
2-
31
#[derive(Debug)]
42
pub enum Error {
53
Internal(InternalError),
@@ -73,39 +71,39 @@ impl std::fmt::Display for Error {
7371
}
7472
}
7573

76-
// Maybe this should live in LDK-Node instead?
77-
impl From<APIError> for Error {
78-
fn from(value: APIError) -> Self {
79-
match value {
80-
APIError::APIMisuseError { err } => {
81-
Self::Internal(InternalError::FundingTxGenerationFailed(err))
82-
},
83-
APIError::FeeRateTooHigh { err, feerate } => Self::Internal(
84-
InternalError::FundingTxGenerationFailed(format!("{} feerate: {}", err, feerate)),
85-
),
86-
APIError::InvalidRoute { err } => {
87-
Self::Internal(InternalError::FundingTxGenerationFailed(format!(
88-
"Invalid route provided: {}",
89-
err
90-
)))
91-
},
92-
APIError::ChannelUnavailable { err } => Self::Internal(
93-
InternalError::FundingTxGenerationFailed(format!("Channel unavailable: {}", err)),
94-
),
95-
APIError::MonitorUpdateInProgress => {
96-
Self::Internal(InternalError::FundingTxGenerationFailed(
97-
"Client indicated a channel monitor update is in progress but not yet complete"
98-
.to_string(),
99-
))
100-
},
101-
APIError::IncompatibleShutdownScript { script } => {
102-
Self::Internal(InternalError::FundingTxGenerationFailed(format!(
103-
"Provided a scriptpubkey format not accepted by peer: {}",
104-
script
105-
)))
106-
},
107-
}
108-
}
109-
}
74+
// // Maybe this should live in LDK-Node instead?
75+
// impl From<APIError> for Error {
76+
// fn from(value: APIError) -> Self {
77+
// match value {
78+
// APIError::APIMisuseError { err } => {
79+
// Self::Internal(InternalError::FundingTxGenerationFailed(err))
80+
// },
81+
// APIError::FeeRateTooHigh { err, feerate } => Self::Internal(
82+
// InternalError::FundingTxGenerationFailed(format!("{} feerate: {}", err, feerate)),
83+
// ),
84+
// APIError::InvalidRoute { err } => {
85+
// Self::Internal(InternalError::FundingTxGenerationFailed(format!(
86+
// "Invalid route provided: {}",
87+
// err
88+
// )))
89+
// },
90+
// APIError::ChannelUnavailable { err } => Self::Internal(
91+
// InternalError::FundingTxGenerationFailed(format!("Channel unavailable: {}", err)),
92+
// ),
93+
// APIError::MonitorUpdateInProgress => {
94+
// Self::Internal(InternalError::FundingTxGenerationFailed(
95+
// "Client indicated a channel monitor update is in progress but not yet complete"
96+
// .to_string(),
97+
// ))
98+
// },
99+
// APIError::IncompatibleShutdownScript { script } => {
100+
// Self::Internal(InternalError::FundingTxGenerationFailed(format!(
101+
// "Provided a scriptpubkey format not accepted by peer: {}",
102+
// script
103+
// )))
104+
// },
105+
// }
106+
// }
107+
// }
110108

111109
impl std::error::Error for Error {}

src/error.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,19 @@ impl From<lightning_transaction_sync::TxSyncError> for Error {
139139
Self::TxSyncFailed
140140
}
141141
}
142+
143+
impl From<Error> for lightning_payjoin::error::Error {
144+
fn from(e: Error) -> Self {
145+
match e {
146+
Error::WalletOperationFailed => lightning_payjoin::error::Error::Internal(
147+
lightning_payjoin::error::InternalError::WalletOperationFailed,
148+
),
149+
Error::OnchainTxSigningFailed => lightning_payjoin::error::Error::Internal(
150+
lightning_payjoin::error::InternalError::OnchainTxSigningFailed,
151+
),
152+
_ => lightning_payjoin::error::Error::Internal(
153+
lightning_payjoin::error::InternalError::NodeFailed,
154+
),
155+
}
156+
}
157+
}

src/event.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -706,7 +706,7 @@ where
706706
}
707707
},
708708
LdkEvent::SpendableOutputs { outputs, channel_id } => {
709-
self.output_sweeper.track_spendable_outputs(outputs, channel_id, true, None)
709+
self.output_sweeper.track_spendable_outputs(outputs, channel_id, true, None);
710710
},
711711
LdkEvent::OpenChannelRequest {
712712
temporary_channel_id,

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ use error::Error;
109109

110110
pub use event::Event;
111111
pub use types::ChannelConfig;
112+
mod payjoin_handler;
112113

113114
pub use io::utils::generate_entropy_mnemonic;
114115

src/payjoin_handler.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
use crate::types::{ChannelManager, Wallet};
2+
use bdk::SignOptions;
3+
use bitcoin::address::NetworkChecked;
4+
use bitcoin::psbt::Psbt;
5+
use bitcoin::secp256k1::PublicKey;
6+
use bitcoin::ScriptBuf;
7+
use lightning::ln::ChannelId;
8+
use lightning_payjoin::error::Error as PayjoinError;
9+
use lightning_payjoin::scheduler::{ChannelScheduler, ScheduledChannel};
10+
use lightning_payjoin::{LightningPayjoin, Receiver};
11+
use std::sync::Arc;
12+
use tokio::sync::Mutex;
13+
14+
#[derive(Clone)]
15+
pub(crate) struct PayjoinHandler {
16+
inner: Arc<Mutex<LightningPayjoin<PayjoinReceiver>>>,
17+
}
18+
19+
impl PayjoinHandler {
20+
pub(crate) fn new(
21+
wallet: Arc<Wallet>, channel_manager: Arc<ChannelManager>,
22+
scheduler: Arc<Mutex<ChannelScheduler>>, payjoin_directory: lightning_payjoin::Url,
23+
payjoin_relay: lightning_payjoin::Url,
24+
) -> Self {
25+
let payjoin_receiver = PayjoinReceiver::new(wallet, channel_manager);
26+
let lightning_payjoin =
27+
LightningPayjoin::new(payjoin_receiver, scheduler, payjoin_directory, payjoin_relay);
28+
let inner = Arc::new(Mutex::new(lightning_payjoin));
29+
Self { inner }
30+
}
31+
32+
pub(crate) async fn process_request(&self) -> Result<(), PayjoinError> {
33+
self.inner.lock().await.process_request().await
34+
}
35+
36+
pub(crate) async fn payjoin_uri(&self, amount: bitcoin::Amount) -> String {
37+
self.inner.lock().await.construct_payjoin_uri(amount)
38+
}
39+
40+
pub(crate) async fn schedule_channel(
41+
&self, amount: bitcoin::Amount, counterparty_node_id: PublicKey, channel_id: u128,
42+
) {
43+
let channel = ScheduledChannel::new(amount, counterparty_node_id, channel_id);
44+
self.inner.lock().await.schedule(channel).await
45+
}
46+
47+
pub(crate) async fn list_scheduled_channels(&self) -> Vec<ScheduledChannel> {
48+
self.inner.lock().await.list_channels().await
49+
}
50+
}
51+
52+
struct PayjoinReceiver {
53+
channel_manager: Arc<ChannelManager>,
54+
wallet: Arc<Wallet>,
55+
}
56+
57+
impl std::ops::Deref for PayjoinReceiver {
58+
type Target = dyn Receiver;
59+
60+
fn deref(&self) -> &Self::Target {
61+
self
62+
}
63+
}
64+
65+
impl PayjoinReceiver {
66+
fn new(wallet: Arc<Wallet>, channel_manager: Arc<ChannelManager>) -> Self {
67+
Self { wallet, channel_manager }
68+
}
69+
}
70+
71+
impl Receiver for PayjoinReceiver {
72+
fn get_new_address(&self) -> Result<bitcoin::Address<NetworkChecked>, PayjoinError> {
73+
Ok(self.wallet.get_new_address()?)
74+
}
75+
76+
fn check_inputs_not_owned(&self, script: &ScriptBuf) -> Result<bool, PayjoinError> {
77+
Ok(self.wallet.is_mine(script)?)
78+
}
79+
80+
fn identify_my_outputs(&self, script: &ScriptBuf) -> Result<bool, PayjoinError> {
81+
Ok(self.wallet.is_mine(script)?)
82+
}
83+
84+
fn funding_transaction_generated(
85+
&self, temporary_channel_id: [u8; 32], counterparty_node_id: PublicKey,
86+
funding_tx: bitcoin::Transaction,
87+
) -> Result<(), PayjoinError> {
88+
Ok(self
89+
.channel_manager
90+
.funding_transaction_generated(
91+
&ChannelId::from_bytes(temporary_channel_id),
92+
&counterparty_node_id,
93+
funding_tx,
94+
)
95+
.unwrap())
96+
}
97+
98+
fn sign_tx(&self, psbt: &Psbt) -> Result<Psbt, PayjoinError> {
99+
let mut sign_options = SignOptions::default();
100+
sign_options.try_finalize = false;
101+
Ok(self.wallet.sign_tx(psbt, Some(sign_options))?)
102+
}
103+
104+
fn can_broadcast(&self, _transaction: &bitcoin::Transaction) -> Result<bool, PayjoinError> {
105+
// unimplemented!()
106+
Ok(true)
107+
}
108+
}

0 commit comments

Comments
 (0)