-
Notifications
You must be signed in to change notification settings - Fork 162
fix(l1): update existing contact ENR on NODES response #6172
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
base: main
Are you sure you want to change the base?
fix(l1): update existing contact ENR on NODES response #6172
Conversation
**Motivation** <!-- Why does this pull request exist? What are its goals? --> **Description** <!-- A clear and concise general description of the changes this PR introduces --> <!-- Link to issues: Resolves #111, Resolves #222 --> **Checklist** - [ ] Updated `STORE_SCHEMA_VERSION` (crates/storage/lib.rs) if the PR includes breaking changes to the `Store` requiring a re-sync. Closes #5574 and #5575. Co-authored-by: Esteban Dimitroff Hodi <[email protected]>
**Checklist** - [ ] Updated `STORE_SCHEMA_VERSION` (crates/storage/lib.rs) if the PR includes breaking changes to the `Store` requiring a re-sync. Closes #issue_number
**Motivation** I saw current new_nonce impl allocated a vector when it can just return a fixed size array. **Description** Removes the needless vec **Checklist** - [ ] Updated `STORE_SCHEMA_VERSION` (crates/storage/lib.rs) if the PR includes breaking changes to the `Store` requiring a re-sync.
**Motivation** In order to start merging discv5 code into main, to avoid having a huge PR at the end of the development, we should create a feature flag disabled by default. Closes #5639
**Motivation** <!-- Why does this pull request exist? What are its goals? --> **Description** <!-- A clear and concise general description of the changes this PR introduces --> <!-- Link to issues: Resolves #111, Resolves #222 --> **Checklist** - [ ] Updated `STORE_SCHEMA_VERSION` (crates/storage/lib.rs) if the PR includes breaking changes to the `Store` requiring a re-sync.
**Motivation** <!-- Why does this pull request exist? What are its goals? --> **Description** <!-- A clear and concise general description of the changes this PR introduces --> <!-- Link to issues: Resolves #111, Resolves #222 --> **Checklist** - [ ] Updated `STORE_SCHEMA_VERSION` (crates/storage/lib.rs) if the PR includes breaking changes to the `Store` requiring a re-sync. Closes #5570 Closes #5571
🤖 Kimi Code ReviewSecurity & Correctness Issues1. Missing signature verification in
|
🤖 Codex Code ReviewFindings (ordered by severity)
Notes
If you want, I can propose a minimal patch to fix the FINDNODE trigger and move the fork-id check behind the seq gate. Potential next steps
Automated review by OpenAI Codex · custom prompt |
🤖 Claude Code ReviewNow let me compile my review. I have enough context to provide a comprehensive code review. Code Review: PR #6172 - fix(l1): update existing contact ENR on NODES responseOverall AssessmentThis PR successfully addresses the issue where updated ENRs for existing contacts were silently discarded. The implementation is sound and the added test coverage is excellent. However, there are several concerns regarding correctness, security, and potential edge cases. Critical Issues1. Incomplete Contact State Preservation (peer_table.rs:878-881)When updating an existing contact, only contact.node = node;
contact.record = Some(node_record);
contact.is_fork_id_valid = is_fork_id_valid;Missing updates:
Recommendation: Add a comment explaining why these fields are intentionally preserved, or update them if needed. At minimum: // If the node's network address changed, clear validation state
if contact.node.ip != node.ip || contact.node.tcp_port != node.tcp_port {
contact.validation_timestamp = None;
contact.ping_req_id = None;
}2. ENR Sequence Number Semantics (server.rs:480-481, 485-486)The comment justifies using // The spec says to check for `>`, but we use `!=` to be more defensive
// (e.g. handle rollbacks or resets).
let cached_seq = contact.record.as_ref().map_or(0, |r| r.seq);
if pong_message.enr_seq != cached_seq {Issues:
Recommendation:
let cached_seq = contact.record.as_ref().map_or(0, |r| r.seq);
if pong_message.enr_seq > cached_seq {
// Normal case: peer has newer ENR
let find_node = Message::FindNode(FindNodeMessage { /* ... */ });
self.send_ordinary(find_node, &contact.node).await?;
} else if pong_message.enr_seq < cached_seq {
warn!(
from = %sender_id,
cached_seq,
pong_seq = pong_message.enr_seq,
"ENR sequence number decreased - possible node reset or attack"
);
}High Priority Issues3. Race Condition in seq Comparison (peer_table.rs:877)The comparison let existing_seq = occupied_entry
.get()
.record
.as_ref()
.map_or(0, |r| r.seq);
if node_record.seq > existing_seq {Problem: If we have a contact with Recommendation: Change to let should_update = match occupied_entry.get().record.as_ref() {
None => true, // Always accept first ENR
Some(r) => node_record.seq > r.seq,
};
if should_update {
// ... update
}4. Missing Metrics Update (peer_table.rs:871-883)When updating an existing contact with a new ENR, no metric is recorded. This makes it hard to monitor how often ENR updates occur. Recommendation: Add a metric: Entry::Occupied(mut occupied_entry) => {
// ...
if node_record.seq > existing_seq {
let contact = occupied_entry.get_mut();
// ... update fields
METRICS.record_enr_update().await; // New metric
}
}Medium Priority Issues5. Code Duplication in Fork ID Validation (peer_table.rs:854-862)The fork ID validation logic is duplicated and executed for all records, even if they won't be used (e.g., if Recommendation: Move fork ID validation inside the update branches to avoid unnecessary async work: match self.contacts.entry(node_id) {
Entry::Vacant(vacant_entry) => {
let is_fork_id_valid = validate_fork_id(&node_record, &self.store).await;
// ... create contact
}
Entry::Occupied(mut occupied_entry) => {
if should_update {
let is_fork_id_valid = validate_fork_id(&node_record, &self.store).await;
// ... update contact
}
}
}6. Test Gap: ENR Update Flow (server.rs:1010-1099)The test
Recommendation: Add a follow-up test: #[tokio::test]
async fn test_enr_update_completion() {
// Setup with initial ENR seq=5
// Send PONG with seq=10 (triggers FINDNODE)
// Simulate NODES response with seq=10 ENR
// Verify contact was updated with new ENR
// Verify old fields preserved appropriately
}Low Priority / Style Issues7. Function Extraction (server.rs:901-906)Good refactor extracting /// Generate a unique request ID for discovery protocol messages.
fn generate_req_id() -> Bytes {
let mut rng = OsRng;
Bytes::from(rng.r#gen::<u64>().to_be_bytes().to_vec())
}8. Unnecessary Clone (server.rs:476)The .record_pong_received(&sender_id, pong_message.req_id.clone())Check if Positive Aspects ✅
Security Considerations
SummaryThe PR correctly implements ENR updates for existing contacts and adds proper signature validation. The main concerns are:
The code is generally well-structured and the problem is solved correctly at a high level, but the details need refinement to ensure spec compliance and security. Automated review by Claude (Anthropic) · custom prompt |
Lines of code reportTotal lines added: Detailed view |
Greptile OverviewGreptile SummaryThis PR fixes a bug where ENR (Ethereum Node Record) updates for existing contacts were silently discarded. The
Confidence Score: 5/5
|
| Filename | Overview |
|---|---|
| crates/networking/p2p/discv5/peer_table.rs | Updated new_contact_records() to handle ENR updates for existing contacts by checking seq numbers and adding signature verification |
| crates/networking/p2p/discv5/server.rs | Added ENR update request on PONG with mismatched seq, extracted generate_req_id() helper, and added comprehensive test coverage |
Sequence Diagram
sequenceDiagram
participant Local as Local Node
participant Remote as Remote Node
participant PT as Peer Table
Note over Local,Remote: ENR Update Flow via PONG
Remote->>Local: PONG (enr_seq=10)
Local->>PT: get_contact(remote_id)
PT-->>Local: Contact (cached_seq=5)
alt enr_seq != cached_seq
Note over Local: ENR seq mismatch detected
Local->>Remote: FINDNODE(distance=0)
Remote->>Local: NODES(enr with seq=10)
Local->>PT: new_contact_records([enr])
PT->>PT: verify_signature()
alt Entry::Occupied && new_seq > cached_seq
Note over PT: Update existing contact
PT->>PT: Update node, record, is_fork_id_valid
else Entry::Vacant
Note over PT: Insert new contact
PT->>PT: Create and insert new Contact
end
else enr_seq == cached_seq
Note over Local: No update needed
end
6af59eb to
60f1455
Compare
60f1455 to
a1a0e15
Compare
Addressing agent reviewsFixes applied
False positives dismissed
|
Motivation
new_contact_records()only inserted new contacts (Entry::Vacant), silently discarding updated ENRs for already-known peers. This made the FINDNODE(distance=0) request from #5910 ineffective — the request was sent but the response couldn't update the existing record.Description
Entry::Occupiedinnew_contact_records(): when the incoming record has a higherseqthan the existing one, update the contact'snode,record, andis_fork_id_valid.verify_signature()) upfront for all records, which was previously missing even for new contacts.matchto avoid duplication.Closes #6166