-
Notifications
You must be signed in to change notification settings - Fork 405
Plumb Features through into Routes #433
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d2ba7ca
a19d71d
912f877
617a680
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -275,6 +275,12 @@ pub(super) struct ChannelHolder<ChanSigner: ChannelKeys> { | |
pub(super) pending_msg_events: Vec<events::MessageSendEvent>, | ||
} | ||
|
||
/// State we hold per-peer. In the future we should put channels in here, but for now we only hold | ||
/// the latest Init features we heard from the peer. | ||
struct PeerState { | ||
latest_features: InitFeatures, | ||
} | ||
|
||
#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))] | ||
const ERR: () = "You need at least 32 bit pointers (well, usize, but we'll assume they're the same) for ChannelManager::latest_block_height"; | ||
|
||
|
@@ -328,6 +334,14 @@ pub struct ChannelManager<ChanSigner: ChannelKeys> { | |
channel_state: Mutex<ChannelHolder<ChanSigner>>, | ||
our_network_key: SecretKey, | ||
|
||
/// The bulk of our storage will eventually be here (channels and message queues and the like). | ||
/// If we are connected to a peer we always at least have an entry here, even if no channels | ||
/// are currently open with that peer. | ||
/// Because adding or removing an entry is rare, we usually take an outer read lock and then | ||
/// operate on the inner value freely. Sadly, this prevents parallel operation when opening a | ||
/// new channel. | ||
Comment on lines
+340
to
+342
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure that I follow what is meant by "usually" here. Can it be removed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As in, if you add or remove a connection you'll need the outer write lock, but usually we dont so take the read lock. |
||
per_peer_state: RwLock<HashMap<PublicKey, Mutex<PeerState>>>, | ||
|
||
pending_events: Mutex<Vec<events::Event>>, | ||
/// Used when we have to take a BIG lock to make sure everything is self-consistent. | ||
/// Essentially just when we're serializing ourselves out. | ||
|
@@ -390,6 +404,10 @@ pub struct ChannelDetails { | |
pub short_channel_id: Option<u64>, | ||
/// The node_id of our counterparty | ||
pub remote_network_id: PublicKey, | ||
/// The Features the channel counterparty provided upon last connection. | ||
/// Useful for routing as it is the most up-to-date copy of the counterparty's features and | ||
/// many routing-relevant features are present in the init context. | ||
pub counterparty_features: InitFeatures, | ||
/// The value, in satoshis, of this channel as appears in the funding output | ||
pub channel_value_satoshis: u64, | ||
/// The user_id passed in to create_channel, or 0 if the channel was inbound. | ||
|
@@ -610,6 +628,8 @@ impl<ChanSigner: ChannelKeys> ChannelManager<ChanSigner> { | |
}), | ||
our_network_key: keys_manager.get_node_secret(), | ||
|
||
per_peer_state: RwLock::new(HashMap::new()), | ||
|
||
pending_events: Mutex::new(Vec::new()), | ||
total_consistency_lock: RwLock::new(()), | ||
|
||
|
@@ -660,56 +680,53 @@ impl<ChanSigner: ChannelKeys> ChannelManager<ChanSigner> { | |
Ok(()) | ||
} | ||
|
||
/// Gets the list of open channels, in random order. See ChannelDetail field documentation for | ||
/// more information. | ||
pub fn list_channels(&self) -> Vec<ChannelDetails> { | ||
let channel_state = self.channel_state.lock().unwrap(); | ||
let mut res = Vec::with_capacity(channel_state.by_id.len()); | ||
for (channel_id, channel) in channel_state.by_id.iter() { | ||
let (inbound_capacity_msat, outbound_capacity_msat) = channel.get_inbound_outbound_available_balance_msat(); | ||
res.push(ChannelDetails { | ||
channel_id: (*channel_id).clone(), | ||
short_channel_id: channel.get_short_channel_id(), | ||
remote_network_id: channel.get_their_node_id(), | ||
channel_value_satoshis: channel.get_value_satoshis(), | ||
inbound_capacity_msat, | ||
outbound_capacity_msat, | ||
user_id: channel.get_user_id(), | ||
is_live: channel.is_live(), | ||
}); | ||
} | ||
res | ||
} | ||
|
||
/// Gets the list of usable channels, in random order. Useful as an argument to | ||
/// Router::get_route to ensure non-announced channels are used. | ||
/// | ||
/// These are guaranteed to have their is_live value set to true, see the documentation for | ||
/// ChannelDetails::is_live for more info on exactly what the criteria are. | ||
pub fn list_usable_channels(&self) -> Vec<ChannelDetails> { | ||
let channel_state = self.channel_state.lock().unwrap(); | ||
let mut res = Vec::with_capacity(channel_state.by_id.len()); | ||
for (channel_id, channel) in channel_state.by_id.iter() { | ||
// Note we use is_live here instead of usable which leads to somewhat confused | ||
// internal/external nomenclature, but that's ok cause that's probably what the user | ||
// really wanted anyway. | ||
if channel.is_live() { | ||
fn list_channels_with_filter<F: FnMut(&(&[u8; 32], &Channel<ChanSigner>)) -> bool>(&self, f: F) -> Vec<ChannelDetails> { | ||
let mut res = Vec::new(); | ||
{ | ||
let channel_state = self.channel_state.lock().unwrap(); | ||
res.reserve(channel_state.by_id.len()); | ||
for (channel_id, channel) in channel_state.by_id.iter().filter(f) { | ||
let (inbound_capacity_msat, outbound_capacity_msat) = channel.get_inbound_outbound_available_balance_msat(); | ||
res.push(ChannelDetails { | ||
channel_id: (*channel_id).clone(), | ||
short_channel_id: channel.get_short_channel_id(), | ||
remote_network_id: channel.get_their_node_id(), | ||
counterparty_features: InitFeatures::empty(), | ||
channel_value_satoshis: channel.get_value_satoshis(), | ||
inbound_capacity_msat, | ||
outbound_capacity_msat, | ||
user_id: channel.get_user_id(), | ||
is_live: true, | ||
is_live: channel.is_live(), | ||
}); | ||
} | ||
} | ||
let per_peer_state = self.per_peer_state.read().unwrap(); | ||
for chan in res.iter_mut() { | ||
if let Some(peer_state) = per_peer_state.get(&chan.remote_network_id) { | ||
chan.counterparty_features = peer_state.lock().unwrap().latest_features.clone(); | ||
} | ||
} | ||
res | ||
} | ||
|
||
/// Gets the list of open channels, in random order. See ChannelDetail field documentation for | ||
/// more information. | ||
pub fn list_channels(&self) -> Vec<ChannelDetails> { | ||
self.list_channels_with_filter(|_| true) | ||
} | ||
|
||
/// Gets the list of usable channels, in random order. Useful as an argument to | ||
/// Router::get_route to ensure non-announced channels are used. | ||
/// | ||
/// These are guaranteed to have their is_live value set to true, see the documentation for | ||
/// ChannelDetails::is_live for more info on exactly what the criteria are. | ||
pub fn list_usable_channels(&self) -> Vec<ChannelDetails> { | ||
// Note we use is_live here instead of usable which leads to somewhat confused | ||
// internal/external nomenclature, but that's ok cause that's probably what the user | ||
// really wanted anyway. | ||
self.list_channels_with_filter(|&(_, ref channel)| channel.is_live()) | ||
} | ||
|
||
/// Begins the process of closing a channel. After this call (plus some timeout), no new HTLCs | ||
/// will be accepted on the given channel, and after additional timeout/the closing of all | ||
/// pending HTLCs, the channel will be closed on chain. | ||
|
@@ -2780,6 +2797,7 @@ impl<ChanSigner: ChannelKeys> ChannelMessageHandler for ChannelManager<ChanSigne | |
let _ = self.total_consistency_lock.read().unwrap(); | ||
let mut failed_channels = Vec::new(); | ||
let mut failed_payments = Vec::new(); | ||
let mut no_channels_remain = true; | ||
{ | ||
let mut channel_state_lock = self.channel_state.lock().unwrap(); | ||
let channel_state = &mut *channel_state_lock; | ||
|
@@ -2818,6 +2836,8 @@ impl<ChanSigner: ChannelKeys> ChannelMessageHandler for ChannelManager<ChanSigne | |
short_to_id.remove(&short_id); | ||
} | ||
return false; | ||
} else { | ||
no_channels_remain = false; | ||
} | ||
} | ||
true | ||
|
@@ -2843,6 +2863,10 @@ impl<ChanSigner: ChannelKeys> ChannelMessageHandler for ChannelManager<ChanSigne | |
} | ||
}); | ||
} | ||
if no_channels_remain { | ||
self.per_peer_state.write().unwrap().remove(their_node_id); | ||
} | ||
|
||
for failure in failed_channels.drain(..) { | ||
self.finish_force_close_channel(failure); | ||
} | ||
|
@@ -2853,10 +2877,25 @@ impl<ChanSigner: ChannelKeys> ChannelMessageHandler for ChannelManager<ChanSigne | |
} | ||
} | ||
|
||
fn peer_connected(&self, their_node_id: &PublicKey) { | ||
fn peer_connected(&self, their_node_id: &PublicKey, init_msg: &msgs::Init) { | ||
log_debug!(self, "Generating channel_reestablish events for {}", log_pubkey!(their_node_id)); | ||
|
||
let _ = self.total_consistency_lock.read().unwrap(); | ||
|
||
{ | ||
let mut peer_state_lock = self.per_peer_state.write().unwrap(); | ||
match peer_state_lock.entry(their_node_id.clone()) { | ||
hash_map::Entry::Vacant(e) => { | ||
e.insert(Mutex::new(PeerState { | ||
latest_features: init_msg.features.clone(), | ||
})); | ||
}, | ||
hash_map::Entry::Occupied(e) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there any risk of peer collision ? An attacker can usurp pubkey available on the net but without privkey it won't get through the noise phase..and if a peer leaks its privkey we can't guarantee consistency of our structs (and we may have bigger troubles) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right. If you can impersonate a peer, that peer is screwed. Not a lot we can do then. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When would this occur? Won't There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you're using our PeerManager, yes, this should not be possible, though in theory these functions are public and the user may run their own PeerManager-like thing that may not properly check. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What sort of semantics do we want to provide to users in this case and where do we plan on documenting it? I can see three possibilities:
For (1), my assumption is (that if used properly) there should already have been a Currently (2) and (3) are equivalent but may not be in the future. You're implementing this as (3) though the code would be simplified if (2) is the desired behavior (i.e., you could blindly insert into the map instead of matching on the entry). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good question. I think in general if you don't follow the API, there are no guarantees period. It maybe could be further documented that you must somehow verify that a given message came from something that has proven knowledge of the relevant private key, but if a counterparty's private key leaks, we won't panic, it just may result in the counterparty losing their funds/channels. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Hmmm not sure I would change, it's state relative to the connection it shouldn't leak to the ChannelManager layer, if it's concerning channel+routing, assuming LN messages are ordered newer ones should take precedence.
Transport is assumed to be authenticated and encrypted so verification than message came from something which owns the relevant private key is done I think. That's said identity of the public key responder, how do you establish this knowledge is beyond specs. Keys leakage means your funds are loss, just send a HTLC emptying your balance to some exit node, it doesn't enter in the scope of option_upfront_shutdown_script. |
||
e.get().lock().unwrap().latest_features = init_msg.features.clone(); | ||
}, | ||
} | ||
} | ||
|
||
let mut channel_state_lock = self.channel_state.lock().unwrap(); | ||
let channel_state = &mut *channel_state_lock; | ||
let pending_msg_events = &mut channel_state.pending_msg_events; | ||
|
@@ -3123,6 +3162,14 @@ impl<ChanSigner: ChannelKeys + Writeable> Writeable for ChannelManager<ChanSigne | |
} | ||
} | ||
|
||
let per_peer_state = self.per_peer_state.write().unwrap(); | ||
(per_peer_state.len() as u64).write(writer)?; | ||
for (peer_pubkey, peer_state_mutex) in per_peer_state.iter() { | ||
peer_pubkey.write(writer)?; | ||
let peer_state = peer_state_mutex.lock().unwrap(); | ||
peer_state.latest_features.write(writer)?; | ||
} | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
@@ -3256,6 +3303,16 @@ impl<'a, R : ::std::io::Read, ChanSigner: ChannelKeys + Readable<R>> ReadableArg | |
claimable_htlcs.insert(payment_hash, previous_hops); | ||
} | ||
|
||
let peer_count: u64 = Readable::read(reader)?; | ||
let mut per_peer_state = HashMap::with_capacity(cmp::min(peer_count as usize, 128)); | ||
for _ in 0..peer_count { | ||
let peer_pubkey = Readable::read(reader)?; | ||
let peer_state = PeerState { | ||
latest_features: Readable::read(reader)?, | ||
}; | ||
per_peer_state.insert(peer_pubkey, Mutex::new(peer_state)); | ||
} | ||
|
||
let channel_manager = ChannelManager { | ||
genesis_hash, | ||
fee_estimator: args.fee_estimator, | ||
|
@@ -3275,6 +3332,8 @@ impl<'a, R : ::std::io::Read, ChanSigner: ChannelKeys + Readable<R>> ReadableArg | |
}), | ||
our_network_key: args.keys_manager.get_node_secret(), | ||
|
||
per_peer_state: RwLock::new(per_peer_state), | ||
|
||
pending_events: Mutex::new(Vec::new()), | ||
total_consistency_lock: RwLock::new(()), | ||
keys_manager: args.keys_manager, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
High-level question: Given this is duplicated in
PeerManager
'sPeer
structs, has there been any though around designing the API in terms ofPeer
andChannel
abstractions?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what you mean - you mean something like replacing the their_node_id argument with some struct that holds more information (eg the init message)? We could, I suppose, though note that latest_features here is intended to be available even while disconnected (though maybe it doesn't actually need to be, just nice to have).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm mostly thinking whether it would be suitable to design the overall API (not just this module) in terms of
Peer
andChannel
abstractions and use them throughout to build higher-level abstractions likeChannelManager
. Admittedly, this is outside the scope of this PR.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. Maybe. It would certainly be tricky because a Peer "owns" a channel, but also the Peer can disconnect and reconnect and get back the same Channel. We can explore more in the future.