openmina_node_invariants/p2p/
states_are_consistent.rs1use std::collections::BTreeSet;
2
3use node::{p2p::PeerId, ActionKind, ActionWithMeta, Service, Store};
4
5use crate::{Invariant, InvariantResult};
6
7#[derive(documented::Documented, Default, Clone, Copy)]
11pub struct P2pStatesAreConsistent;
12
13impl Invariant for P2pStatesAreConsistent {
14 type InternalState = ();
15 fn triggers(&self) -> &[ActionKind] {
16 &[ActionKind::P2pPeerReady, ActionKind::P2pDisconnectionFinish]
17 }
18
19 fn check<S: Service>(
20 self,
21 _: &mut Self::InternalState,
22 store: &Store<S>,
23 _action: &ActionWithMeta,
24 ) -> InvariantResult {
25 if let Some((missing_connections, extra_connections)) =
26 self.webrtc_peer_inconsistencies(store)
27 {
28 return InvariantResult::Violation(format!("WebRTC inconsistency! missing_connections:\n{missing_connections:?}\nextra_connections:\n{extra_connections:?}"));
29 }
30
31 InvariantResult::Ok
32 }
33}
34
35impl P2pStatesAreConsistent {
36 fn webrtc_peer_inconsistencies<S: Service>(
37 self,
38 store: &Store<S>,
39 ) -> Option<(BTreeSet<PeerId>, BTreeSet<PeerId>)> {
40 if store.service.is_replay() {
41 return None;
42 }
43 let p2p_state = store.state().p2p.ready()?;
44 let mut connections = store.service.connections();
45 let peers = p2p_state
46 .peers
47 .iter()
48 .filter(|(_, s)| !s.is_libp2p() && s.status.is_connected_or_connecting())
49 .map(|(peer_id, _)| *peer_id)
50 .filter(|peer_id| !connections.remove(peer_id))
51 .collect::<BTreeSet<_>>();
52
53 if !peers.is_empty() || !connections.is_empty() {
54 return Some((peers, connections));
55 }
56 None
57 }
58}