feat(beacon): report the node's ENR from /node/identity, and discover keystores without a definitions file - #644
pablodeymo wants to merge 4 commits into
Conversation
🤖 Claude Code ReviewReview of PR 644: ENR and multiaddrs in
|
🤖 Codex Code ReviewI found 2 review items.
Otherwise this looks clean:
Automated review by OpenAI Codex · gpt-5.4 · custom prompt |
🤖 Kimi Code ReviewI'll review this PR focusing on the areas you specified. This PR adds Beacon API Critical Issues1. ENR Padding Fix - Potential Panic on Empty String (crates/net/p2p/src/discovery/mod.rs:198-200)let local_enr = local_record
.enr_url()
.map_err(DiscoveryError::EncodeEnr)?
.trim_end_matches('=')
.to_string();Bug: If More serious: The Suggestion: Consider whether the identity endpoint should reflect the current ENR rather than the startup snapshot, or document this limitation more prominently. 2. IPv6 Address Formatting Bug (bin/ethlambda/src/main.rs:129-132)let family = if ip.is_ipv4() { "ip4" } else { "ip6" };
// ...
format!("/{family}/{ip}/udp/{gossipsub_port}/quic-v1/p2p/{peer_id}"),Bug: IPv6 addresses in multiaddr format must be enclosed in square brackets when used with Wait — re-checking: The Security Issues3. Path Traversal in Keystore Discovery (crates/validator/src/keys/definitions.rs:102-120)pub fn discover_if_absent(validators_dir: &Path, secrets_dir: &Path) -> Result<usize> {
// ...
let keystore_path = entry.path().join("voting-keystore.json");
// ...
let pubkey = format!("0x{}", pubkey.trim_start_matches("0x"));
definitions.push(ValidatorDefinition {
enabled: true,
voting_keystore_password_path: secrets_dir.join(&pubkey),
// ...
});Vulnerability: The
Fix (line 113-114): let pubkey = crate::keys::keystore::Keystore::from_json(&json)
.ok()
.and_then(|keystore| keystore.pubkey)
.ok_or_else(|| Error::Keystore { ... })?;
// Add validation:
if !pubkey.chars().all(|c| c.is_ascii_hexdigit()) {
return Err(Error::Keystore {
path: keystore_path.display().to_string(),
reason: "pubkey contains non-hex characters".to_string(),
});
}
let pubkey = format!("0x{}", pubkey.trim_start_matches("0x"));4. TOCTOU Race Condition in File Existence Check (crates/validator/src/keys/definitions.rs:97-100)pub fn discover_if_absent(validators_dir: &Path, secrets_dir: &Path) -> Result<usize> {
if validators_dir.join(DEFINITIONS_FILE).exists() {
return Ok(0);
}Classic time-of-check to time-of-use race. Between the Fix: Use Correctness & Logic Issues5. Missing
|
| Priority | Issue | Location |
|---|---|---|
| Critical | Missing discovery_addresses assertion in test |
crates/net/rpc/src/beacon/node.rs:193 |
| Security | Path traversal via unvalidated pubkey | crates/validator/src/keys/definitions.rs:113-114 |
| Medium | Stale ENR after discv5 re-signs | crates/net/p2p/src/lib.rs:1112 (design limitation) |
| Low | Doc comment placement | crates/validator/src/keys/definitions.rs:79-80 |
| Low | TOCTOU race in file check | crates/validator/src/keys/definitions.rs:98 |
The PR is generally well-structured and the ENR padding fix is correct. The main concerns are the incomplete test and the potential path traversal in keystore discovery.
Automated review by Kimi (Moonshot AI) · kimi-k2.6 · custom prompt
… instead of empty strings, resolving the recorded spec deviation. A devnet orchestrator (ethereum-package reads .data.enr and .data.p2p_addresses[0] from every consensus client to wire bootnodes) could not use ethlambda beacon as a peer or a bootnode without it. P2P now keeps the ENR discovery published at startup (P2P::local_enr), and main.rs builds a BeaconIdentity from it: the QUIC and TCP libp2p multiaddrs and the discv5 one, from --discovery.advertise-ip and the two ports. Without that flag the addresses stay empty rather than guessed, since a node bound to 0.0.0.0 does not know the address peers reach it on. It reaches the handler through BeaconApiHandles; the handler still answers, with empty network fields, when none is layered.
…ile, so ethlambda validator runs on the key layout eth2-val-tools, staking-deposit-cli imports and ethereum-package produce without a hand-written validator_definitions.yml. discover_if_absent scans <validators-dir>/<0xpubkey>/voting-keystore.json, pairs each with <secrets-dir>/<0xpubkey>, and writes the file (sorted, so it is reproducible) before the store loads it, which then checks every declared key against its decrypted secret as before. An existing file is never rescanned, since it is the keymanager's record and a rescan would bring back a key it deleted. This is what lets an ethereum-package vc_type for ethlambda pass the keystore artifact straight through, as its Lighthouse launcher does.
…URL-safe base64 with no padding, but ethrex's enr_url pads it, and Lighthouse refuses a padded record ("Not valid as ENR nor Multiaddr"). That surfaced as soon as /eth/v1/node/identity started reporting the ENR: an ethereum-package devnet with ethlambda as the first participant handed the padded record to Lighthouse as its bootnode, and Lighthouse exited before opening its HTTP port. The discovery test now pins that the record carries no padding.
…. Since #637 made discovery opt-in on node, P2P::local_enr is None without it, as no record is published; the identity handler already answers with empty network fields in that case.
8022f38 to
c5221be
Compare
🗒️ Description / Motivation
Three small changes that let an ethereum-package devnet use ethlambda as a real participant: as a peer or bootnode for other consensus clients, and as a validator client fed ethereum-package's own keystore artifact. They were written for lambdaclass/ethlambda_private#16 but pushed after it merged; they are rebased onto
beacon-chain-integrationhere, now that lambdaclass/ethlambda_private#34 and lambdaclass/ethlambda_private#40 are merged./eth/v1/node/identityreported emptyenrandp2p_addresses. ethereum-package reads.data.enrand.data.p2p_addresses[0]from every consensus client to wire bootnodes, so ethlambda beacon could not serve as a peer or a bootnode. This was recorded as a spec deviation.enr_urlpads it.ethlambda validatorrequired a hand-writtenvalidator_definitions.yml. The layout eth2-val-tools, staking-deposit-cli and ethereum-package produce has no such file, so an ethereum-packagevc_typefor ethlambda could not pass the keystore artifact straight through, as its Lighthouse launcher does.What Changed
48ebe3c: report the ENR and listen addressesP2P::local_enrkeeps the ENR discovery published at startup.main.rsbuilds aBeaconIdentityfrom it: the QUIC and TCP libp2p multiaddrs and the discv5 one, from--discovery.advertise-ipand the two ports.BeaconApiHandles.docs/spec_deviations.mdand updatesdocs/rpc.md.1b1a985: discover keystores without a definitions file (crates/validator/src/keys/definitions.rs,bin/ethlambda/src/validator.rs,docs/cli.md)discover_if_absentscans<validators-dir>/<0xpubkey>/voting-keystore.jsonand pairs each with<secrets-dir>/<0xpubkey>.6a2915d: publish the ENR without base64 padding (crates/net/p2p/src/discovery/mod.rs).Correctness / Behavior Guarantees
--discovery.advertise-ip,p2p_addressesanddiscovery_addressesstay empty: a node bound to0.0.0.0doesn't know the address peers reach it on.Tests Added / Run
keystores_in_the_lighthouse_layout_are_discovered_when_no_file_existsan_existing_definitions_file_is_never_rescannedidentity_reports_the_enr_and_listen_addressesidentity_without_an_identity_carries_the_peer_id_and_empty_network_fieldsmain.rs, around the RPC handles. The resolution keeps this PR'srpc_identitybinding and drops the duplicateSharedAttestationPoollines the original commit carried as context, since lambdaclass/ethlambda_private#40 already removed that duplicate.Related Issues / PRs
✅ Verification Checklist
make fmt— cleanmake lint(clippy with-D warnings) — cleanmake test(test-consensusplustest-node, atrelease-fast) — all passing (1845 passed, 0 failed, 25 ignored)