Skip to content

Commit fefdaf6

Browse files
committed
f Add PayjoinScheduler and PayjoinExecuter
1 parent 71a3e32 commit fefdaf6

File tree

11 files changed

+531
-6
lines changed

11 files changed

+531
-6
lines changed

Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ lightning-liquidity = { version = "0.1.0-alpha.1", features = ["std"] }
5757

5858
bdk = { version = "0.29.0", default-features = false, features = ["std", "async-interface", "use-esplora-async", "sqlite-bundled", "keys-bip39"]}
5959

60-
reqwest = { version = "0.11", default-features = false, features = ["json", "rustls-tls"] }
60+
reqwest = { version = "0.11", default-features = false, features = ["json", "rustls-tls", "blocking"] }
6161
rusqlite = { version = "0.28.0", features = ["bundled"] }
6262
bitcoin = "0.30.2"
6363
bip39 = "2.0.0"
@@ -69,6 +69,11 @@ tokio = { version = "1", default-features = false, features = [ "rt-multi-thread
6969
esplora-client = { version = "0.6", default-features = false }
7070
libc = "0.2"
7171
uniffi = { version = "0.26.0", features = ["build"], optional = true }
72+
payjoin = { version = "0.13.0", features = ["receive", "send"] }
73+
http-body-util = "0.1.0"
74+
hyper = {version = "1.2.0", features = ["http1", "server"]}
75+
bytes = "1.5.0"
76+
hyper-util = {version = "0.1.3", features = ["tokio"] }
7277

7378
[target.'cfg(vss)'.dependencies]
7479
vss-client = "0.2"

bindings/kotlin/ldk-node-jvm/lib/src/test/kotlin/org/lightningdevkit/ldknode/LibraryTest.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ class LibraryTest {
119119
config1.listeningAddresses = listOf(listenAddress1)
120120
config1.network = Network.REGTEST
121121
config1.logLevel = LogLevel.TRACE
122+
config1.payjoin_server_port = 1345
122123

123124
println("Config 1: $config1")
124125

@@ -127,6 +128,7 @@ class LibraryTest {
127128
config2.listeningAddresses = listOf(listenAddress2)
128129
config2.network = Network.REGTEST
129130
config2.logLevel = LogLevel.TRACE
131+
config2.payjoin_server_port = 1346
130132
println("Config 2: $config2")
131133

132134
val builder1 = Builder.fromConfig(config1)

bindings/ldk_node.udl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ dictionary Config {
1515
sequence<PublicKey> trusted_peers_0conf;
1616
u64 probing_liquidity_limit_multiplier;
1717
LogLevel log_level;
18+
u16 payjoin_server_port;
1819
};
1920

2021
interface Builder {

src/builder.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ use crate::logger::{log_error, FilesystemLogger, Logger};
1212
use crate::message_handler::NodeCustomMessageHandler;
1313
use crate::payment_store::PaymentStore;
1414
use crate::peer_store::PeerStore;
15+
use crate::pjoin::LDKPayjoinExecuter;
1516
use crate::sweep::OutputSweeper;
1617
use crate::tx_broadcaster::TransactionBroadcaster;
1718
use crate::types::{
1819
ChainMonitor, ChannelManager, FakeMessageRouter, GossipSync, KeysManager, NetworkGraph,
1920
OnionMessenger, PeerManager,
2021
};
2122
use crate::wallet::Wallet;
23+
use crate::LDKPayjoin;
2224
use crate::{LogLevel, Node};
2325

2426
use lightning::chain::{chainmonitor, BestBlock, Watch};
@@ -944,6 +946,13 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
944946
};
945947

946948
let (stop_sender, _) = tokio::sync::watch::channel(());
949+
let payjoin_executer = LDKPayjoinExecuter::new(
950+
Arc::clone(&wallet),
951+
Arc::clone(&logger),
952+
Arc::clone(&peer_manager),
953+
Arc::clone(&channel_manager),
954+
);
955+
let payjoin = Arc::new(LDKPayjoin::new(payjoin_executer));
947956

948957
Ok(Node {
949958
runtime,
@@ -957,6 +966,7 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
957966
channel_manager,
958967
chain_monitor,
959968
output_sweeper,
969+
payjoin,
960970
peer_manager,
961971
keys_manager,
962972
network_graph,

src/config.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ pub(crate) const WALLET_SYNC_INTERVAL_MINIMUM_SECS: u64 = 10;
4444
// The length in bytes of our wallets' keys seed.
4545
pub(crate) const WALLET_KEYS_SEED_LEN: usize = 64;
4646

47+
// Port used by the Payjoin HTTP server.
48+
pub(crate) const DEFAULT_PAYJOIN_HTTP_SERVER_PORT: u16 = 3227;
49+
4750
#[derive(Debug, Clone)]
4851
/// Represents the configuration of an [`Node`] instance.
4952
///
@@ -104,6 +107,8 @@ pub struct Config {
104107
///
105108
/// Any messages below this level will be excluded from the logs.
106109
pub log_level: LogLevel,
110+
/// Payjoin server port
111+
pub payjoin_server_port: u16,
107112
}
108113

109114
impl Default for Config {
@@ -120,6 +125,7 @@ impl Default for Config {
120125
trusted_peers_0conf: Vec::new(),
121126
probing_liquidity_limit_multiplier: DEFAULT_PROBING_LIQUIDITY_LIMIT_MULTIPLIER,
122127
log_level: DEFAULT_LOG_LEVEL,
128+
payjoin_server_port: DEFAULT_PAYJOIN_HTTP_SERVER_PORT,
123129
}
124130
}
125131
}

src/lib.rs

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,14 +89,16 @@ mod logger;
8989
mod message_handler;
9090
mod payment_store;
9191
mod peer_store;
92+
mod pj_new_crate;
9293
mod sweep;
9394
mod tx_broadcaster;
9495
mod types;
9596
#[cfg(feature = "uniffi")]
9697
mod uniffi_types;
9798
mod wallet;
9899

99-
pub use bip39;
100+
use crate::pj_new_crate::ScheduledChannel;
101+
use crate::pjoin::LDKPayjoin;
100102
pub use bitcoin;
101103
pub use lightning;
102104
pub use lightning_invoice;
@@ -107,6 +109,8 @@ pub use error::Error as NodeError;
107109
use error::Error;
108110

109111
pub use event::Event;
112+
use payjoin::Uri;
113+
mod pjoin;
110114
pub use types::ChannelConfig;
111115

112116
pub use io::utils::generate_entropy_mnemonic;
@@ -157,18 +161,18 @@ use lightning_transaction_sync::EsploraSyncClient;
157161
use lightning::routing::router::{PaymentParameters, RouteParameters};
158162
use lightning_invoice::{payment, Bolt11Invoice, Currency};
159163

160-
use bitcoin::hashes::sha256::Hash as Sha256;
161164
use bitcoin::hashes::Hash;
162165
use bitcoin::secp256k1::PublicKey;
166+
use bitcoin::{hashes::sha256::Hash as Sha256, Amount};
163167

164168
use bitcoin::{Address, Txid};
165169

166170
use rand::Rng;
167171

168-
use std::default::Default;
169-
use std::net::ToSocketAddrs;
172+
use std::net::{SocketAddr, ToSocketAddrs};
170173
use std::sync::{Arc, Mutex, RwLock};
171174
use std::time::{Duration, Instant, SystemTime};
175+
use std::{default::Default, str::FromStr};
172176

173177
#[cfg(feature = "uniffi")]
174178
uniffi::include_scaffolding!("ldk_node");
@@ -188,6 +192,7 @@ pub struct Node<K: KVStore + Sync + Send + 'static> {
188192
channel_manager: Arc<ChannelManager<K>>,
189193
chain_monitor: Arc<ChainMonitor<K>>,
190194
output_sweeper: Arc<Sweeper<K>>,
195+
payjoin: Arc<LDKPayjoin<K>>,
191196
peer_manager: Arc<PeerManager<K>>,
192197
keys_manager: Arc<KeysManager>,
193198
network_graph: Arc<NetworkGraph>,
@@ -461,7 +466,30 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
461466
});
462467
}
463468

464-
// Regularly reconnect to persisted peers.
469+
let payjoin_handler = Arc::clone(&self.payjoin);
470+
let mut stop_payjoin_server = self.stop_sender.subscribe();
471+
let pj_port = self.config.payjoin_server_port;
472+
runtime.spawn(async move {
473+
let addr = SocketAddr::from(([127, 0, 0, 1], pj_port));
474+
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
475+
loop {
476+
let (stream, _) = match listener.accept().await {
477+
Ok(res) => res,
478+
Err(e) => {
479+
println!("Failed to accept incoming payjoin connection: {}", e);
480+
continue;
481+
},
482+
};
483+
tokio::select! {
484+
_ = stop_payjoin_server.changed() => {
485+
return;
486+
}
487+
_ = payjoin_handler.serve(stream) => {}
488+
}
489+
}
490+
});
491+
492+
// Regularly reconnect to channel peers.
465493
let connect_pm = Arc::clone(&self.peer_manager);
466494
let connect_logger = Arc::clone(&self.logger);
467495
let connect_peer_store = Arc::clone(&self.peer_store);
@@ -667,6 +695,33 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
667695
self.runtime.read().unwrap().is_some()
668696
}
669697

698+
/// Request a new channel to be opened with a remote peer.
699+
pub async fn schedule_payjoin_channel(
700+
&self, channel_amount_sats: u64, push_msat: Option<u64>, announce_channel: bool,
701+
node_id: PublicKey,
702+
) -> Result<String, Error> {
703+
let channel =
704+
ScheduledChannel::new(channel_amount_sats, push_msat, announce_channel, node_id);
705+
self.payjoin.schedule(channel).await;
706+
let bip21 = self.payjoin_bip21(channel_amount_sats);
707+
bip21
708+
}
709+
710+
/// Generate a BIP21 URI for a payjoin request.
711+
pub fn payjoin_bip21(&self, amount_sats: u64) -> Result<String, Error> {
712+
let address = self.wallet.get_new_address()?;
713+
let amount = Amount::from_sat(amount_sats);
714+
let pj = format!("https://0.0.0.0:{}/payjoin", self.config.payjoin_server_port);
715+
let pj_uri_string = format!("{}?amount={}&pj={}", address.to_qr_uri(), amount.to_btc(), pj);
716+
assert!(Uri::from_str(&pj_uri_string).is_ok());
717+
Ok(pj_uri_string)
718+
}
719+
720+
/// List all scheduled payjoin channels.
721+
pub async fn list_scheduled_channels(&self) -> Result<Vec<ScheduledChannel>, Error> {
722+
Ok(self.payjoin.list_scheduled_channels().await)
723+
}
724+
670725
/// Disconnects all peers, stops all running background tasks, and shuts down [`Node`].
671726
///
672727
/// After this returns most API methods will return [`Error::NotRunning`].

0 commit comments

Comments
 (0)