Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bindings/ldk_node.udl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ dictionary Config {
u64 wallet_sync_interval_secs;
u64 fee_rate_cache_update_interval_secs;
LogLevel log_level;
sequence<PublicKey> trusted_peers_0conf;
};

interface Builder {
Expand Down
53 changes: 49 additions & 4 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ where
payment_store: Arc<PaymentStore<K, L>>,
runtime: Arc<RwLock<Option<tokio::runtime::Runtime>>>,
logger: L,
_config: Arc<Config>,
config: Arc<Config>,
}

impl<K: KVStore + Sync + Send + 'static, L: Deref> EventHandler<K, L>
Expand All @@ -242,7 +242,7 @@ where
wallet: Arc<Wallet<bdk::database::SqliteDatabase>>, event_queue: Arc<EventQueue<K, L>>,
channel_manager: Arc<ChannelManager<K>>, network_graph: Arc<NetworkGraph>,
keys_manager: Arc<KeysManager>, payment_store: Arc<PaymentStore<K, L>>,
runtime: Arc<RwLock<Option<tokio::runtime::Runtime>>>, logger: L, _config: Arc<Config>,
runtime: Arc<RwLock<Option<tokio::runtime::Runtime>>>, logger: L, config: Arc<Config>,
) -> Self {
Self {
event_queue,
Expand All @@ -253,7 +253,7 @@ where
payment_store,
logger,
runtime,
_config,
config,
}
}

Expand Down Expand Up @@ -545,7 +545,52 @@ where
}
}
}
LdkEvent::OpenChannelRequest { .. } => {}
LdkEvent::OpenChannelRequest {
temporary_channel_id,
counterparty_node_id,
funding_satoshis,
channel_type: _,
push_msat: _,
} => {
let user_channel_id: u128 = rand::thread_rng().gen::<u128>();
let allow_0conf = self.config.trusted_peers_0conf.contains(&counterparty_node_id);
let res = if allow_0conf {
self.channel_manager.accept_inbound_channel_from_trusted_peer_0conf(
&temporary_channel_id,
&counterparty_node_id,
user_channel_id,
)
} else {
self.channel_manager.accept_inbound_channel(
&temporary_channel_id,
&counterparty_node_id,
user_channel_id,
)
};

match res {
Ok(()) => {
log_info!(
self.logger,
"Accepting inbound{} channel of {}sats from{} peer {}",
if allow_0conf { " 0conf" } else { "" },
funding_satoshis,
if allow_0conf { " trusted" } else { "" },
counterparty_node_id,
);
}
Err(e) => {
log_error!(
self.logger,
"Error while accepting inbound{} channel from{} peer {}: {:?}",
if allow_0conf { " 0conf" } else { "" },
counterparty_node_id,
if allow_0conf { " trusted" } else { "" },
e,
);
}
}
}
LdkEvent::PaymentForwarded {
prev_channel_id,
next_channel_id,
Expand Down
13 changes: 13 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ const WALLET_KEYS_SEED_LEN: usize = 64;
/// | `onchain_wallet_sync_interval_secs` | 60 |
/// | `wallet_sync_interval_secs` | 20 |
/// | `fee_rate_cache_update_interval_secs` | 600 |
/// | `trusted_peers_0conf` | [] |
/// | `log_level` | `Debug` |
///
pub struct Config {
Expand All @@ -247,6 +248,12 @@ pub struct Config {
///
/// **Note:** A minimum of 10 seconds is always enforced.
pub fee_rate_cache_update_interval_secs: u64,
/// A list of peers that we allow to establish zero confirmation channels to us.
///
/// **Note:** Allowing payments via zero-confirmation channels is potentially insecure if the
/// funding transaction ends up never being confirmed on-chain. Zero-confirmation channels
/// should therefore only be accepted from trusted peers.
pub trusted_peers_0conf: Vec<PublicKey>,
/// The level at which we log messages.
///
/// Any messages below this level will be excluded from the logs.
Expand All @@ -263,6 +270,7 @@ impl Default for Config {
onchain_wallet_sync_interval_secs: DEFAULT_BDK_WALLET_SYNC_INTERVAL_SECS,
wallet_sync_interval_secs: DEFAULT_LDK_WALLET_SYNC_INTERVAL_SECS,
fee_rate_cache_update_interval_secs: DEFAULT_FEE_RATE_CACHE_UPDATE_INTERVAL_SECS,
trusted_peers_0conf: Vec::new(),
log_level: DEFAULT_LOG_LEVEL,
}
}
Expand Down Expand Up @@ -569,6 +577,11 @@ impl Builder {
// Initialize the ChannelManager
let mut user_config = UserConfig::default();
user_config.channel_handshake_limits.force_announced_channel_preference = false;
if !config.trusted_peers_0conf.is_empty() {
// Manually accept inbound channels if we expect 0conf channel requests, avoid
// generating the events otherwise.
user_config.manually_accept_inbound_channels = true;
}
let channel_manager = {
if let Ok(mut reader) = kv_store
.read(CHANNEL_MANAGER_PERSISTENCE_NAMESPACE, CHANNEL_MANAGER_PERSISTENCE_KEY)
Expand Down
4 changes: 4 additions & 0 deletions src/peer_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ where
pub(crate) fn add_peer(&self, peer_info: PeerInfo) -> Result<(), Error> {
let mut locked_peers = self.peers.write().unwrap();

if locked_peers.contains_key(&peer_info.node_id) {
return Ok(());
}

locked_peers.insert(peer_info.node_id, peer_info);
self.persist_peers(&*locked_peers)
}
Expand Down
50 changes: 45 additions & 5 deletions src/test/functional_tests.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
use crate::io::KVStore;
use crate::test::utils::*;
use crate::test::utils::{expect_event, random_config};
use crate::{Builder, Error, Event, PaymentDirection, PaymentStatus};
use crate::{Builder, Error, Event, Node, PaymentDirection, PaymentStatus};

use bitcoin::Amount;
use electrsd::bitcoind::BitcoinD;
use electrsd::ElectrsD;

use std::sync::Arc;

#[test]
fn channel_full_cycle() {
Expand All @@ -14,14 +19,46 @@ fn channel_full_cycle() {
builder_a.set_esplora_server(esplora_url.clone());
let node_a = builder_a.build();
node_a.start().unwrap();
let addr_a = node_a.new_funding_address().unwrap();

println!("\n== Node B ==");
let config_b = random_config();
let builder_b = Builder::from_config(config_b);
builder_b.set_esplora_server(esplora_url);
let node_b = builder_b.build();
node_b.start().unwrap();

do_channel_full_cycle(node_a, node_b, &bitcoind, &electrsd, false);
}

#[test]
fn channel_full_cycle_0conf() {
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
println!("== Node A ==");
let esplora_url = format!("http://{}", electrsd.esplora_url.as_ref().unwrap());
let config_a = random_config();
let builder_a = Builder::from_config(config_a);
builder_a.set_esplora_server(esplora_url.clone());
let node_a = builder_a.build();
node_a.start().unwrap();

println!("\n== Node B ==");
let mut config_b = random_config();
config_b.trusted_peers_0conf.push(node_a.node_id());

let builder_b = Builder::from_config(config_b);
builder_b.set_esplora_server(esplora_url.clone());
let node_b = builder_b.build();

node_b.start().unwrap();

do_channel_full_cycle(node_a, node_b, &bitcoind, &electrsd, true)
}

fn do_channel_full_cycle<K: KVStore + Sync + Send>(
node_a: Arc<Node<K>>, node_b: Arc<Node<K>>, bitcoind: &BitcoinD, electrsd: &ElectrsD,
allow_0conf: bool,
) {
let addr_a = node_a.new_funding_address().unwrap();
let addr_b = node_b.new_funding_address().unwrap();

let premine_amount_sat = 100_000;
Expand Down Expand Up @@ -71,8 +108,11 @@ fn channel_full_cycle() {

wait_for_tx(&electrsd, funding_txo.txid);

println!("\n .. generating blocks, syncing wallets .. ");
generate_blocks_and_wait(&bitcoind, &electrsd, 6);
if !allow_0conf {
println!("\n .. generating blocks ..");
generate_blocks_and_wait(&bitcoind, &electrsd, 6);
}

node_a.sync_wallets().unwrap();
node_b.sync_wallets().unwrap();

Expand Down Expand Up @@ -271,7 +311,7 @@ fn channel_open_fails_when_funds_insufficient() {
node_b.listening_address().unwrap().into(),
120000,
None,
true
true,
)
);
}
Expand Down