Skip to content

Commit 095e82b

Browse files
committed
Allow for trusted inbound 0conf channels
1 parent 8ba0604 commit 095e82b

File tree

5 files changed

+167
-5
lines changed

5 files changed

+167
-5
lines changed

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: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ where
257257
payment_store: Arc<PaymentStore<K, L>>,
258258
runtime: Arc<RwLock<Option<tokio::runtime::Runtime>>>,
259259
logger: L,
260-
_config: Arc<Config>,
260+
config: Arc<Config>,
261261
}
262262

263263
impl<K: Deref + Clone, L: Deref> EventHandler<K, L>
@@ -269,7 +269,7 @@ where
269269
wallet: Arc<Wallet<bdk::database::SqliteDatabase>>, event_queue: Arc<EventQueue<K, L>>,
270270
channel_manager: Arc<ChannelManager>, network_graph: Arc<NetworkGraph>,
271271
keys_manager: Arc<KeysManager>, payment_store: Arc<PaymentStore<K, L>>,
272-
runtime: Arc<RwLock<Option<tokio::runtime::Runtime>>>, logger: L, _config: Arc<Config>,
272+
runtime: Arc<RwLock<Option<tokio::runtime::Runtime>>>, logger: L, config: Arc<Config>,
273273
) -> Self {
274274
Self {
275275
event_queue,
@@ -280,7 +280,7 @@ where
280280
payment_store,
281281
logger,
282282
runtime,
283-
_config,
283+
config,
284284
}
285285
}
286286

@@ -571,7 +571,62 @@ where
571571
}
572572
}
573573
}
574-
LdkEvent::OpenChannelRequest { .. } => {}
574+
LdkEvent::OpenChannelRequest {
575+
temporary_channel_id,
576+
counterparty_node_id,
577+
funding_satoshis,
578+
channel_type: _,
579+
push_msat: _,
580+
} => {
581+
let user_channel_id: u128 = rand::thread_rng().gen::<u128>();
582+
if self.config.peers_trusted_0conf.contains(&counterparty_node_id) {
583+
match self.channel_manager.accept_inbound_channel_from_trusted_peer_0conf(
584+
&temporary_channel_id,
585+
&counterparty_node_id,
586+
user_channel_id,
587+
) {
588+
Ok(()) => {
589+
log_info!(
590+
self.logger,
591+
"Accepting inbound 0conf channel of {}sats from trusted peer {}",
592+
funding_satoshis,
593+
counterparty_node_id,
594+
);
595+
}
596+
Err(e) => {
597+
log_error!(
598+
self.logger,
599+
"Error while accepting inbound 0conf channel from trusted peer {}: {:?}",
600+
counterparty_node_id,
601+
e,
602+
);
603+
}
604+
}
605+
} else {
606+
match self.channel_manager.accept_inbound_channel(
607+
&temporary_channel_id,
608+
&counterparty_node_id,
609+
user_channel_id,
610+
) {
611+
Ok(()) => {
612+
log_info!(
613+
self.logger,
614+
"Accepting inbound channel of {}sats from peer {}",
615+
funding_satoshis,
616+
counterparty_node_id,
617+
);
618+
}
619+
Err(e) => {
620+
log_error!(
621+
self.logger,
622+
"Error while accepting inbound channel from peer {}: {:?}",
623+
counterparty_node_id,
624+
e,
625+
);
626+
}
627+
}
628+
}
629+
}
575630
LdkEvent::PaymentForwarded {
576631
prev_channel_id,
577632
next_channel_id,

src/lib.rs

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

196202
impl Default for Config {
@@ -201,6 +207,7 @@ impl Default for Config {
201207
network: Network::Regtest,
202208
listening_address: Some("0.0.0.0:9735".parse().unwrap()),
203209
default_cltv_expiry_delta: 144,
210+
peers_trusted_0conf: Vec::new(),
204211
}
205212
}
206213
}
@@ -470,6 +477,11 @@ impl Builder {
470477
// Initialize the ChannelManager
471478
let mut user_config = UserConfig::default();
472479
user_config.channel_handshake_limits.force_announced_channel_preference = false;
480+
if !config.peers_trusted_0conf.is_empty() {
481+
// Manually accept inbound channels if we expect 0conf channel requests, avoid
482+
// generating the events otherwise.
483+
user_config.manually_accept_inbound_channels = true;
484+
}
473485
let channel_manager = {
474486
if let Ok(mut reader) = kv_store
475487
.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
@@ -36,6 +36,10 @@ where
3636
pub(crate) fn add_peer(&self, peer_info: PeerInfo) -> Result<(), Error> {
3737
let mut locked_peers = self.peers.write().unwrap();
3838

39+
if locked_peers.contains_key(&peer_info.node_id) {
40+
return Ok(());
41+
}
42+
3943
locked_peers.insert(peer_info.node_id, peer_info);
4044
self.write_peers_and_commit(&*locked_peers)
4145
}

src/test/functional_tests.rs

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ fn channel_open_fails_when_funds_insufficient() {
254254
node_b.listening_address().unwrap().into(),
255255
120000,
256256
None,
257-
true
257+
true,
258258
)
259259
);
260260
}
@@ -373,6 +373,96 @@ fn onchain_spend_receive() {
373373
assert!(node_b.onchain_balance().unwrap().get_spendable() < 100000);
374374
}
375375

376+
#[test]
377+
fn channel_full_cycle_0conf() {
378+
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
379+
println!("== Node A ==");
380+
let esplora_url = electrsd.esplora_url.as_ref().unwrap();
381+
let config_a = random_config(esplora_url);
382+
let node_a = Builder::from_config(config_a).build();
383+
node_a.start().unwrap();
384+
let addr_a = node_a.new_funding_address().unwrap();
385+
386+
println!("\n== Node B ==");
387+
let mut config_b = random_config(esplora_url);
388+
config_b.peers_trusted_0conf.push(node_a.node_id());
389+
390+
let node_b = Builder::from_config(config_b).build();
391+
node_b.start().unwrap();
392+
let addr_b = node_b.new_funding_address().unwrap();
393+
394+
let premine_amount_sat = 100_000;
395+
396+
premine_and_distribute_funds(
397+
&bitcoind,
398+
&electrsd,
399+
vec![addr_a, addr_b],
400+
Amount::from_sat(premine_amount_sat),
401+
);
402+
node_a.sync_wallets().unwrap();
403+
node_b.sync_wallets().unwrap();
404+
assert_eq!(node_a.onchain_balance().unwrap().get_spendable(), premine_amount_sat);
405+
assert_eq!(node_b.onchain_balance().unwrap().get_spendable(), premine_amount_sat);
406+
407+
println!("\nA -- connect_open_channel -> B");
408+
let funding_amount_sat = 80_000;
409+
let push_msat = (funding_amount_sat / 2) * 1000; // balance the channel
410+
node_a
411+
.connect_open_channel(
412+
node_b.node_id(),
413+
node_b.listening_address().unwrap(),
414+
funding_amount_sat,
415+
Some(push_msat),
416+
true,
417+
)
418+
.unwrap();
419+
420+
node_a.sync_wallets().unwrap();
421+
node_b.sync_wallets().unwrap();
422+
423+
expect_event!(node_a, ChannelPending);
424+
425+
let _funding_txo = match node_b.next_event() {
426+
ref e @ Event::ChannelPending { funding_txo, .. } => {
427+
println!("{} got event {:?}", std::stringify!(node_b), e);
428+
node_b.event_handled();
429+
funding_txo
430+
}
431+
ref e => {
432+
panic!("{} got unexpected event!: {:?}", std::stringify!(node_b), e);
433+
}
434+
};
435+
436+
node_a.sync_wallets().unwrap();
437+
node_b.sync_wallets().unwrap();
438+
439+
expect_event!(node_a, ChannelReady);
440+
let _channel_id = match node_b.next_event() {
441+
ref e @ Event::ChannelReady { ref channel_id, .. } => {
442+
println!("{} got event {:?}", std::stringify!(node_b), e);
443+
node_b.event_handled();
444+
channel_id.clone()
445+
}
446+
ref e => {
447+
panic!("{} got unexpected event!: {:?}", std::stringify!(node_b), e);
448+
}
449+
};
450+
451+
node_a.sync_wallets().unwrap();
452+
node_b.sync_wallets().unwrap();
453+
454+
println!("\nB receive_payment");
455+
let invoice_amount_1_msat = 1000000;
456+
let invoice = node_b.receive_payment(invoice_amount_1_msat, &"asdf", 9217).unwrap();
457+
458+
println!("\nA send_payment");
459+
let payment_hash = node_a.send_payment(&invoice).unwrap();
460+
expect_event!(node_a, PaymentSuccessful);
461+
expect_event!(node_b, PaymentReceived);
462+
assert_eq!(node_a.payment(&payment_hash).unwrap().status, PaymentStatus::Succeeded);
463+
assert_eq!(node_a.payment(&payment_hash).unwrap().direction, PaymentDirection::Outbound);
464+
}
465+
376466
#[test]
377467
fn sign_verify_msg() {
378468
let (_, electrsd) = setup_bitcoind_and_electrsd();

0 commit comments

Comments
 (0)