Skip to content

Commit 7de34ce

Browse files
committed
Allow for trusted inbound 0conf channels
1 parent 80d62d4 commit 7de34ce

File tree

5 files changed

+167
-7
lines changed

5 files changed

+167
-7
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
SocketAddr? 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
@@ -252,7 +252,7 @@ where
252252
payment_store: Arc<PaymentStore<K, L>>,
253253
runtime: Arc<RwLock<Option<tokio::runtime::Runtime>>>,
254254
logger: L,
255-
_config: Arc<Config>,
255+
config: Arc<Config>,
256256
}
257257

258258
impl<K: Deref + Clone, L: Deref> EventHandler<K, L>
@@ -264,7 +264,7 @@ where
264264
wallet: Arc<Wallet<bdk::database::SqliteDatabase>>, event_queue: Arc<EventQueue<K, L>>,
265265
channel_manager: Arc<ChannelManager>, network_graph: Arc<NetworkGraph>,
266266
keys_manager: Arc<KeysManager>, payment_store: Arc<PaymentStore<K, L>>,
267-
runtime: Arc<RwLock<Option<tokio::runtime::Runtime>>>, logger: L, _config: Arc<Config>,
267+
runtime: Arc<RwLock<Option<tokio::runtime::Runtime>>>, logger: L, config: Arc<Config>,
268268
) -> Self {
269269
Self {
270270
event_queue,
@@ -275,7 +275,7 @@ where
275275
payment_store,
276276
logger,
277277
runtime,
278-
_config,
278+
config,
279279
}
280280
}
281281

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

src/lib.rs

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

192198
impl Default for Config {
@@ -197,6 +203,7 @@ impl Default for Config {
197203
network: Network::Regtest,
198204
listening_address: Some("0.0.0.0:9735".parse().unwrap()),
199205
default_cltv_expiry_delta: 144,
206+
peers_trusted_0conf: Vec::new(),
200207
}
201208
}
202209
}
@@ -442,6 +449,11 @@ impl Builder {
442449
// Initialize the ChannelManager
443450
let mut user_config = UserConfig::default();
444451
user_config.channel_handshake_limits.force_announced_channel_preference = false;
452+
if !config.peers_trusted_0conf.is_empty() {
453+
// Manually accept inbound channels if we expect 0conf channel requests, avoid
454+
// generating the events otherwise.
455+
user_config.manually_accept_inbound_channels = true;
456+
}
445457
let channel_manager = {
446458
if let Ok(mut reader) = kv_store
447459
.read(CHANNEL_MANAGER_PERSISTENCE_NAMESPACE, CHANNEL_MANAGER_PERSISTENCE_KEY)

src/peer_store.rs

Lines changed: 4 additions & 2 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.pubkey) {
40+
return Ok(());
41+
}
42+
3943
locked_peers.insert(peer_info.pubkey, peer_info);
4044
self.write_peers_and_commit(&*locked_peers)
4145
}
@@ -166,7 +170,6 @@ impl Readable for PeerInfo {
166170
};
167171

168172
let port: u16 = Readable::read(reader)?;
169-
170173
let address = SocketAddr::new(ip_addr, port);
171174

172175
Ok(PeerInfo { pubkey, address })
@@ -189,7 +192,6 @@ impl Writeable for PeerInfo {
189192
}
190193

191194
self.address.port().write(writer)?;
192-
193195
Ok(())
194196
}
195197
}

src/test/functional_tests.rs

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ fn channel_open_fails_when_funds_insufficient() {
244244
node_b.listening_address().unwrap(),
245245
120000,
246246
None,
247-
true
247+
true,
248248
)
249249
);
250250
}
@@ -362,3 +362,93 @@ fn onchain_spend_receive() {
362362
assert!(node_b.onchain_balance().unwrap().get_spendable() > 99000);
363363
assert!(node_b.onchain_balance().unwrap().get_spendable() < 100000);
364364
}
365+
366+
#[test]
367+
fn channel_full_cycle_0conf() {
368+
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
369+
println!("== Node A ==");
370+
let esplora_url = electrsd.esplora_url.as_ref().unwrap();
371+
let config_a = random_config(esplora_url);
372+
let node_a = Builder::from_config(config_a).build();
373+
node_a.start().unwrap();
374+
let addr_a = node_a.new_funding_address().unwrap();
375+
376+
println!("\n== Node B ==");
377+
let mut config_b = random_config(esplora_url);
378+
config_b.peers_trusted_0conf.push(node_a.node_id());
379+
380+
let node_b = Builder::from_config(config_b).build();
381+
node_b.start().unwrap();
382+
let addr_b = node_b.new_funding_address().unwrap();
383+
384+
let premine_amount_sat = 100_000;
385+
386+
premine_and_distribute_funds(
387+
&bitcoind,
388+
&electrsd,
389+
vec![addr_a, addr_b],
390+
Amount::from_sat(premine_amount_sat),
391+
);
392+
node_a.sync_wallets().unwrap();
393+
node_b.sync_wallets().unwrap();
394+
assert_eq!(node_a.onchain_balance().unwrap().get_spendable(), premine_amount_sat);
395+
assert_eq!(node_b.onchain_balance().unwrap().get_spendable(), premine_amount_sat);
396+
397+
println!("\nA -- connect_open_channel -> B");
398+
let funding_amount_sat = 80_000;
399+
let push_msat = (funding_amount_sat / 2) * 1000; // balance the channel
400+
node_a
401+
.connect_open_channel(
402+
node_b.node_id(),
403+
node_b.listening_address().unwrap(),
404+
funding_amount_sat,
405+
Some(push_msat),
406+
true,
407+
)
408+
.unwrap();
409+
410+
node_a.sync_wallets().unwrap();
411+
node_b.sync_wallets().unwrap();
412+
413+
expect_event!(node_a, ChannelPending);
414+
415+
let _funding_txo = match node_b.next_event() {
416+
ref e @ Event::ChannelPending { funding_txo, .. } => {
417+
println!("{} got event {:?}", std::stringify!(node_b), e);
418+
node_b.event_handled();
419+
funding_txo
420+
}
421+
ref e => {
422+
panic!("{} got unexpected event!: {:?}", std::stringify!(node_b), e);
423+
}
424+
};
425+
426+
node_a.sync_wallets().unwrap();
427+
node_b.sync_wallets().unwrap();
428+
429+
expect_event!(node_a, ChannelReady);
430+
let _channel_id = match node_b.next_event() {
431+
ref e @ Event::ChannelReady { ref channel_id, .. } => {
432+
println!("{} got event {:?}", std::stringify!(node_b), e);
433+
node_b.event_handled();
434+
channel_id.clone()
435+
}
436+
ref e => {
437+
panic!("{} got unexpected event!: {:?}", std::stringify!(node_b), e);
438+
}
439+
};
440+
441+
node_a.sync_wallets().unwrap();
442+
node_b.sync_wallets().unwrap();
443+
444+
println!("\nB receive_payment");
445+
let invoice_amount_1_msat = 1000000;
446+
let invoice = node_b.receive_payment(invoice_amount_1_msat, &"asdf", 9217).unwrap();
447+
448+
println!("\nA send_payment");
449+
let payment_hash = node_a.send_payment(&invoice).unwrap();
450+
expect_event!(node_a, PaymentSuccessful);
451+
expect_event!(node_b, PaymentReceived);
452+
assert_eq!(node_a.payment(&payment_hash).unwrap().status, PaymentStatus::Succeeded);
453+
assert_eq!(node_a.payment(&payment_hash).unwrap().direction, PaymentDirection::Outbound);
454+
}

0 commit comments

Comments
 (0)