Skip to content

Commit 8fd8403

Browse files
committed
Added bad node identify test
1 parent 9aabaa2 commit 8fd8403

File tree

3 files changed

+216
-9
lines changed

3 files changed

+216
-9
lines changed

p2p/src/network/identify/p2p_network_identify_protocol.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ impl TryFrom<super::pb::Identify> for P2pNetworkIdentify {
7777
}
7878
}
7979

80+
impl P2pNetworkIdentify {
81+
pub fn to_proto_message(&self) -> super::pb::Identify {
82+
super::pb::Identify::from(self)
83+
}
84+
}
85+
8086
impl<'a> From<&'a P2pNetworkIdentify> for super::pb::Identify {
8187
fn from(value: &'a P2pNetworkIdentify) -> Self {
8288
Self {

p2p/src/network/kad/p2p_network_kad_internals.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ use serde::{Deserialize, Serialize};
1010
use sha2::{Digest, Sha256};
1111

1212
use crate::{
13-
ConnectionType, P2pNetworkKademliaMultiaddrError, P2pNetworkKademliaPeerIdError, PeerId,
13+
socket_addr_try_from_multiaddr, ConnectionType, P2pNetworkKademliaMultiaddrError,
14+
P2pNetworkKademliaPeerIdError, PeerId,
1415
};
1516

1617
use super::CID;
@@ -245,10 +246,16 @@ impl<const K: usize> P2pNetworkKadRoutingTable<K> {
245246
/// K-bucket is full and cannot be split), then `Err(_)` value is returned.
246247
///
247248
/// TODO: should it be just another variant in `Ok(_)`?
249+
///
250+
/// Filters `entry.addrs` for supported addresses if non of addresses are supported returns `Err(_)`
248251
pub fn insert(
249252
&mut self,
250-
entry: P2pNetworkKadEntry,
253+
mut entry: P2pNetworkKadEntry,
251254
) -> Result<bool, P2pNetworkKadRoutingTableInsertError> {
255+
entry.filter_addrs();
256+
if entry.addrs.is_empty() {
257+
return Err(P2pNetworkKadRoutingTableInsertError);
258+
}
252259
// distance to this node
253260
let dist = &self.this_key - &entry.key;
254261

@@ -371,6 +378,18 @@ impl P2pNetworkKadEntry {
371378
pub fn dist(&self, other: &P2pNetworkKadEntry) -> P2pNetworkKadDist {
372379
&self.key - &other.key
373380
}
381+
382+
pub fn filter_addrs(&mut self) {
383+
let addrs = std::mem::take(&mut self.addrs);
384+
self.addrs = addrs
385+
.into_iter()
386+
.filter_map(|multiaddr| {
387+
socket_addr_try_from_multiaddr(&multiaddr)
388+
.is_ok()
389+
.then_some(multiaddr)
390+
})
391+
.collect()
392+
}
374393
}
375394

376395
#[derive(Clone, Debug, Serialize, PartialEq, Deserialize, thiserror::Error)]
@@ -573,6 +592,7 @@ mod tests {
573592
use std::collections::BTreeSet;
574593

575594
use crypto_bigint::{Random, U256};
595+
use multiaddr::multiaddr;
576596

577597
use crate::{identity::SecretKey, PeerId, CID};
578598

@@ -601,7 +621,7 @@ mod tests {
601621
P2pNetworkKadEntry {
602622
key,
603623
peer_id,
604-
addrs: vec![],
624+
addrs: vec![multiaddr!(Ip4([0; 4]), Tcp(1000_u16))],
605625
connection: super::ConnectionType::Connected,
606626
}
607627
}
@@ -611,7 +631,7 @@ mod tests {
611631
P2pNetworkKadEntry {
612632
key,
613633
peer_id,
614-
addrs: vec![],
634+
addrs: vec![multiaddr!(Ip4([0; 4]), Tcp(1000_u16))],
615635
connection: super::ConnectionType::Connected,
616636
}
617637
}

p2p/tests/identify.rs

Lines changed: 186 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,31 @@
1-
use std::{collections::BTreeSet, time::Duration};
1+
use std::{
2+
collections::{BTreeSet, HashSet},
3+
time::Duration,
4+
};
25

3-
use multiaddr::Multiaddr;
4-
use p2p::PeerId;
6+
use multiaddr::{multiaddr, Multiaddr};
7+
use p2p::{
8+
identity::SecretKey,
9+
network::identify::{
10+
stream::P2pNetworkIdentifyStreamState, P2pNetworkIdentify, P2pNetworkIdentifyAction,
11+
P2pNetworkIdentifyStreamAction,
12+
},
13+
p2p_effects, p2p_timeout_effects,
14+
token::{self, DiscoveryAlgorithm},
15+
Data, P2pAction, P2pNetworkAction, P2pNetworkYamuxAction, PeerId,
16+
};
517
use p2p_testing::{
6-
cluster::{Cluster, ClusterBuilder, ClusterEvent},
7-
event::RustNodeEvent,
18+
cluster::{Cluster, ClusterBuilder, ClusterEvent, Listener},
19+
event::{event_mapper_effect, RustNodeEvent},
820
futures::TryStreamExt,
921
predicates::{async_fn, listener_is_ready, peer_is_connected},
22+
redux::{Action, State},
1023
rust_node::{RustNodeConfig, RustNodeId},
24+
service::ClusterService,
1125
stream::ClusterStreamExt,
26+
test_node::TestNode,
1227
};
28+
use redux::{ActionWithMeta, Store};
1329

1430
#[tokio::test]
1531
async fn rust_node_to_rust_node() -> anyhow::Result<()> {
@@ -85,6 +101,171 @@ async fn rust_node_to_rust_node() -> anyhow::Result<()> {
85101
Ok(())
86102
}
87103

104+
#[tokio::test]
105+
/// Test that even if bad node spams many different listen_addrs we don't end up with duplicates
106+
async fn test_bad_node() -> anyhow::Result<()> {
107+
let mut cluster = ClusterBuilder::new()
108+
.ports_with_len(100)
109+
.idle_duration(Duration::from_millis(100))
110+
.start()
111+
.await?;
112+
113+
let bad_node = cluster.add_rust_node(
114+
RustNodeConfig::default()
115+
.with_discovery(true)
116+
.with_override(bad_node_effects),
117+
)?;
118+
let bad_node_peer_id = cluster.rust_node(bad_node).peer_id();
119+
let bad_node_port = cluster.rust_node(bad_node).libp2p_port();
120+
121+
let node = cluster.add_rust_node(
122+
RustNodeConfig::default()
123+
.with_discovery(true)
124+
.with_initial_peers([Listener::Rust(bad_node)]),
125+
)?;
126+
127+
let mut not_identified = BTreeSet::from_iter([(node, bad_node_peer_id)]);
128+
wait_for_identify(&mut cluster, &mut not_identified, Duration::from_secs(10)).await?;
129+
130+
let routing_table = &cluster
131+
.rust_node(node)
132+
.state()
133+
.network
134+
.scheduler
135+
.discovery_state()
136+
.expect("State must be initialized")
137+
.routing_table;
138+
139+
let bad_peer_entry = routing_table
140+
.look_up(&bad_node_peer_id.into())
141+
.expect("Node not found");
142+
143+
let bad_peer_addresses = bad_peer_entry
144+
.addrs
145+
.iter()
146+
.map(Clone::clone)
147+
.collect::<HashSet<_>>();
148+
149+
let expected_addrs = [
150+
multiaddr!(Ip4([127, 0, 0, 1]), Tcp(bad_node_port)),
151+
multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16)),
152+
multiaddr!(Ip6([0; 16]), Tcp(10500u16)),
153+
multiaddr!(Ip6([1; 16]), Tcp(10500u16)),
154+
]
155+
.into_iter()
156+
.collect::<HashSet<_>>();
157+
158+
assert_eq!(bad_peer_addresses, expected_addrs);
159+
160+
Ok(())
161+
}
162+
163+
fn bad_node_effects(
164+
store: &mut Store<State, ClusterService, Action>,
165+
action: ActionWithMeta<Action>,
166+
) {
167+
{
168+
let (action, meta) = action.split();
169+
match action {
170+
Action::P2p(a) => {
171+
match a.clone() {
172+
P2pAction::Network(P2pNetworkAction::Identify(
173+
P2pNetworkIdentifyAction::Stream(P2pNetworkIdentifyStreamAction::New {
174+
addr,
175+
peer_id,
176+
stream_id,
177+
..
178+
}),
179+
)) => {
180+
let state = store
181+
.state()
182+
.state()
183+
.network
184+
.scheduler
185+
.identify_state
186+
.find_identify_stream_state(&peer_id, &stream_id)
187+
.expect("Unable to find identify stream");
188+
189+
if let P2pNetworkIdentifyStreamState::SendIdentify = state {
190+
let listen_addrs = vec![
191+
multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16)),
192+
multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16)),
193+
multiaddr!(Ip6([0; 16]), Tcp(10500u16)),
194+
multiaddr!(Ip6([0; 16]), Tcp(10500u16)),
195+
multiaddr!(Ip6([1; 16]), Tcp(10500u16)),
196+
multiaddr!(Ip6([1; 16]), Tcp(10500u16)),
197+
multiaddr!(Dns("domain.com"), Tcp(10530u16)),
198+
multiaddr!(Dns("domain.com"), Tcp(10530u16)),
199+
multiaddr!(Dns("domain.com"), Tcp(10530u16)),
200+
multiaddr!(Dns("domain.com"), Tcp(10530u16)),
201+
multiaddr!(Unix("domain.com")),
202+
multiaddr!(Https),
203+
];
204+
205+
let public_key = Some(SecretKey::rand().public_key());
206+
207+
let protocols = vec![
208+
token::StreamKind::Identify(
209+
token::IdentifyAlgorithm::Identify1_0_0,
210+
),
211+
token::StreamKind::Broadcast(
212+
p2p::token::BroadcastAlgorithm::Meshsub1_1_0,
213+
),
214+
p2p::token::StreamKind::Rpc(token::RpcAlgorithm::Rpc0_0_1),
215+
p2p::token::StreamKind::Discovery(
216+
DiscoveryAlgorithm::Kademlia1_0_0,
217+
),
218+
];
219+
220+
let identify_msg = P2pNetworkIdentify {
221+
protocol_version: Some("ipfs/0.1.0".to_string()),
222+
agent_version: Some("openmina".to_owned()),
223+
public_key,
224+
listen_addrs,
225+
observed_addr: None,
226+
protocols,
227+
};
228+
229+
let mut out = Vec::new();
230+
let identify_msg_proto = identify_msg.to_proto_message();
231+
232+
prost::Message::encode_length_delimited(&identify_msg_proto, &mut out)
233+
.expect("Error converting message");
234+
235+
store.dispatch(Action::P2p(
236+
P2pNetworkYamuxAction::OutgoingData {
237+
addr,
238+
stream_id,
239+
data: Data(out.into_boxed_slice()),
240+
flags: Default::default(),
241+
}
242+
.into(),
243+
));
244+
245+
store.dispatch(Action::P2p(
246+
P2pNetworkIdentifyStreamAction::Close {
247+
addr,
248+
peer_id,
249+
stream_id,
250+
}
251+
.into(),
252+
));
253+
}
254+
}
255+
256+
a => {
257+
p2p_effects(store, meta.with_action(a.clone()));
258+
}
259+
}
260+
event_mapper_effect(store, a);
261+
}
262+
Action::Idle(_) => {
263+
p2p_timeout_effects(store, &meta);
264+
}
265+
};
266+
}
267+
}
268+
88269
async fn wait_for_identify(
89270
cluster: &mut Cluster,
90271
nodes_peers: &mut BTreeSet<(RustNodeId, PeerId)>,

0 commit comments

Comments
 (0)