Skip to content

Implement checks amt_to_forward and outgoing_cltv_value acceptable ranges #114

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

Conversation

ariard
Copy link

@ariard ariard commented Aug 17, 2018

Just implemented checks as described in rfc 04 Packet structure. Maybe we should check also against an older fee value as recommended in rfc 07 due to propagation delay, but if so need to log old fee somewhere?

@TheBlueMatt
Copy link
Collaborator

Looks like maybe the fake network tests are violating the rules somehow?

@ariard ariard force-pushed the ranges_amt_to_forward_outgoing_cltv_values branch from 4b2281d to a5a1f7e Compare August 18, 2018 02:51
@ariard
Copy link
Author

ariard commented Aug 18, 2018

Yeah, it's the 6th send_payment in fake_network_test with a channel_outbound increase. I'm still digging to see if this case is right handle by get_route to generate first hop amount_msat..

@ariard
Copy link
Author

ariard commented Aug 18, 2018

Looking on how others implementations manage get_our_fee_base_msat, spec is unclear on what is the right way to do it.

@ariard ariard force-pushed the ranges_amt_to_forward_outgoing_cltv_values branch from a5a1f7e to 90d6260 Compare August 20, 2018 01:33
@ariard
Copy link
Author

ariard commented Aug 20, 2018

Was a funny one to track :)
Amount msat in send_payment in fake_network_test were all false due to the way Router::handle_channel_update manages the msg. When this method receives a msg, it checks if it's a two_to_one or one_to_two one and completes DirectionalChannelInfo accordingly.
The problem is that cltv_expiry_delta, htlc_minimum_msat, fee_base_msat, as included in the message, aren't the policy that the source node will seen apply for its htlc but the policy it will apply for the other node policy. So we need to reverse.
If not, get_route uses this false information when adding node for Route and the final amount msat is inaccurate.
The fix is maybe a bit ugly, I think we can also fix it at route generation?

I wouldn't have fall on this if we didn't used get_our_fee_base_msat, I find it a little bit complex due to the channel_outbound case but I guess it's better than a magic default value of 1000 msat as lnd and c-lightning do.

90d626 is log stuff for NetworkMap, really useful for route bug tracking , but it's need network map fields as public so maybe not?

src/ln/router.rs Outdated
htlc_minimum_msat: u64,
fee_base_msat: u32,
fee_proportional_millionths: u32,
pub struct DirectionalChannelInfo {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These probably want pub(crate), not pub, and maybe a comment that they're only crate-pub for the logger's access.

@@ -1625,7 +1625,18 @@ impl ChannelMessageHandler for ChannelManager {
hmac: next_hop_data.hmac.clone(),
};

//TODO: Check amt_to_forward and outgoing_cltv_value are within acceptable ranges!
let channel_state = self.channel_state.lock().unwrap();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, can you move this check down past the existing channel_state.lock() call? It really sucks to lock, do a channel lookup, check something, unlock, then lock and do the same channel lookup again. That's likely to introduce strange races as well.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, but it's add a check to prevent the case of the HTLC being our payment

src/ln/router.rs Outdated
$target.htlc_minimum_msat = msg.contents.htlc_minimum_msat;
$target.fee_base_msat = msg.contents.fee_base_msat;
$target.fee_proportional_millionths = msg.contents.fee_proportional_millionths;
chan_was_enabled = $first_side.enabled;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice debugging, but sadly I think you came to the wrong conclusion. The way I read the second paragraph in https://github.com/lightningnetwork/lightning-rfc/blob/master/07-routing-gossip.md#the-channel_update-message reads to me like the original code here is correct and the issue is in the new check - fees/cltv_deltas are announced on the basis of the channel where the HTLC will be forwarded to, not the basis of which channel the HTLC came in on.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah but seems that my code follows rfc 07 fee calculation : fee_base_msat + ( amount_msat * fee_proportional_millionths / 1000000 ) and the rfc 04 check : incoming_htlc_amt - fee >= amt_to_forward ? The amounts don't match as seen there, running cargo test fake_network_test -- --no-capture on my demo_bug_channelinfo_handling branch : https://0bin.net/paste/lixegfyl2cDb+n25#B9BdwQDf4pqNYa5S4dZuK6i1ZGM+OQMYqjIcMgSuilW

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, fees/cltv_deltas are based on the receiving node policy where the HTLC will be forwarded to, the issue is on the way handle_channel_update is recording the channel_update as one_to_two or two_to_one. When the node is one, and uses the one_to_two side, it has to use the two_to_one directional info and so the receiving node policy. That's this policy in ChannelInfo, i think, is inverted now.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The information going into the channels map here looks right to me - its setting the fees on a channel on the entry that represents the outgoing hop (as indicated by it being the side with the src_node_id being equal to the node which signed the message). In order to be calculating the fee on the basis of where the payment is being forwarded to (ie if you route A -> B -> C, the fees paid are the ones that B announced for the B -> C hop) you'd need to change the check that was added to update_add_htlc.

Ok(())
}
}
macro_rules! log_networkmap {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are really awesome, but it sucks to add unused code cause it generates way more compile warnings. Honestly its probably worth adding a pub fn Router::log_network function to keep these around.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use [allow(dead_code)] and [allow(unused_macros], seems that these attributes been there for avoid lint warnings ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just adding allow(dead_code) doesn't resolve the issue - the point is that code should be used or it won't be kept up-to-date and probably doesn't belong in the repo at all.

@ariard ariard force-pushed the ranges_amt_to_forward_outgoing_cltv_values branch 2 times, most recently from 4762f02 to 65d8210 Compare August 21, 2018 00:09
@ariard
Copy link
Author

ariard commented Aug 21, 2018

travis failures on fuzz part due to amount in/out fee required mismatch, what is test:test_no_existing_test_breakage? it's calling handle_update_add_htlc somewhere?

@ariard ariard force-pushed the ranges_amt_to_forward_outgoing_cltv_values branch from 65d8210 to bfce91c Compare August 21, 2018 00:56
Copy link
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new full_stack_target test tests that existing fuzz test cases aren't broken by changes in behavior. This is mostly for my tracking so I know what bits to not take until I want to restart fuzzing on all new test-cases. It will break generally when you add new feerate requests and new checks like this. If you don't feel like fiing the test case that's written out there that's find, I'd generally be fine merging things that comment it out and then fixing it myself.

There is another issue here that I forgot about - if we let routing fees charged float with network fees (which I think is prudent, but may be more complexity than its worth) then we have to pay close attention to when those fees change and send channel_update messages when the feerate info causes us to change our feerate, which is hopefully not too often.

{
if next_hop_data.hmac != [0;32] {
// check doesn't apply if we are last node (check happens here due to lock optimizations)
let chan = channel_state_lock.by_id.get(&msg.channel_id).unwrap();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still wrong - we should be doing the fee checks against the place we're sending the payment (ie pending_forward_info.short_channel_id ie the channel that is looked up in the next if statement.

src/ln/router.rs Outdated
$target.htlc_minimum_msat = msg.contents.htlc_minimum_msat;
$target.fee_base_msat = msg.contents.fee_base_msat;
$target.fee_proportional_millionths = msg.contents.fee_proportional_millionths;
chan_was_enabled = $first_side.enabled;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The information going into the channels map here looks right to me - its setting the fees on a channel on the entry that represents the outgoing hop (as indicated by it being the side with the src_node_id being equal to the node which signed the message). In order to be calculating the fee on the basis of where the payment is being forwarded to (ie if you route A -> B -> C, the fees paid are the ones that B announced for the B -> C hop) you'd need to change the check that was added to update_add_htlc.

@ariard ariard force-pushed the ranges_amt_to_forward_outgoing_cltv_values branch 3 times, most recently from 9ff8238 to 50bcfda Compare August 22, 2018 00:22
@ariard
Copy link
Author

ariard commented Aug 22, 2018

Aaaaah finally got it! After reading again and scrupulously this time the rfcs and lnd, c-lightning codes! Sorry for the lost time, i was perpexled at first i think by the channel_outbound estimation thing case and from then, misjudged all the rest.. At least i've seen the Router internals.

For the routing fees coupled on network fees I think that's an issue with FeeEstimator implementation to avoid too much channel_update messages. We should warn it to have an update limit for a given window time and not necessarily to echo all network fees moves (but don't know algorithm fees so surely harder to implement than to describe I guess).

@ariard
Copy link
Author

ariard commented Aug 22, 2018

I commented out the new fuzzing case, will dig into the fuzz tools and set it up on my environnement soon, need to understand this too

@@ -1656,6 +1654,20 @@ impl ChannelMessageHandler for ChannelManager {
Some(id) => id.clone(),
};
let chan = channel_state.by_id.get_mut(&forwarding_id).unwrap();
let fee = 0;
if next_hop_data.hmac != [0;32] {
let fee = chan.get_our_fee_base_msat(&*self.fee_estimator) + (pending_forward_info.amt_to_forward * self.fee_proportional_millionths as u64 / 1000000) as u32;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because you have a "let" here, fee is always 0 in the next if statement. Also, the pending_forward_info.onion_packet.is_some() check above is duplicative with this - if next_hop_data.hmac is 0s onion_packet is always None.

src/ln/router.rs Outdated
fee_base_msat: u32,
fee_proportional_millionths: u32,
//Here, DirectionalChannelInfo, ChannelInfo, NodeInfo and NetworkMap are pub(crate) only for log_* macros access
pub(crate) struct DirectionalChannelInfo {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, it seems weird to put the logger in log macros and then expose all these things, maybe just implement Display for these types directly instead of going through the indirection?

@ariard ariard force-pushed the ranges_amt_to_forward_outgoing_cltv_values branch from 50bcfda to 8462362 Compare August 23, 2018 02:20
@ariard
Copy link
Author

ariard commented Aug 23, 2018

Updated, with Display trait on networkmap structs.

@TheBlueMatt
Copy link
Collaborator

Cool, thanks, will take as #125.

TheBlueMatt added a commit that referenced this pull request Aug 23, 2018
@ariard ariard deleted the ranges_amt_to_forward_outgoing_cltv_values branch August 23, 2018 21:05
TheBlueMatt added a commit that referenced this pull request Aug 23, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants