p2p/channels/best_tip/
p2p_channels_best_tip_actions.rs

1use openmina_core::{block::ArcBlockWithHash, ActionEvent};
2use serde::{Deserialize, Serialize};
3
4use crate::{
5    channels::{best_tip::P2pChannelsBestTipState, P2pChannelsAction},
6    P2pState, PeerId,
7};
8
9use super::BestTipPropagationState;
10
11#[derive(Debug, Clone, Serialize, Deserialize, ActionEvent)]
12#[action_event(fields(display(peer_id), best_tip = display(&best_tip.hash)))]
13pub enum P2pChannelsBestTipAction {
14    /// Initialize best tip channel.
15    Init {
16        peer_id: PeerId,
17    },
18    Pending {
19        peer_id: PeerId,
20    },
21    /// Best tip channel is ready.
22    Ready {
23        peer_id: PeerId,
24    },
25    /// Send best tip request.
26    RequestSend {
27        peer_id: PeerId,
28    },
29    /// Best tip response is received.
30    Received {
31        peer_id: PeerId,
32        best_tip: ArcBlockWithHash,
33    },
34    /// Best tip request is received.
35    RequestReceived {
36        peer_id: PeerId,
37    },
38    /// Best tip response is sent.
39    ResponseSend {
40        peer_id: PeerId,
41        best_tip: ArcBlockWithHash,
42    },
43}
44
45impl P2pChannelsBestTipAction {
46    pub fn peer_id(&self) -> &PeerId {
47        match self {
48            Self::Init { peer_id }
49            | Self::Pending { peer_id }
50            | Self::Ready { peer_id }
51            | Self::RequestSend { peer_id }
52            | Self::Received { peer_id, .. }
53            | Self::RequestReceived { peer_id }
54            | Self::ResponseSend { peer_id, .. } => peer_id,
55        }
56    }
57}
58
59impl redux::EnablingCondition<P2pState> for P2pChannelsBestTipAction {
60    fn is_enabled(&self, state: &P2pState, _time: redux::Timestamp) -> bool {
61        match self {
62            P2pChannelsBestTipAction::Init { peer_id } => state
63                .get_ready_peer(peer_id)
64                .is_some_and(|p| matches!(&p.channels.best_tip, P2pChannelsBestTipState::Enabled)),
65            P2pChannelsBestTipAction::Pending { peer_id } => {
66                state.get_ready_peer(peer_id).is_some_and(|p| {
67                    matches!(&p.channels.best_tip, P2pChannelsBestTipState::Init { .. })
68                })
69            }
70            P2pChannelsBestTipAction::Ready { peer_id } => {
71                state.get_ready_peer(peer_id).is_some_and(|p| {
72                    matches!(
73                        &p.channels.best_tip,
74                        P2pChannelsBestTipState::Pending { .. }
75                    )
76                })
77            }
78            P2pChannelsBestTipAction::RequestSend { peer_id } => state
79                .get_ready_peer(peer_id)
80                .is_some_and(|p| match &p.channels.best_tip {
81                    P2pChannelsBestTipState::Ready { local, .. } => matches!(
82                        local,
83                        BestTipPropagationState::WaitingForRequest { .. }
84                            | BestTipPropagationState::Responded { .. },
85                    ),
86                    _ => false,
87                }),
88            P2pChannelsBestTipAction::Received { peer_id, .. } => {
89                // TODO(binier): use consensus to enforce that peer doesn't send
90                // us inferior block than it has in the past.
91                state
92                    .get_ready_peer(peer_id)
93                    .is_some_and(|p| match &p.channels.best_tip {
94                        P2pChannelsBestTipState::Ready { local, .. } => {
95                            matches!(local, BestTipPropagationState::Requested { .. })
96                        }
97                        _ => false,
98                    })
99            }
100            P2pChannelsBestTipAction::RequestReceived { peer_id } => state
101                .get_ready_peer(peer_id)
102                .is_some_and(|p| match &p.channels.best_tip {
103                    P2pChannelsBestTipState::Ready { remote, .. } => matches!(
104                        remote,
105                        BestTipPropagationState::WaitingForRequest { .. }
106                            | BestTipPropagationState::Responded { .. }
107                    ),
108                    _ => false,
109                }),
110            P2pChannelsBestTipAction::ResponseSend { peer_id, best_tip } => state
111                .get_ready_peer(peer_id)
112                .is_some_and(|p| match &p.channels.best_tip {
113                    P2pChannelsBestTipState::Ready {
114                        remote, last_sent, ..
115                    } => {
116                        if !matches!(remote, BestTipPropagationState::Requested { .. }) {
117                            return false;
118                        }
119                        last_sent
120                            .as_ref()
121                            .is_none_or(|sent| sent.hash != best_tip.hash)
122                    }
123                    _ => false,
124                }),
125        }
126    }
127}
128
129impl From<P2pChannelsBestTipAction> for crate::P2pAction {
130    fn from(action: P2pChannelsBestTipAction) -> Self {
131        Self::Channels(P2pChannelsAction::BestTip(action))
132    }
133}