Skip to content

Commit 2a93f98

Browse files
authored
Merge pull request #150 from TheBlueMatt/2018-09-bolt7-compliance
Finish up #129 BOLT 7 compliance
2 parents 7473080 + 227c1d2 commit 2a93f98

File tree

2 files changed

+87
-37
lines changed

2 files changed

+87
-37
lines changed

src/ln/channelmanager.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3355,9 +3355,9 @@ mod tests {
33553355
chain_hash: genesis_block(Network::Testnet).header.bitcoin_hash(),
33563356
short_channel_id: as_chan.get_short_channel_id().unwrap(),
33573357
node_id_1: if were_node_one { as_network_key } else { bs_network_key },
3358-
node_id_2: if !were_node_one { bs_network_key } else { as_network_key },
3358+
node_id_2: if were_node_one { bs_network_key } else { as_network_key },
33593359
bitcoin_key_1: if were_node_one { as_bitcoin_key } else { bs_bitcoin_key },
3360-
bitcoin_key_2: if !were_node_one { bs_bitcoin_key } else { as_bitcoin_key },
3360+
bitcoin_key_2: if were_node_one { bs_bitcoin_key } else { as_bitcoin_key },
33613361
excess_data: Vec::new(),
33623362
};
33633363
}
@@ -3372,9 +3372,9 @@ mod tests {
33723372
let bs_node_sig = secp_ctx.sign(&msghash, &nodes[1].node.our_network_key);
33733373
chan_announcement = msgs::ChannelAnnouncement {
33743374
node_signature_1 : if were_node_one { as_node_sig } else { bs_node_sig},
3375-
node_signature_2 : if !were_node_one { bs_node_sig } else { as_node_sig},
3375+
node_signature_2 : if were_node_one { bs_node_sig } else { as_node_sig},
33763376
bitcoin_signature_1: if were_node_one { as_bitcoin_sig } else { bs_bitcoin_sig },
3377-
bitcoin_signature_2 : if !were_node_one { bs_bitcoin_sig } else { as_bitcoin_sig },
3377+
bitcoin_signature_2 : if were_node_one { bs_bitcoin_sig } else { as_bitcoin_sig },
33783378
contents: $unsigned_msg
33793379
}
33803380
}

src/ln/router.rs

Lines changed: 83 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,21 @@ struct NetworkMap {
102102
our_node_id: PublicKey,
103103
nodes: HashMap<PublicKey, NodeInfo>,
104104
}
105-
105+
struct MutNetworkMap<'a> {
106+
#[cfg(feature = "non_bitcoin_chain_hash_routing")]
107+
channels: &'a mut HashMap<(u64, Sha256dHash), ChannelInfo>,
108+
#[cfg(not(feature = "non_bitcoin_chain_hash_routing"))]
109+
channels: &'a mut HashMap<u64, ChannelInfo>,
110+
nodes: &'a mut HashMap<PublicKey, NodeInfo>,
111+
}
112+
impl NetworkMap {
113+
fn borrow_parts(&mut self) -> MutNetworkMap {
114+
MutNetworkMap {
115+
channels: &mut self.channels,
116+
nodes: &mut self.nodes,
117+
}
118+
}
119+
}
106120
impl std::fmt::Display for NetworkMap {
107121
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
108122
write!(f, "Node id {} network map\n[Channels]\n", log_pubkey!(self.our_node_id))?;
@@ -199,6 +213,10 @@ impl RoutingMessageHandler for Router {
199213
}
200214

201215
fn handle_channel_announcement(&self, msg: &msgs::ChannelAnnouncement) -> Result<bool, HandleError> {
216+
if msg.contents.node_id_1 == msg.contents.node_id_2 || msg.contents.bitcoin_key_1 == msg.contents.bitcoin_key_2 {
217+
return Err(HandleError{err: "Channel announcement node had a channel with itself", action: Some(ErrorAction::IgnoreError)});
218+
}
219+
202220
let msg_hash = Message::from_slice(&Sha256dHash::from_data(&msg.contents.encode()[..])[..]).unwrap();
203221
secp_verify_sig!(self.secp_ctx, &msg_hash, &msg.node_signature_1, &msg.contents.node_id_1);
204222
secp_verify_sig!(self.secp_ctx, &msg_hash, &msg.node_signature_2, &msg.contents.node_id_2);
@@ -209,7 +227,7 @@ impl RoutingMessageHandler for Router {
209227
panic!("Unknown-required-features ChannelAnnouncements should never deserialize!");
210228
}
211229

212-
match self.chain_monitor.get_chain_utxo(msg.contents.chain_hash, msg.contents.short_channel_id) {
230+
let checked_utxo = match self.chain_monitor.get_chain_utxo(msg.contents.chain_hash, msg.contents.short_channel_id) {
213231
Ok((script_pubkey, _value)) => {
214232
let expected_script = Builder::new().push_opcode(opcodes::All::OP_PUSHNUM_2)
215233
.push_slice(&msg.contents.bitcoin_key_1.serialize())
@@ -220,49 +238,67 @@ impl RoutingMessageHandler for Router {
220238
}
221239
//TODO: Check if value is worth storing, use it to inform routing, and compare it
222240
//to the new HTLC max field in channel_update
241+
true
223242
},
224243
Err(ChainError::NotSupported) => {
225244
// Tentatively accept, potentially exposing us to DoS attacks
245+
false
226246
},
227247
Err(ChainError::NotWatched) => {
228248
return Err(HandleError{err: "Channel announced on an unknown chain", action: Some(ErrorAction::IgnoreError)});
229249
},
230250
Err(ChainError::UnknownTx) => {
231251
return Err(HandleError{err: "Channel announced without corresponding UTXO entry", action: Some(ErrorAction::IgnoreError)});
232252
},
233-
}
253+
};
234254

235-
let mut network = self.network_map.write().unwrap();
255+
let mut network_lock = self.network_map.write().unwrap();
256+
let network = network_lock.borrow_parts();
257+
258+
let chan_info = ChannelInfo {
259+
features: msg.contents.features.clone(),
260+
one_to_two: DirectionalChannelInfo {
261+
src_node_id: msg.contents.node_id_1.clone(),
262+
last_update: 0,
263+
enabled: false,
264+
cltv_expiry_delta: u16::max_value(),
265+
htlc_minimum_msat: u64::max_value(),
266+
fee_base_msat: u32::max_value(),
267+
fee_proportional_millionths: u32::max_value(),
268+
},
269+
two_to_one: DirectionalChannelInfo {
270+
src_node_id: msg.contents.node_id_2.clone(),
271+
last_update: 0,
272+
enabled: false,
273+
cltv_expiry_delta: u16::max_value(),
274+
htlc_minimum_msat: u64::max_value(),
275+
fee_base_msat: u32::max_value(),
276+
fee_proportional_millionths: u32::max_value(),
277+
}
278+
};
236279

237280
match network.channels.entry(NetworkMap::get_key(msg.contents.short_channel_id, msg.contents.chain_hash)) {
238-
Entry::Occupied(_) => {
281+
Entry::Occupied(mut entry) => {
239282
//TODO: because asking the blockchain if short_channel_id is valid is only optional
240283
//in the blockchain API, we need to handle it smartly here, though its unclear
241284
//exactly how...
242-
return Err(HandleError{err: "Already have knowledge of channel", action: Some(ErrorAction::IgnoreError)})
285+
if checked_utxo {
286+
// Either our UTXO provider is busted, there was a reorg, or the UTXO provider
287+
// only sometimes returns results. In any case remove the previous entry. Note
288+
// that the spec expects us to "blacklist" the node_ids involved, but we can't
289+
// do that because
290+
// a) we don't *require* a UTXO provider that always returns results.
291+
// b) we don't track UTXOs of channels we know about and remove them if they
292+
// get reorg'd out.
293+
// c) it's unclear how to do so without exposing ourselves to massive DoS risk.
294+
Self::remove_channel_in_nodes(network.nodes, &entry.get(), msg.contents.short_channel_id);
295+
*entry.get_mut() = chan_info;
296+
} else {
297+
return Err(HandleError{err: "Already have knowledge of channel", action: Some(ErrorAction::IgnoreError)})
298+
}
243299
},
244300
Entry::Vacant(entry) => {
245-
entry.insert(ChannelInfo {
246-
features: msg.contents.features.clone(),
247-
one_to_two: DirectionalChannelInfo {
248-
src_node_id: msg.contents.node_id_1.clone(),
249-
last_update: 0,
250-
enabled: false,
251-
cltv_expiry_delta: u16::max_value(),
252-
htlc_minimum_msat: u64::max_value(),
253-
fee_base_msat: u32::max_value(),
254-
fee_proportional_millionths: u32::max_value(),
255-
},
256-
two_to_one: DirectionalChannelInfo {
257-
src_node_id: msg.contents.node_id_2.clone(),
258-
last_update: 0,
259-
enabled: false,
260-
cltv_expiry_delta: u16::max_value(),
261-
htlc_minimum_msat: u64::max_value(),
262-
fee_base_msat: u32::max_value(),
263-
fee_proportional_millionths: u32::max_value(),
264-
}
265-
});
301+
entry.insert(chan_info);
266302
}
267303
};
268304

@@ -302,12 +338,7 @@ impl RoutingMessageHandler for Router {
302338
&msgs::HTLCFailChannelUpdate::ChannelClosed { ref short_channel_id } => {
303339
let mut network = self.network_map.write().unwrap();
304340
if let Some(chan) = network.channels.remove(short_channel_id) {
305-
network.nodes.get_mut(&chan.one_to_two.src_node_id).unwrap().channels.retain(|chan_id| {
306-
chan_id != NetworkMap::get_short_id(chan_id)
307-
});
308-
network.nodes.get_mut(&chan.two_to_one.src_node_id).unwrap().channels.retain(|chan_id| {
309-
chan_id != NetworkMap::get_short_id(chan_id)
310-
});
341+
Self::remove_channel_in_nodes(&mut network.nodes, &chan, *short_channel_id);
311342
}
312343
},
313344
}
@@ -458,6 +489,25 @@ impl Router {
458489
unimplemented!();
459490
}
460491

492+
fn remove_channel_in_nodes(nodes: &mut HashMap<PublicKey, NodeInfo>, chan: &ChannelInfo, short_channel_id: u64) {
493+
macro_rules! remove_from_node {
494+
($node_id: expr) => {
495+
if let Entry::Occupied(mut entry) = nodes.entry($node_id) {
496+
entry.get_mut().channels.retain(|chan_id| {
497+
short_channel_id != *NetworkMap::get_short_id(chan_id)
498+
});
499+
if entry.get().channels.is_empty() {
500+
entry.remove_entry();
501+
}
502+
} else {
503+
panic!("Had channel that pointed to unknown node (ie inconsistent network map)!");
504+
}
505+
}
506+
}
507+
remove_from_node!(chan.one_to_two.src_node_id);
508+
remove_from_node!(chan.two_to_one.src_node_id);
509+
}
510+
461511
/// Gets a route from us to the given target node.
462512
/// Extra routing hops between known nodes and the target will be used if they are included in
463513
/// last_hops.

0 commit comments

Comments
 (0)