-
Notifications
You must be signed in to change notification settings - Fork 3
feat(app): wire the core node and implement the run command #536
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
Open
emlautarom1-agent
wants to merge
29
commits into
main
Choose a base branch
from
emlautarom1/app-wiring
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+3,101
−90
Open
Changes from all commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
dd41593
Initial wiring
emlautarom1 b8ae7b2
feat(app): wire core duty workflow
emlautarom1-agent[bot] 0efaac4
Remove reexport comment
emlautarom1 3d81f25
Remove lifetimes from slice
emlautarom1 8640ec0
Inline arguments
emlautarom1 508fffe
Rename `setup_p2p` to `wire_p2p` to better match Charon naming
emlautarom1 f1339ee
Code cleanup
emlautarom1 3fee8a9
Add TODO regarding `Arc<OnceLock<Handle>>` pattern
emlautarom1 7ed55b2
Small refactor
emlautarom1 5942052
feat(app): wire eth2 sigagg verifier and consensus feature set
emlautarom1-agent[bot] 31ba7de
feat(app): wire real duty gater, deadlines, graffiti and fetch flags
emlautarom1-agent[bot] 59482fb
feat(app): register validator API duty-query back-edges
emlautarom1-agent[bot] 6c62073
feat(app): verify inbound partial signatures with the eth2 verifier
emlautarom1-agent[bot] 402d4f4
feat(app): verify the cluster lock on startup
emlautarom1-agent[bot] 57e7ff2
docs(app): clarify submission-client and pending gas-limit/fallback T…
emlautarom1-agent[bot] f0bc80c
feat(app): encode all duty types for consensus via the core codec
emlautarom1-agent[bot] 4592989
feat(app): finish Part A — privkey lock, sign-path test coverage, cle…
emlautarom1-agent[bot] 9e2ff54
Cleanup docs
emlautarom1 bce13aa
fix(app): seed the shared validator cache into both beacon clients
emlautarom1-agent[bot] b142300
fix(app): align no_verify, parsigex errors and p2p-key checks with Ch…
emlautarom1-agent[bot] fe431d1
feat(app): wire relay support into the core node
emlautarom1-agent[bot] 61a3806
refactor(app): load and verify the cluster lock via pluto-cluster
emlautarom1-agent[bot] 5c98b0c
feat(featureset): add case-insensitive string parsing for Status
emlautarom1-agent[bot] 920397c
refactor(app): drop dead AppConfig data_dir and target_gas_limit fields
emlautarom1-agent[bot] 62ab52f
chore(cli): add pluto-featureset dependency
emlautarom1-agent[bot] 3a63a06
feat(cli): add featureset and app error variants
emlautarom1-agent[bot] 57a44c4
feat(cli): bridge RunConfig into pluto-app and wire App::run
emlautarom1-agent[bot] f05569f
test(cli): cover the RunConfig to AppConfig bridge
emlautarom1-agent[bot] 223d691
fix(app): fail the run when a lifecycle task errors
emlautarom1-agent[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,166 @@ | ||
| //! Composed P2P [`NetworkBehaviour`] for the core duty workflow. | ||
| //! | ||
| //! This is the Rust analog of Charon's `wireP2P`: it composes the relay | ||
| //! transport behaviours (relay client, [`RelayManager`], force-direct) with | ||
| //! the three core protocol behaviours (partial-signature exchange, QBFT | ||
| //! consensus, peerinfo) into a single libp2p behaviour and builds a [`Node`] | ||
| //! driving it. Relay reservation + routing is the cluster's only | ||
| //! peer-discovery path (lock ENRs carry no addresses), mirroring Charon's | ||
| //! `NewRelays` / `NewRelayReserver` / `NewRelayRouter` / | ||
| //! `ForceDirectConnections` (`app.go:341-400`). | ||
| //! | ||
| //! Routing is push-based inside the individual behaviours (the QBFT p2p | ||
| //! [`Handler`](pluto_consensus::qbft::p2p) holds an `Arc<Consensus>` and calls | ||
| //! `consensus.handle()`; parsigex's [`Handle::subscribe`] dispatches inbound | ||
| //! partial signatures), so the swarm drive loop body can be empty for | ||
| //! correctness — see [`crate::node::drive_network`]. | ||
|
|
||
| use std::{collections::HashMap, sync::Arc}; | ||
|
|
||
| use libp2p::{relay, swarm::NetworkBehaviour}; | ||
| use pluto_consensus::qbft; | ||
| use pluto_core::{gater::DutyGaterFn, types::PubKey}; | ||
| use pluto_crypto::types::PublicKey; | ||
| use pluto_eth2api::EthBeaconNodeApiClient; | ||
| use pluto_p2p::{ | ||
| bootnode, | ||
| force_direct::ForceDirectBehaviour, | ||
| gater, | ||
| p2p::{Node, NodeType}, | ||
| p2p_context::P2PContext, | ||
| peer::{self, Peer}, | ||
| relay::RelayManager, | ||
| }; | ||
| use pluto_parsigex as parsigex; | ||
| use pluto_peerinfo::{self as peerinfo, LocalPeerInfo}; | ||
| use tokio_util::sync::CancellationToken; | ||
|
|
||
| use crate::node::AppError; | ||
|
|
||
| /// Composed network behaviour for the core duty workflow. | ||
| #[derive(NetworkBehaviour)] | ||
| pub(crate) struct CoreBehaviour { | ||
| /// Relay client transport (circuit reservations and relayed dials). | ||
| pub relay: relay::client::Behaviour, | ||
| /// Relay reservation lifecycle + relay-circuit peer routing (Charon's | ||
| /// `NewRelayReserver` + `NewRelayRouter`). | ||
| pub relay_manager: RelayManager, | ||
| /// Upgrades relay-routed connections to direct ones (Charon's | ||
| /// `ForceDirectConnections`). | ||
| pub force_direct: ForceDirectBehaviour, | ||
| /// Partial signature exchange between cluster peers. | ||
| pub parsigex: parsigex::Behaviour, | ||
| /// QBFT consensus message transport. | ||
| pub consensus: qbft::p2p::Behaviour, | ||
| /// Peer metadata exchange. | ||
| pub peerinfo: peerinfo::Behaviour, | ||
| } | ||
|
|
||
| /// Async handles for driving the composed behaviour from the core workflow. | ||
| pub struct CoreHandles { | ||
| /// Outbound partial-signature broadcast + inbound subscription handle. | ||
| pub parsigex: parsigex::Handle, | ||
| /// Outbound QBFT broadcast handle. | ||
| pub consensus: qbft::p2p::Handle, | ||
| } | ||
|
|
||
| /// Composes the core behaviours and builds the libp2p [`Node`]. | ||
| // TODO(#402 part B): QUIC transport (featureset-gated off at v1.7.1) and | ||
| // bandwidth metrics. | ||
| #[allow( | ||
| clippy::too_many_arguments, | ||
| reason = "wireP2P aggregates independent inputs; a config struct is deferred to part B when priority inputs are added" | ||
| )] | ||
| pub(crate) async fn wire_p2p( | ||
| key: k256::SecretKey, | ||
| p2p_config: pluto_p2p::config::P2PConfig, | ||
| peers: Vec<Peer>, | ||
| consensus: Arc<qbft::Consensus>, | ||
| duty_gater: DutyGaterFn, | ||
| eth2_cl: EthBeaconNodeApiClient, | ||
| pub_shares_by_key: HashMap<PubKey, HashMap<u64, PublicKey>>, | ||
| lock_hash: Vec<u8>, | ||
| builder_enabled: bool, | ||
| nickname: String, | ||
| cancellation: CancellationToken, | ||
| ) -> Result<(Node<CoreBehaviour>, CoreHandles), AppError> { | ||
| let peer_ids = peers.iter().map(|peer| peer.id).collect::<Vec<_>>(); | ||
| let local_peer_id = peer::peer_id_from_key(key.public_key())?; | ||
|
|
||
| // Relay endpoints resolve in the background; the `Charon-Cluster` header | ||
| // carries the lock hash hex (Charon v1.7.1 `p2p.NewRelays`). Post-#4130 | ||
| // Charon additionally sends a `Cluster-Uuid` header for relay-side load | ||
| // balancing — a pending follow-up in pluto-p2p's `new_relays`. | ||
| let relay_addrs = bootnode::relay_addrs_for_resolution(&p2p_config.relays); | ||
| let relays = | ||
| bootnode::new_relays(cancellation.clone(), &relay_addrs, &hex::encode(&lock_hash)).await?; | ||
|
|
||
| // Closed gater: only cluster peers and the resolved relays may connect. | ||
| let conn_gater = gater::ConnGater::new_conn_gater(peer_ids.clone(), relays.clone()); | ||
|
|
||
| let p2p_context = P2PContext::new(peer_ids.clone()); | ||
| p2p_context.set_local_peer_id(local_peer_id); | ||
|
|
||
| // Keeps one circuit reservation alive per relay and continuously routes | ||
| // cluster peers via relay circuit addresses; force-direct then upgrades | ||
| // relayed connections to direct ones. | ||
| let relay_manager = RelayManager::new(relays, p2p_context.clone()); | ||
| let force_direct = ForceDirectBehaviour::new(p2p_context.clone(), local_peer_id); | ||
|
|
||
| // Partial signature exchange. Inbound partial signatures are verified | ||
| // against the sender's public share for the duty via the eth2 verifier | ||
| // (Charon `parsigex.NewEth2Verifier`). | ||
| let parsigex_config = parsigex::Config::new( | ||
| local_peer_id, | ||
| p2p_context.clone(), | ||
| parsigex::new_eth2_verifier(eth2_cl, pub_shares_by_key), | ||
| duty_gater, | ||
| ); | ||
| let (parsigex_comp, parsigex_handle) = parsigex::Behaviour::new(parsigex_config); | ||
|
|
||
| // QBFT consensus transport. `Behaviour::new` errors if the local peer id is | ||
| // not present in the configured cluster peer list. | ||
| let (consensus_comp, consensus_handle) = qbft::p2p::Behaviour::new(qbft::p2p::Config { | ||
| consensus, | ||
| p2p_context: p2p_context.clone(), | ||
| local_peer_id, | ||
| cancellation, | ||
| })?; | ||
|
|
||
| // Peer metadata exchange. | ||
| let (git_hash, _) = pluto_core::version::git_commit(); | ||
| let peerinfo_config = peerinfo::Config::new(LocalPeerInfo::new( | ||
| pluto_core::version::VERSION.to_string(), | ||
| lock_hash, | ||
| git_hash, | ||
| builder_enabled, | ||
| nickname, | ||
| )) | ||
| .with_peers(peer_ids.clone()); | ||
| let peerinfo_comp = peerinfo::Behaviour::new(local_peer_id, peerinfo_config); | ||
|
|
||
| let node = Node::new( | ||
| p2p_config, | ||
| key, | ||
| NodeType::TCP, | ||
| false, | ||
| p2p_context, | ||
| |builder, _keypair, relay_client| { | ||
| builder.with_gater(conn_gater).with_inner(CoreBehaviour { | ||
| relay: relay_client, | ||
| relay_manager, | ||
| force_direct, | ||
| parsigex: parsigex_comp, | ||
| consensus: consensus_comp, | ||
| peerinfo: peerinfo_comp, | ||
| }) | ||
| }, | ||
| )?; | ||
|
|
||
| let handles = CoreHandles { | ||
| parsigex: parsigex_handle, | ||
| consensus: consensus_handle, | ||
| }; | ||
|
|
||
| Ok((node, handles)) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| //! Configuration for the distributed-validator node. | ||
|
|
||
| use std::{net::SocketAddr, path::PathBuf, sync::Arc, time::Duration}; | ||
|
|
||
| use pluto_featureset::FeatureSet; | ||
| use pluto_p2p::config::P2PConfig; | ||
|
|
||
| /// Application configuration for running a distributed-validator node. | ||
| /// | ||
| /// This is the Rust analog of Charon's `app.Config` (`app/app.go`), reduced to | ||
| /// the minimal set required to wire and run the core duty workflow. | ||
| /// Observability (monitoring/debug API, tracing/OTLP) and simnet/mock-only | ||
| /// fields are intentionally omitted for the minimal-runnable wiring. | ||
| // TODO(#402 part B): add monitoring/debug addrs, OTLP/Jaeger tracing config, | ||
| // simnet (beacon/validator mock) and `TestConfig`-style overrides. | ||
| #[derive(Debug, Clone)] | ||
| pub struct AppConfig { | ||
| /// P2P networking configuration (listen/advertise addresses, relays, ...). | ||
| pub p2p: P2PConfig, | ||
|
|
||
| /// Path to the cluster lock file (`cluster-lock.json`). | ||
| pub lock_file: PathBuf, | ||
|
|
||
| /// Path to the node's secp256k1 P2P private key. | ||
| pub priv_key_file: PathBuf, | ||
|
|
||
| /// Enable private-key file locking. When set, a | ||
| /// `<priv_key_file>.lock`sentinel is maintained for the node's lifetime to | ||
| /// detect a second node started against the same key. | ||
| pub priv_key_locking: bool, | ||
|
|
||
| /// Beacon node API endpoints. The first reachable endpoint is used; | ||
| /// multiple addresses enable fallback. | ||
| pub beacon_node_addrs: Vec<String>, | ||
|
|
||
| /// Timeout for general beacon node requests. | ||
| pub beacon_node_timeout: Duration, | ||
|
|
||
| /// Timeout for beacon node submission (broadcast) requests. | ||
| pub beacon_node_submit_timeout: Duration, | ||
|
|
||
| /// Address the validator API HTTP server binds to. | ||
| pub validator_api_addr: SocketAddr, | ||
|
|
||
| /// Whether the builder API (MEV-boost) is enabled. | ||
| pub builder_api: bool, | ||
|
|
||
| /// Human-readable node nickname, surfaced via the peerinfo protocol. | ||
| pub nickname: String, | ||
|
|
||
| /// Skip cluster lock hash + signature verification. | ||
| pub no_verify: bool, | ||
|
|
||
| /// Execution-layer (eth1) JSON-RPC endpoint, used to verify operator | ||
| /// signatures (including EIP-1271 smart-contract signatures) in the cluster | ||
| /// lock. When `None`, lock verification runs without eth1 and such operator | ||
| /// signatures are not checked. Mirrors Charon's | ||
| /// `--execution-client-rpc-endpoint`. | ||
| pub eth1_endpoint: Option<String>, | ||
|
|
||
| /// Graffiti included in proposed blocks. `None` gives every validator the | ||
| /// default (client) graffiti; a single value applies to all validators; one | ||
| /// value per validator otherwise. Mirrors Charon's `--graffiti`. | ||
| pub graffiti: Option<Vec<String>>, | ||
|
|
||
| /// Disable appending the client version/codex to graffiti. Mirrors Charon's | ||
| /// `--graffiti-disable-client-append`. | ||
| pub graffiti_disable_client_append: bool, | ||
|
|
||
| /// Feature set controlling optional/alpha behaviors (e.g. | ||
| /// `FetchOnlyCommIdx0`, `ChainSplitHalt`). Resolved from the CLI | ||
| /// feature flags (out of scope here). | ||
| pub feature_set: Arc<FeatureSet>, | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lockHashHex := Hex7(cluster.GetInitialMutationHash())instead of&hex::encode(&lock_hash)