node/logger/
logger_effects.rs

1use openmina_core::log::{
2    inner::{
3        field::{display, DisplayValue},
4        Value,
5    },
6    time_to_str, ActionEvent, EventContext,
7};
8use p2p::{
9    connection::P2pConnectionEffectfulAction, P2pNetworkConnectionError, P2pNetworkSchedulerAction,
10    PeerId,
11};
12
13use crate::{
14    p2p::{
15        channels::P2pChannelsAction, connection::P2pConnectionAction, network::P2pNetworkAction,
16        P2pAction,
17    },
18    snark::SnarkAction,
19    transition_frontier::candidate::TransitionFrontierCandidateAction,
20    Action, ActionWithMetaRef, BlockProducerAction, Service, Store, TransitionFrontierAction,
21};
22
23struct ActionLoggerContext {
24    time: redux::Timestamp,
25    time_str: String,
26    node_id: DisplayValue<PeerId>,
27    log_node_id: bool,
28}
29
30impl ActionLoggerContext {
31    fn new(time: redux::Timestamp, node_id: PeerId, log_node_id: bool) -> Self {
32        ActionLoggerContext {
33            time,
34            time_str: time_to_str(time),
35            node_id: display(node_id),
36            log_node_id,
37        }
38    }
39}
40
41impl EventContext for ActionLoggerContext {
42    fn timestamp(&self) -> redux::Timestamp {
43        self.time
44    }
45
46    fn time(&self) -> &'_ dyn Value {
47        &self.time_str
48    }
49
50    fn log_node_id(&self) -> bool {
51        self.log_node_id
52    }
53
54    fn node_id(&self) -> &'_ dyn Value {
55        &self.node_id
56    }
57}
58
59pub fn logger_effects<S: Service>(store: &Store<S>, action: ActionWithMetaRef<'_>) {
60    let (action, meta) = action.split();
61    let context = ActionLoggerContext::new(
62        meta.time(),
63        store.state().p2p.my_id(),
64        store.state().should_log_node_id(),
65    );
66
67    match action {
68        Action::P2p(action) => match action {
69            P2pAction::Initialization(action) => action.action_event(&context),
70            P2pAction::Connection(action) => match action {
71                P2pConnectionAction::Outgoing(action) => action.action_event(&context),
72                P2pConnectionAction::Incoming(action) => action.action_event(&context),
73            },
74            P2pAction::Disconnection(action) => action.action_event(&context),
75            P2pAction::Identify(action) => action.action_event(&context),
76            P2pAction::Channels(action) => match action {
77                P2pChannelsAction::MessageReceived(action) => action.action_event(&context),
78                P2pChannelsAction::SignalingDiscovery(action) => action.action_event(&context),
79                P2pChannelsAction::SignalingExchange(action) => action.action_event(&context),
80                P2pChannelsAction::BestTip(action) => action.action_event(&context),
81                P2pChannelsAction::Transaction(action) => action.action_event(&context),
82                P2pChannelsAction::Snark(action) => action.action_event(&context),
83                P2pChannelsAction::SnarkJobCommitment(action) => action.action_event(&context),
84                P2pChannelsAction::Rpc(action) => action.action_event(&context),
85                P2pChannelsAction::StreamingRpc(action) => action.action_event(&context),
86            },
87            P2pAction::Peer(action) => action.action_event(&context),
88            P2pAction::Network(action) => match action {
89                P2pNetworkAction::Scheduler(action) => match action {
90                    // MioErrors in scheduler are logged using debug instead of warn, to prevent spam
91                    P2pNetworkSchedulerAction::Error {
92                        error: P2pNetworkConnectionError::MioError(summary),
93                        addr,
94                    } => {
95                        openmina_core::action_debug!(
96                            context,
97                            kind = "P2pNetworkSchedulerError",
98                            summary = display(summary),
99                            addr = display(addr)
100                        );
101                    }
102                    action => action.action_event(&context),
103                },
104                P2pNetworkAction::Pnet(action) => action.action_event(&context),
105                P2pNetworkAction::Select(action) => action.action_event(&context),
106                P2pNetworkAction::Noise(action) => action.action_event(&context),
107                P2pNetworkAction::Yamux(action) => action.action_event(&context),
108                P2pNetworkAction::Rpc(action) => action.action_event(&context),
109                P2pNetworkAction::Kad(action) => action.action_event(&context),
110                P2pNetworkAction::Pubsub(action) => action.action_event(&context),
111                P2pNetworkAction::Identify(action) => action.action_event(&context),
112            },
113        },
114        Action::P2pEffectful(action) => match action {
115            p2p::P2pEffectfulAction::Channels(action) => action.action_event(&context),
116            p2p::P2pEffectfulAction::Connection(action) => match action {
117                P2pConnectionEffectfulAction::Outgoing(action) => action.action_event(&context),
118                P2pConnectionEffectfulAction::Incoming(action) => action.action_event(&context),
119            },
120            p2p::P2pEffectfulAction::Disconnection(action) => action.action_event(&context),
121            p2p::P2pEffectfulAction::Network(action) => action.action_event(&context),
122            p2p::P2pEffectfulAction::Initialize => {}
123        },
124        Action::ExternalSnarkWorker(action) => action.action_event(&context),
125        Action::SnarkPool(action) => action.action_event(&context),
126        Action::Snark(SnarkAction::WorkVerify(a)) => a.action_event(&context),
127        Action::Snark(SnarkAction::UserCommandVerify(a)) => a.action_event(&context),
128        Action::TransitionFrontier(a) => match a {
129            TransitionFrontierAction::Candidate(
130                TransitionFrontierCandidateAction::BlockReceived { block, chain_proof },
131            ) => {
132                openmina_core::action_info!(
133                    context,
134                    kind = action.kind().to_string(),
135                    summary = "candidate block received",
136                    block_hash = block.hash().to_string(),
137                    block_height = block.height(),
138                    has_chain_proof = chain_proof.is_some(),
139                );
140            }
141            TransitionFrontierAction::Synced { .. } => {
142                let tip = store.state().transition_frontier.best_tip().unwrap();
143
144                if store.state().block_producer.is_produced_by_me(tip) {
145                    openmina_core::action_info!(
146                        context,
147                        kind = "BlockProducerBlockIntegrated",
148                        summary = "produced block integrated into frontier",
149                        block_hash = tip.hash().to_string(),
150                        block_height = tip.height(),
151                    );
152                }
153
154                openmina_core::action_info!(
155                    context,
156                    kind = action.kind().to_string(),
157                    summary = "transition frontier synced",
158                    block_hash = tip.hash().to_string(),
159                    block_height = tip.height(),
160                );
161            }
162            TransitionFrontierAction::SyncFailed { best_tip, error } => {
163                openmina_core::action_error!(
164                    context,
165                    kind = action.kind().to_string(),
166                    summary = "transition frontier failed to sync",
167                    block_hash = best_tip.hash().to_string(),
168                    block_height = best_tip.height(),
169                    error = error.to_string(),
170                );
171            }
172            a => a.action_event(&context),
173        },
174        Action::BlockProducer(a) => match a {
175            BlockProducerAction::BlockProduced => {
176                let block = store.state().block_producer.produced_block().unwrap();
177                openmina_core::action_info!(
178                    context,
179                    kind = action.kind().to_string(),
180                    summary = "produced a block",
181                    block_hash = block.hash().to_string(),
182                    block_height = block.height(),
183                );
184            }
185            BlockProducerAction::BlockInjected => {
186                let block = store.state().transition_frontier.sync.best_tip().unwrap();
187                openmina_core::action_info!(
188                    context,
189                    kind = action.kind().to_string(),
190                    summary = "produced block injected",
191                    block_hash = block.hash().to_string(),
192                    block_height = block.height(),
193                );
194            }
195            a => a.action_event(&context),
196        },
197        Action::Rpc(a) => a.action_event(&context),
198        Action::TransactionPool(a) => a.action_event(&context),
199        _ => {}
200    }
201}