Skip to content

Commit

Permalink
Allow for trusted inbound 0conf channels
Browse files Browse the repository at this point in the history
  • Loading branch information
tnull committed May 10, 2023
1 parent 80d62d4 commit 4d90ae4
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 7 deletions.
1 change: 1 addition & 0 deletions bindings/ldk_node.udl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ dictionary Config {
Network network;
SocketAddr? listening_address;
u32 default_cltv_expiry_delta;
sequence<PublicKey> peers_trusted_0conf;
};

interface Builder {
Expand Down
63 changes: 59 additions & 4 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,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: Deref + Clone, L: Deref> EventHandler<K, L>
Expand All @@ -264,7 +264,7 @@ where
wallet: Arc<Wallet<bdk::database::SqliteDatabase>>, event_queue: Arc<EventQueue<K, L>>,
channel_manager: Arc<ChannelManager>, 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 @@ -275,7 +275,7 @@ where
payment_store,
logger,
runtime,
_config,
config,
}
}

Expand Down Expand Up @@ -566,7 +566,62 @@ 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>();
if self.config.peers_trusted_0conf.contains(&counterparty_node_id) {
match self.channel_manager.accept_inbound_channel_from_trusted_peer_0conf(
&temporary_channel_id,
&counterparty_node_id,
user_channel_id,
) {
Ok(()) => {
log_info!(
self.logger,
"Accepting inbound channel of {}sats from trusted peer {}",
funding_satoshis,
counterparty_node_id,
);
}
Err(e) => {
log_error!(
self.logger,
"Error while accepting inbound channel from trusted peer {}: {:?}",
counterparty_node_id,
e,
);
}
}
} else {
match self.channel_manager.accept_inbound_channel(
&temporary_channel_id,
&counterparty_node_id,
user_channel_id,
) {
Ok(()) => {
log_info!(
self.logger,
"Accepting inbound channel of {}sats from peer {}",
funding_satoshis,
counterparty_node_id,
);
}
Err(e) => {
log_error!(
self.logger,
"Error while accepting inbound channel from peer {}: {:?}",
counterparty_node_id,
e,
);
}
}
}
}
LdkEvent::PaymentForwarded {
prev_channel_id,
next_channel_id,
Expand Down
12 changes: 12 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,12 @@ pub struct Config {
pub listening_address: Option<SocketAddr>,
/// The default CLTV expiry delta to be used for payments.
pub default_cltv_expiry_delta: u32,
/// A list of peers which 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 peers_trusted_0conf: Vec<PublicKey>,
}

impl Default for Config {
Expand All @@ -197,6 +203,7 @@ impl Default for Config {
network: Network::Regtest,
listening_address: Some("0.0.0.0:9735".parse().unwrap()),
default_cltv_expiry_delta: 144,
peers_trusted_0conf: Vec::new(),
}
}
}
Expand Down Expand Up @@ -442,6 +449,11 @@ impl Builder {
// Initialize the ChannelManager
let mut user_config = UserConfig::default();
user_config.channel_handshake_limits.force_announced_channel_preference = false;
if !config.peers_trusted_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
6 changes: 4 additions & 2 deletions src/peer_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,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.pubkey) {
return Ok(());
}

locked_peers.insert(peer_info.pubkey, peer_info);
self.write_peers_and_commit(&*locked_peers)
}
Expand Down Expand Up @@ -166,7 +170,6 @@ impl Readable for PeerInfo {
};

let port: u16 = Readable::read(reader)?;

let address = SocketAddr::new(ip_addr, port);

Ok(PeerInfo { pubkey, address })
Expand All @@ -189,7 +192,6 @@ impl Writeable for PeerInfo {
}

self.address.port().write(writer)?;

Ok(())
}
}
Expand Down
92 changes: 91 additions & 1 deletion src/test/functional_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ fn channel_open_fails_when_funds_insufficient() {
node_b.listening_address().unwrap(),
120000,
None,
true
true,
)
);
}
Expand Down Expand Up @@ -362,3 +362,93 @@ fn onchain_spend_receive() {
assert!(node_b.onchain_balance().unwrap().get_spendable() > 99000);
assert!(node_b.onchain_balance().unwrap().get_spendable() < 100000);
}

#[test]
fn channel_full_cycle_0conf() {
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
println!("== Node A ==");
let esplora_url = electrsd.esplora_url.as_ref().unwrap();
let config_a = random_config(esplora_url);
let node_a = Builder::from_config(config_a).build();
node_a.start().unwrap();
let addr_a = node_a.new_funding_address().unwrap();

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

let node_b = Builder::from_config(config_b).build();
node_b.start().unwrap();
let addr_b = node_b.new_funding_address().unwrap();

let premine_amount_sat = 100_000;

premine_and_distribute_funds(
&bitcoind,
&electrsd,
vec![addr_a, addr_b],
Amount::from_sat(premine_amount_sat),
);
node_a.sync_wallets().unwrap();
node_b.sync_wallets().unwrap();
assert_eq!(node_a.onchain_balance().unwrap().get_spendable(), premine_amount_sat);
assert_eq!(node_b.onchain_balance().unwrap().get_spendable(), premine_amount_sat);

println!("\nA -- connect_open_channel -> B");
let funding_amount_sat = 80_000;
let push_msat = (funding_amount_sat / 2) * 1000; // balance the channel
node_a
.connect_open_channel(
node_b.node_id(),
node_b.listening_address().unwrap(),
funding_amount_sat,
Some(push_msat),
true,
)
.unwrap();

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

expect_event!(node_a, ChannelPending);

let _funding_txo = match node_b.next_event() {
ref e @ Event::ChannelPending { funding_txo, .. } => {
println!("{} got event {:?}", std::stringify!(node_b), e);
node_b.event_handled();
funding_txo
}
ref e => {
panic!("{} got unexpected event!: {:?}", std::stringify!(node_b), e);
}
};

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

expect_event!(node_a, ChannelReady);
let _channel_id = match node_b.next_event() {
ref e @ Event::ChannelReady { ref channel_id, .. } => {
println!("{} got event {:?}", std::stringify!(node_b), e);
node_b.event_handled();
channel_id.clone()
}
ref e => {
panic!("{} got unexpected event!: {:?}", std::stringify!(node_b), e);
}
};

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

println!("\nB receive_payment");
let invoice_amount_1_msat = 1000000;
let invoice = node_b.receive_payment(invoice_amount_1_msat, &"asdf", 9217).unwrap();

println!("\nA send_payment");
let payment_hash = node_a.send_payment(&invoice).unwrap();
expect_event!(node_a, PaymentSuccessful);
expect_event!(node_b, PaymentReceived);
assert_eq!(node_a.payment(&payment_hash).unwrap().status, PaymentStatus::Succeeded);
assert_eq!(node_a.payment(&payment_hash).unwrap().direction, PaymentDirection::Outbound);
}

0 comments on commit 4d90ae4

Please sign in to comment.