openmina_node_invariants/p2p/
states_are_consistent.rs

1use std::collections::BTreeSet;
2
3use node::{p2p::PeerId, ActionKind, ActionWithMeta, Service, Store};
4
5use crate::{Invariant, InvariantResult};
6
7/// Makes sure that:
8/// 1. For WebRTC peers, we have same number of peers in state and service.
9/// 2. TODO: libp2p
10#[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}