Skip to content
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

fix(p2p): validate chain exchange responses for block headers #4454

Merged
merged 13 commits into from
Jul 11, 2024

Conversation

hanabi1224
Copy link
Contributor

@hanabi1224 hanabi1224 commented Jun 26, 2024

Summary of changes

Part of #4441 investigation

Confirmed that ChainExchange failures should be in the console output or log. Theoretically, there is a chance that a remote peer always returns a successful empty response and leads the downloading code to a dead loop with no errors.

This PR tries to mitigate the issue by adding validation logic to the chain exchange response

Changes introduced in this pull request:

  • add validation logic to the chain exchange response
  • minor logging improvement
  • use NonEmptyU64 instead of u64 for chain_exchange request_len
  • refine stateless peer filtering logic to fallback to all peers when the result is empty

Reference issue to close (if applicable)

Closes

Other information and links

Change checklist

  • I have performed a self-review of my own code,
  • I have made corresponding changes to the documentation,
  • I have added tests that prove my fix is effective or that my feature works (if possible),
  • I have made sure the CHANGELOG is up-to-date. All user-facing changes should be reflected in this document.

@hanabi1224 hanabi1224 marked this pull request as ready for review June 26, 2024 06:48
@hanabi1224 hanabi1224 requested a review from a team as a code owner June 26, 2024 06:48
@hanabi1224 hanabi1224 requested review from LesnyRumcajs and elmattic and removed request for a team June 26, 2024 06:48
Comment on lines 150 to 167
|tipsets: &Vec<Arc<Tipset>>| {
if let Some(start) = tipsets.first() {
if start.key() != tsk {
tracing::warn!(epoch=%start.epoch(), expected=%tsk, actual=%start.key(), "start tipset key mismatch");
return false;
}
for (ts, pts) in tipsets.iter().zip(tipsets.iter().skip(1)) {
if ts.parents() != pts.key() {
tracing::warn!(epoch=%ts.epoch(), expected_parent=%pts.key(), actual_parent=%ts.parents(), "invalid chain");
return false;
}
}
true
} else {
tracing::warn!(%count, "invalid empty chain_exchange_headers response");
false
}
},
Copy link
Member

Choose a reason for hiding this comment

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

Would it make sense to extract this to a named method a write a unit test to check and document the behaviour? It also might prevent future regressions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

Copy link
Member

@LesnyRumcajs LesnyRumcajs left a comment

Choose a reason for hiding this comment

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

I'm still waiting for a unit test but manage my expectations. No bonusly to incentivize one. :(

@hanabi1224
Copy link
Contributor Author

I'm still waiting for a unit test but manage my expectations. No bonusly to incentivize one. :(

@LesnyRumcajs unit tests added!

@@ -562,4 +597,37 @@ mod tests {
assert_eq!(batch.get_ok().await, None);
assert!(exceeded.load(Ordering::Relaxed));
}

#[test]
#[allow(unused_variables)]
Copy link
Member

Choose a reason for hiding this comment

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

What variables are unused?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

genesis_header, first_header etc.

@hanabi1224 hanabi1224 force-pushed the hm/validate-result--chain_exchange_headers branch from c52a1c5 to 0acd8e2 Compare June 27, 2024 07:07
}
let cost = if info.successes + info.failures > 0 {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

wibble: this was changed to if info.successes > 0 to address #4376 (comment)
However, this unexpectedly changes the logic to favor peers that never succeed.
The original code does not panic but returns inf

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=657686952703dcee856841d192b99774

fn main() {
    let x = 100.0 + f64::from(1) / f64::from(0);
    println!("{x}");
}

// Output: inf

@hanabi1224 hanabi1224 added this pull request to the merge queue Jul 11, 2024
Merged via the queue into main with commit 635906f Jul 11, 2024
28 checks passed
@hanabi1224 hanabi1224 deleted the hm/validate-result--chain_exchange_headers branch July 11, 2024 09:07
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.

None yet

3 participants