Skip to content

Commit b4fba51

Browse files
committed
Allow for trusted inbound 0conf channels
1 parent 319f4ae commit b4fba51

5 files changed

Lines changed: 106 additions & 9 deletions

File tree

bindings/ldk_node.udl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ dictionary Config {
77
Network network;
88
NetAddress? listening_address;
99
u32 default_cltv_expiry_delta;
10+
sequence<PublicKey> peers_trusted_0conf;
1011
};
1112

1213
interface Builder {

src/event.rs

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ where
231231
payment_store: Arc<PaymentStore<K, L>>,
232232
runtime: Arc<RwLock<Option<tokio::runtime::Runtime>>>,
233233
logger: L,
234-
_config: Arc<Config>,
234+
config: Arc<Config>,
235235
}
236236

237237
impl<K: KVStore + Sync + Send + 'static, L: Deref> EventHandler<K, L>
@@ -242,7 +242,7 @@ where
242242
wallet: Arc<Wallet<bdk::database::SqliteDatabase>>, event_queue: Arc<EventQueue<K, L>>,
243243
channel_manager: Arc<ChannelManager<K>>, network_graph: Arc<NetworkGraph>,
244244
keys_manager: Arc<KeysManager>, payment_store: Arc<PaymentStore<K, L>>,
245-
runtime: Arc<RwLock<Option<tokio::runtime::Runtime>>>, logger: L, _config: Arc<Config>,
245+
runtime: Arc<RwLock<Option<tokio::runtime::Runtime>>>, logger: L, config: Arc<Config>,
246246
) -> Self {
247247
Self {
248248
event_queue,
@@ -253,7 +253,7 @@ where
253253
payment_store,
254254
logger,
255255
runtime,
256-
_config,
256+
config,
257257
}
258258
}
259259

@@ -544,7 +544,52 @@ where
544544
}
545545
}
546546
}
547-
LdkEvent::OpenChannelRequest { .. } => {}
547+
LdkEvent::OpenChannelRequest {
548+
temporary_channel_id,
549+
counterparty_node_id,
550+
funding_satoshis,
551+
channel_type: _,
552+
push_msat: _,
553+
} => {
554+
let user_channel_id: u128 = rand::thread_rng().gen::<u128>();
555+
let allow_0conf = self.config.peers_trusted_0conf.contains(&counterparty_node_id);
556+
let res = if allow_0conf {
557+
self.channel_manager.accept_inbound_channel_from_trusted_peer_0conf(
558+
&temporary_channel_id,
559+
&counterparty_node_id,
560+
user_channel_id,
561+
)
562+
} else {
563+
self.channel_manager.accept_inbound_channel(
564+
&temporary_channel_id,
565+
&counterparty_node_id,
566+
user_channel_id,
567+
)
568+
};
569+
570+
match res {
571+
Ok(()) => {
572+
log_info!(
573+
self.logger,
574+
"Accepting inbound{} channel of {}sats from{} peer {}",
575+
if allow_0conf { " 0conf" } else { "" },
576+
funding_satoshis,
577+
if allow_0conf { " trusted" } else { "" },
578+
counterparty_node_id,
579+
);
580+
}
581+
Err(e) => {
582+
log_error!(
583+
self.logger,
584+
"Error while accepting inbound{} channel from{} peer {}: {:?}",
585+
if allow_0conf { " 0conf" } else { "" },
586+
counterparty_node_id,
587+
if allow_0conf { " trusted" } else { "" },
588+
e,
589+
);
590+
}
591+
}
592+
}
548593
LdkEvent::PaymentForwarded {
549594
prev_channel_id,
550595
next_channel_id,

src/lib.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,12 @@ pub struct Config {
194194
pub listening_address: Option<NetAddress>,
195195
/// The default CLTV expiry delta to be used for payments.
196196
pub default_cltv_expiry_delta: u32,
197+
/// A list of peers which we allow to establish zero confirmation channels to us.
198+
///
199+
/// **Note:** Allowing payments via zero-confirmation channels is potentially insecure if the
200+
/// funding transaction ends up never being confirmed on-chain. Zero-confirmation channels
201+
/// should therefore only be accepted from trusted peers.
202+
pub peers_trusted_0conf: Vec<PublicKey>,
197203
}
198204

199205
impl Default for Config {
@@ -204,6 +210,7 @@ impl Default for Config {
204210
network: Network::Regtest,
205211
listening_address: Some("0.0.0.0:9735".parse().unwrap()),
206212
default_cltv_expiry_delta: 144,
213+
peers_trusted_0conf: Vec::new(),
207214
}
208215
}
209216
}
@@ -481,6 +488,11 @@ impl Builder {
481488
// Initialize the ChannelManager
482489
let mut user_config = UserConfig::default();
483490
user_config.channel_handshake_limits.force_announced_channel_preference = false;
491+
if !config.peers_trusted_0conf.is_empty() {
492+
// Manually accept inbound channels if we expect 0conf channel requests, avoid
493+
// generating the events otherwise.
494+
user_config.manually_accept_inbound_channels = true;
495+
}
484496
let channel_manager = {
485497
if let Ok(mut reader) = kv_store
486498
.read(CHANNEL_MANAGER_PERSISTENCE_NAMESPACE, CHANNEL_MANAGER_PERSISTENCE_KEY)

src/peer_store.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ where
3232
pub(crate) fn add_peer(&self, peer_info: PeerInfo) -> Result<(), Error> {
3333
let mut locked_peers = self.peers.write().unwrap();
3434

35+
if locked_peers.contains_key(&peer_info.node_id) {
36+
return Ok(());
37+
}
38+
3539
locked_peers.insert(peer_info.node_id, peer_info);
3640
self.persist_peers(&*locked_peers)
3741
}

src/test/functional_tests.rs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1+
use crate::io::KVStore;
12
use crate::test::utils::*;
23
use crate::test::utils::{expect_event, random_config};
3-
use crate::{Builder, Error, Event, PaymentDirection, PaymentStatus};
4+
use crate::{Builder, Error, Event, Node, PaymentDirection, PaymentStatus};
45

56
use bitcoin::Amount;
7+
use electrsd::bitcoind::BitcoinD;
8+
use electrsd::ElectrsD;
9+
10+
use std::sync::Arc;
611

712
#[test]
813
fn channel_full_cycle() {
@@ -12,12 +17,39 @@ fn channel_full_cycle() {
1217
let config_a = random_config(esplora_url);
1318
let node_a = Builder::from_config(config_a).build();
1419
node_a.start().unwrap();
15-
let addr_a = node_a.new_funding_address().unwrap();
1620

1721
println!("\n== Node B ==");
1822
let config_b = random_config(esplora_url);
1923
let node_b = Builder::from_config(config_b).build();
2024
node_b.start().unwrap();
25+
26+
do_channel_full_cycle(node_a, node_b, &bitcoind, &electrsd, false);
27+
}
28+
29+
#[test]
30+
fn channel_full_cycle_0conf() {
31+
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
32+
println!("== Node A ==");
33+
let esplora_url = electrsd.esplora_url.as_ref().unwrap();
34+
let config_a = random_config(esplora_url);
35+
let node_a = Builder::from_config(config_a).build();
36+
node_a.start().unwrap();
37+
38+
println!("\n== Node B ==");
39+
let mut config_b = random_config(esplora_url);
40+
config_b.peers_trusted_0conf.push(node_a.node_id());
41+
42+
let node_b = Builder::from_config(config_b).build();
43+
node_b.start().unwrap();
44+
45+
do_channel_full_cycle(node_a, node_b, &bitcoind, &electrsd, true)
46+
}
47+
48+
fn do_channel_full_cycle<K: KVStore + Sync + Send>(
49+
node_a: Arc<Node<K>>, node_b: Arc<Node<K>>, bitcoind: &BitcoinD, electrsd: &ElectrsD,
50+
allow_0conf: bool,
51+
) {
52+
let addr_a = node_a.new_funding_address().unwrap();
2153
let addr_b = node_b.new_funding_address().unwrap();
2254

2355
let premine_amount_sat = 100_000;
@@ -67,8 +99,11 @@ fn channel_full_cycle() {
6799

68100
wait_for_tx(&electrsd, funding_txo.txid);
69101

70-
println!("\n .. generating blocks, syncing wallets .. ");
71-
generate_blocks_and_wait(&bitcoind, &electrsd, 6);
102+
if !allow_0conf {
103+
println!("\n .. generating blocks ..");
104+
generate_blocks_and_wait(&bitcoind, &electrsd, 6);
105+
}
106+
72107
node_a.sync_wallets().unwrap();
73108
node_b.sync_wallets().unwrap();
74109

@@ -254,7 +289,7 @@ fn channel_open_fails_when_funds_insufficient() {
254289
node_b.listening_address().unwrap().into(),
255290
120000,
256291
None,
257-
true
292+
true,
258293
)
259294
);
260295
}

0 commit comments

Comments
 (0)