Skip to content

Commit 81a0cf3

Browse files
cdiielsiSDartayetElFantasma
authored
feat(l1): admin_addPeer endpoint (#5004)
**Motivation** Include geth's [admin_addPeer](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-admin#admin-addpeer) endpoint since we found it necessary for running eip7934 hive tests with the `ethereum/eels/consume-sync ` simulation. Besides Nethermind, Reth, Besu, Erigon and other execution clients implement it. **Description** This pr introduces the RPC endpoint `admin_addPeer`. Since this endpoint needed to access p2p's generic server used to establish connections, the `init_l1` function was restructure so the genserver is saved within the peer_handler struct and can later be accessed from the `RPCApiContext`. In `admin_addPeer` the connection is tried in a period of 10 seconds waiting 100 milliseconds in between each message casted. This is because connections are asynchronous so to check if the connection with the peer was made we need to wait until it's established. --------- Co-authored-by: SDartayet <[email protected]> Co-authored-by: SDartayet <[email protected]> Co-authored-by: ElFantasma <[email protected]>
1 parent 2f3a86f commit 81a0cf3

File tree

16 files changed

+214
-80
lines changed

16 files changed

+214
-80
lines changed

Cargo.lock

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/ethrex/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ thiserror.workspace = true
5252
itertools = "0.14.0"
5353
url.workspace = true
5454

55+
spawned-rt.workspace = true
56+
spawned-concurrency.workspace = true
57+
5558
# L2 external dependencies
5659

5760
tui-logger = { workspace = true, optional = true }

cmd/ethrex/initializers.rs

Lines changed: 23 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ use ethrex_common::types::Genesis;
1111
use ethrex_config::networks::Network;
1212

1313
use ethrex_metrics::profiling::{FunctionProfilingLayer, initialize_block_processing_profile};
14-
#[cfg(feature = "l2")]
15-
use ethrex_p2p::rlpx::l2::l2_connection::P2PBasedContext;
14+
use ethrex_p2p::rlpx::initiator::RLPxInitiator;
1615
use ethrex_p2p::{
1716
discv4::peer_table::PeerTable,
1817
network::P2PContext,
@@ -193,13 +192,10 @@ pub async fn init_network(
193192
opts: &Options,
194193
network: &Network,
195194
datadir: &Path,
196-
local_p2p_node: Node,
197-
signer: SecretKey,
198195
peer_handler: PeerHandler,
199-
store: Store,
200196
tracker: TaskTracker,
201197
blockchain: Arc<Blockchain>,
202-
#[cfg(feature = "l2")] based_context: Option<P2PBasedContext>,
198+
context: P2PContext,
203199
) {
204200
if opts.dev {
205201
error!("Binary wasn't built with The feature flag `dev` enabled.");
@@ -210,26 +206,6 @@ pub async fn init_network(
210206

211207
let bootnodes = get_bootnodes(opts, network, datadir);
212208

213-
#[cfg(feature = "l2")]
214-
let based_context_arg = based_context;
215-
216-
#[cfg(not(feature = "l2"))]
217-
let based_context_arg = None;
218-
219-
let context = P2PContext::new(
220-
local_p2p_node,
221-
tracker.clone(),
222-
signer,
223-
peer_handler.peer_table.clone(),
224-
store,
225-
blockchain.clone(),
226-
get_client_version(),
227-
based_context_arg,
228-
opts.tx_broadcasting_time_interval,
229-
)
230-
.await
231-
.expect("P2P context could not be created");
232-
233209
ethrex_p2p::start_network(context, bootnodes)
234210
.await
235211
.expect("Network starts");
@@ -439,17 +415,35 @@ pub async fn init_l1(
439415

440416
let local_node_record = get_local_node_record(datadir, &local_p2p_node, &signer);
441417

442-
let peer_handler = PeerHandler::new(PeerTable::spawn(opts.target_peers));
418+
let peer_table = PeerTable::spawn(opts.target_peers);
443419

444420
// TODO: Check every module starts properly.
445421
let tracker = TaskTracker::new();
446422

447423
let cancel_token = tokio_util::sync::CancellationToken::new();
448424

425+
let p2p_context = P2PContext::new(
426+
local_p2p_node.clone(),
427+
tracker.clone(),
428+
signer,
429+
peer_table.clone(),
430+
store.clone(),
431+
blockchain.clone(),
432+
get_client_version(),
433+
None,
434+
opts.tx_broadcasting_time_interval,
435+
)
436+
.await
437+
.expect("P2P context could not be created");
438+
439+
let initiator = RLPxInitiator::spawn(p2p_context.clone()).await;
440+
441+
let peer_handler = PeerHandler::new(peer_table.clone(), initiator);
442+
449443
init_rpc_api(
450444
&opts,
451445
peer_handler.clone(),
452-
local_p2p_node.clone(),
446+
local_p2p_node,
453447
local_node_record.clone(),
454448
store.clone(),
455449
blockchain.clone(),
@@ -471,14 +465,10 @@ pub async fn init_l1(
471465
&opts,
472466
&network,
473467
datadir,
474-
local_p2p_node,
475-
signer,
476468
peer_handler.clone(),
477-
store.clone(),
478469
tracker.clone(),
479470
blockchain.clone(),
480-
#[cfg(feature = "l2")]
481-
None,
471+
p2p_context,
482472
)
483473
.await;
484474
} else {

cmd/ethrex/l2/initializers.rs

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ use ethrex_l2::SequencerConfig;
1515
use ethrex_l2::sequencer::l1_committer::regenerate_head_state;
1616
use ethrex_p2p::{
1717
discv4::peer_table::PeerTable,
18+
network::P2PContext,
1819
peer_handler::PeerHandler,
19-
rlpx::l2::l2_connection::P2PBasedContext,
20+
rlpx::{initiator::RLPxInitiator, l2::l2_connection::P2PBasedContext},
2021
sync_manager::SyncManager,
2122
types::{Node, NodeRecord},
2223
};
@@ -35,7 +36,7 @@ use url::Url;
3536
async fn init_rpc_api(
3637
opts: &L1Options,
3738
l2_opts: &L2Options,
38-
peer_table: PeerTable,
39+
peer_handler: PeerHandler,
3940
local_p2p_node: Node,
4041
local_node_record: NodeRecord,
4142
store: Store,
@@ -46,8 +47,6 @@ async fn init_rpc_api(
4647
log_filter_handler: Option<reload::Handle<EnvFilter, Registry>>,
4748
gas_ceil: Option<u64>,
4849
) {
49-
let peer_handler = PeerHandler::new(peer_table);
50-
5150
init_datadir(&opts.datadir);
5251

5352
// Create SyncManager
@@ -198,18 +197,51 @@ pub async fn init_l2(
198197

199198
let local_node_record = get_local_node_record(&datadir, &local_p2p_node, &signer);
200199

201-
let peer_handler = PeerHandler::new(PeerTable::spawn(opts.node_opts.target_peers));
200+
let peer_table = PeerTable::spawn(opts.node_opts.target_peers);
202201

203202
// TODO: Check every module starts properly.
204203
let tracker = TaskTracker::new();
205204
let mut join_set = JoinSet::new();
206205

206+
let p2p_context = P2PContext::new(
207+
local_p2p_node.clone(),
208+
tracker.clone(),
209+
signer,
210+
peer_table.clone(),
211+
store.clone(),
212+
blockchain.clone(),
213+
get_client_version(),
214+
#[cfg(feature = "l2")]
215+
Some(P2PBasedContext {
216+
store_rollup: rollup_store.clone(),
217+
// TODO: The Web3Signer refactor introduced a limitation where the committer key cannot be accessed directly because the signer could be either Local or Remote.
218+
// The Signer enum cannot be used in the P2PBasedContext struct due to cyclic dependencies between the l2-rpc and p2p crates.
219+
// As a temporary solution, a dummy committer key is used until a proper mechanism to utilize the Signer enum is implemented.
220+
// This should be replaced with the Signer enum once the refactor is complete.
221+
committer_key: Arc::new(
222+
SecretKey::from_slice(
223+
&hex::decode(
224+
"385c546456b6a603a1cfcaa9ec9494ba4832da08dd6bcf4de9a71e4a01b74924",
225+
)
226+
.expect("Invalid committer key"),
227+
)
228+
.expect("Failed to create committer key"),
229+
),
230+
}),
231+
opts.node_opts.tx_broadcasting_time_interval,
232+
)
233+
.await
234+
.expect("P2P context could not be created");
235+
236+
let initiator = RLPxInitiator::spawn(p2p_context.clone()).await;
237+
let peer_handler = PeerHandler::new(PeerTable::spawn(opts.node_opts.target_peers), initiator);
238+
207239
let cancel_token = tokio_util::sync::CancellationToken::new();
208240

209241
init_rpc_api(
210242
&opts.node_opts,
211243
&opts,
212-
peer_handler.peer_table.clone(),
244+
peer_handler.clone(),
213245
local_p2p_node.clone(),
214246
local_node_record.clone(),
215247
store.clone(),
@@ -241,28 +273,10 @@ pub async fn init_l2(
241273
&opts.node_opts,
242274
&network,
243275
&datadir,
244-
local_p2p_node,
245-
signer,
246276
peer_handler.clone(),
247-
store.clone(),
248277
tracker,
249278
blockchain.clone(),
250-
Some(P2PBasedContext {
251-
store_rollup: rollup_store.clone(),
252-
// TODO: The Web3Signer refactor introduced a limitation where the committer key cannot be accessed directly because the signer could be either Local or Remote.
253-
// The Signer enum cannot be used in the P2PBasedContext struct due to cyclic dependencies between the l2-rpc and p2p crates.
254-
// As a temporary solution, a dummy committer key is used until a proper mechanism to utilize the Signer enum is implemented.
255-
// This should be replaced with the Signer enum once the refactor is complete.
256-
committer_key: Arc::new(
257-
SecretKey::from_slice(
258-
&hex::decode(
259-
"385c546456b6a603a1cfcaa9ec9494ba4832da08dd6bcf4de9a71e4a01b74924",
260-
)
261-
.expect("Invalid committer key"),
262-
)
263-
.expect("Failed to create committer key"),
264-
),
265-
}),
279+
p2p_context,
266280
)
267281
.await;
268282
} else {

crates/networking/p2p/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ default = ["c-kzg"]
6262
c-kzg = ["ethrex-blockchain/c-kzg", "ethrex-common/c-kzg"]
6363
sync-test = []
6464
l2 = ["dep:ethrex-storage-rollup"]
65+
test-utils = []
6566

6667
[lints.clippy]
6768
unwrap_used = "deny"

crates/networking/p2p/network.rs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ use crate::{
1111
metrics::METRICS,
1212
rlpx::{
1313
connection::server::{PeerConnBroadcastSender, PeerConnection},
14-
initiator::RLPxInitiator,
1514
message::Message,
1615
p2p::SUPPORTED_SNAP_CAPABILITIES,
1716
},
@@ -94,6 +93,36 @@ impl P2PContext {
9493
tx_broadcaster,
9594
})
9695
}
96+
97+
#[cfg(any(test, feature = "test-utils"))]
98+
/// Creates a dummy P2PContext for tests
99+
/// This should only be used in tests as it won't be able to connect to the p2p network
100+
pub async fn dummy(peer_table: PeerTable) -> P2PContext {
101+
use ethrex_storage::EngineType;
102+
103+
let storage = Store::new("./temp", EngineType::InMemory).expect("Failed to create Store");
104+
let blockchain: Arc<Blockchain> = Arc::new(Blockchain::default_with_store(storage.clone()));
105+
let local_node = Node::from_enode_url(
106+
"enode://d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666@18.138.108.67:30303",
107+
).expect("Bad enode url");
108+
let (channel_broadcast_send_end, _) =
109+
tokio::sync::broadcast::channel::<(tokio::task::Id, Arc<Message>)>(100000);
110+
P2PContext {
111+
tracker: TaskTracker::default(),
112+
signer: SecretKey::from_byte_array(&[0xcd; 32]).expect("32 bytes, within curve order"),
113+
table: peer_table.clone(),
114+
storage,
115+
blockchain: blockchain.clone(),
116+
broadcast: channel_broadcast_send_end,
117+
local_node: local_node.clone(),
118+
client_version: "".to_string(),
119+
#[cfg(feature = "l2")]
120+
based_context: None,
121+
tx_broadcaster: TxBroadcaster::spawn(peer_table.clone(), blockchain, 1000)
122+
.await
123+
.expect("Failed to spawn tx broadcaster"),
124+
}
125+
}
97126
}
98127

99128
#[derive(Debug, thiserror::Error)]
@@ -123,8 +152,6 @@ pub async fn start_network(context: P2PContext, bootnodes: Vec<Node>) -> Result<
123152
error!("Failed to start discovery server: {e}");
124153
})?;
125154

126-
RLPxInitiator::spawn(context.clone()).await;
127-
128155
context.tracker.spawn(serve_p2p_requests(context.clone()));
129156

130157
Ok(())

crates/networking/p2p/peer_handler.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
#[cfg(any(test, feature = "test-utils"))]
2+
use crate::discv4::peer_table::TARGET_PEERS;
3+
use crate::rlpx::initiator::RLPxInitiator;
14
use crate::{
2-
discv4::peer_table::{PeerData, PeerTable, PeerTableError, TARGET_PEERS},
5+
discv4::peer_table::{PeerData, PeerTable, PeerTableError},
36
metrics::{CurrentStepValue, METRICS},
47
rlpx::{
58
connection::server::PeerConnection,
@@ -34,6 +37,7 @@ use ethrex_rlp::{decode::RLPDecode, encode::RLPEncode};
3437
use ethrex_storage::Store;
3538
use ethrex_trie::Nibbles;
3639
use ethrex_trie::{Node, verify_range};
40+
use spawned_concurrency::tasks::GenServerHandle;
3741
use std::{
3842
collections::{BTreeMap, HashMap, HashSet, VecDeque},
3943
io::ErrorKind,
@@ -68,6 +72,7 @@ pub const MAX_BLOCK_BODIES_TO_REQUEST: usize = 128;
6872
#[derive(Debug, Clone)]
6973
pub struct PeerHandler {
7074
pub peer_table: PeerTable,
75+
pub initiator: GenServerHandle<RLPxInitiator>,
7176
}
7277

7378
pub enum BlockRequestOrder {
@@ -142,14 +147,19 @@ async fn ask_peer_head_number(
142147
}
143148

144149
impl PeerHandler {
145-
pub fn new(peer_table: PeerTable) -> PeerHandler {
146-
Self { peer_table }
150+
pub fn new(peer_table: PeerTable, initiator: GenServerHandle<RLPxInitiator>) -> PeerHandler {
151+
Self {
152+
peer_table,
153+
initiator,
154+
}
147155
}
148156

157+
#[cfg(any(test, feature = "test-utils"))]
149158
/// Creates a dummy PeerHandler for tests where interacting with peers is not needed
150159
/// This should only be used in tests as it won't be able to interact with the node's connected peers
151-
pub fn dummy() -> PeerHandler {
152-
PeerHandler::new(PeerTable::spawn(TARGET_PEERS))
160+
pub async fn dummy() -> PeerHandler {
161+
let peer_table = PeerTable::spawn(TARGET_PEERS);
162+
PeerHandler::new(peer_table.clone(), RLPxInitiator::dummy(peer_table).await)
153163
}
154164

155165
async fn make_request(

0 commit comments

Comments
 (0)