Skip to content

Commit

Permalink
Merge pull request #69 from tnull/2023-04-allow-manual-0conf
Browse files Browse the repository at this point in the history
Allow for trusted inbound 0conf channels
  • Loading branch information
tnull authored Jun 13, 2023
2 parents 97d238c + 4c26c25 commit e93f564
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 9 deletions.
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 @@ -573,6 +581,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

0 comments on commit e93f564

Please sign in to comment.