From 1e420c5e0cf895c3195d0bc4b6935a2c34bcf3ff Mon Sep 17 00:00:00 2001 From: Mimir Date: Wed, 2 Oct 2024 11:26:05 +0200 Subject: [PATCH 1/4] Ported timeouts --- node/src/effects.rs | 3 - node/src/reducer.rs | 14 +- p2p/src/p2p_effects.rs | 272 +----------------------------------- p2p/src/p2p_reducer.rs | 278 ++++++++++++++++++++++++++++++++++++- p2p/testing/src/cluster.rs | 31 +++-- p2p/tests/identify.rs | 4 +- p2p/tests/kademlia.rs | 6 +- 7 files changed, 313 insertions(+), 295 deletions(-) diff --git a/node/src/effects.rs b/node/src/effects.rs index 6b0b3b4fa0..2ea6f94df6 100644 --- a/node/src/effects.rs +++ b/node/src/effects.rs @@ -1,5 +1,4 @@ use openmina_core::log::system_time; -use p2p::p2p_timeout_effects; use crate::block_producer::{block_producer_effects, BlockProducerAction}; use crate::event_source::event_source_effects; @@ -37,8 +36,6 @@ pub fn effects(store: &mut Store, action: ActionWithMeta) { store.dispatch(ExternalSnarkWorkerAction::Start); if store.state().p2p.ready().is_some() { - p2p_timeout_effects(store, &meta); - p2p_request_best_tip_if_needed(store); p2p_request_transactions_if_needed(store); p2p_request_snarks_if_needed(store); diff --git a/node/src/reducer.rs b/node/src/reducer.rs index 92538be208..885c09c2ef 100644 --- a/node/src/reducer.rs +++ b/node/src/reducer.rs @@ -1,5 +1,5 @@ -use openmina_core::{error, Substate}; -use p2p::{P2pAction, P2pInitializeAction}; +use openmina_core::{bug_condition, error, Substate}; +use p2p::{P2pAction, P2pInitializeAction, P2pState}; use crate::{Action, ActionWithMeta, EventSourceAction, P2p, State}; @@ -10,7 +10,15 @@ pub fn reducer( ) { let meta = action.meta().clone(); match action.action() { - Action::CheckTimeouts(_) => {} + Action::CheckTimeouts(_) => { + if state.p2p.ready().is_some() { + if let Err(error) = + P2pState::p2p_timeout_dispatch(Substate::new(state, dispatcher), &meta) + { + bug_condition!("{}", error); + }; + } + } Action::EventSource(EventSourceAction::NewEvent { .. }) => {} Action::EventSource(_) => {} diff --git a/p2p/src/p2p_effects.rs b/p2p/src/p2p_effects.rs index 506387ab03..dad83e8c6d 100644 --- a/p2p/src/p2p_effects.rs +++ b/p2p/src/p2p_effects.rs @@ -1,274 +1,8 @@ -use openmina_core::bug_condition; -use redux::{ActionMeta, ActionWithMeta}; - -use crate::{ - channels::P2pChannelsEffectfulAction, - connection::{outgoing::P2pConnectionOutgoingAction, P2pConnectionEffectfulAction}, - P2pAction, P2pStore, -}; -#[cfg(feature = "p2p-libp2p")] use crate::{ - P2pNetworkKadKey, P2pNetworkKademliaAction, P2pNetworkPnetAction, P2pNetworkSelectAction, - PeerId, + channels::P2pChannelsEffectfulAction, connection::P2pConnectionEffectfulAction, P2pAction, + P2pStore, }; - -pub fn p2p_timeout_effects(store: &mut Store, meta: &ActionMeta) -where - Store: P2pStore, -{ - p2p_connection_timeouts(store, meta); - store.dispatch(P2pConnectionOutgoingAction::RandomInit); - - p2p_try_reconnect_disconnected_peers(store, meta.time()); - - #[cfg(feature = "p2p-libp2p")] - p2p_pnet_timeouts(store, meta); - - p2p_discovery(store, meta); - - #[cfg(feature = "p2p-libp2p")] - p2p_select_timeouts(store, meta); - #[cfg(feature = "p2p-libp2p")] - p2p_rpc_heartbeats(store, meta); - - let state = store.state(); - for (peer_id, id, is_streaming) in state.peer_rpc_timeouts(meta.time()) { - if !is_streaming { - store.dispatch(crate::channels::rpc::P2pChannelsRpcAction::Timeout { peer_id, id }); - } else { - store.dispatch( - crate::channels::streaming_rpc::P2pChannelsStreamingRpcAction::Timeout { - peer_id, - id, - }, - ); - } - } -} - -#[cfg(feature = "p2p-libp2p")] -fn p2p_pnet_timeouts(store: &mut Store, meta: &ActionMeta) -where - Store: P2pStore, -{ - let now = meta.time(); - let timeouts = &store.state().config.timeouts; - let pnet_timeouts: Vec<_> = store - .state() - .network - .scheduler - .connections - .iter() - .filter_map(|(sock_addr, state)| { - if state.pnet.is_timed_out(now, timeouts) { - Some(*sock_addr) - } else { - None - } - }) - .collect(); - - for addr in pnet_timeouts { - store.dispatch(P2pNetworkPnetAction::Timeout { addr }); - } -} - -#[cfg(feature = "p2p-libp2p")] -fn p2p_select_timeouts(store: &mut Store, meta: &ActionMeta) -where - Store: P2pStore, -{ - let now = meta.time(); - let timeouts = &store.state().config.timeouts; - let select_auth_timeouts: Vec<_> = store - .state() - .network - .scheduler - .connections - .iter() - .filter_map(|(sock_addr, state)| { - if state.select_auth.is_timed_out(now, timeouts) { - Some(*sock_addr) - } else { - None - } - }) - .collect(); - - let select_mux_timeouts: Vec<_> = store - .state() - .network - .scheduler - .connections - .iter() - .filter_map(|(sock_addr, state)| { - if state.select_mux.is_timed_out(now, timeouts) { - Some(*sock_addr) - } else { - None - } - }) - .collect(); - - let select_stream_timeouts: Vec<_> = store - .state() - .network - .scheduler - .connections - .iter() - .flat_map(|(sock_addr, state)| { - state.streams.iter().filter_map(|(stream_id, stream)| { - if stream.select.is_timed_out(now, timeouts) { - Some((*sock_addr, *stream_id)) - } else { - None - } - }) - }) - .collect(); - - for addr in select_auth_timeouts { - store.dispatch(P2pNetworkSelectAction::Timeout { - addr, - kind: crate::SelectKind::Authentication, - }); - } - - for addr in select_mux_timeouts { - store.dispatch(P2pNetworkSelectAction::Timeout { - addr, - kind: crate::SelectKind::MultiplexingNoPeerId, - }); - } - - for (addr, stream_id) in select_stream_timeouts { - // TODO: better solution for PeerId - let dummy = PeerId::from_bytes([0u8; 32]); - - store.dispatch(P2pNetworkSelectAction::Timeout { - addr, - kind: crate::SelectKind::Stream(dummy, stream_id), - }); - } -} - -#[cfg(feature = "p2p-libp2p")] -fn p2p_rpc_heartbeats(store: &mut Store, meta: &ActionMeta) -where - Store: P2pStore, -{ - use crate::network::rpc::P2pNetworkRpcAction; - let scheduler = &store.state().network.scheduler; - - let send_heartbeat_actions: Vec<_> = scheduler - .rpc_incoming_streams - .iter() - .chain(&scheduler.rpc_outgoing_streams) - .flat_map(|(peer_id, state)| { - state - .iter() - .filter(|(_, s)| s.should_send_heartbeat(meta.time())) - .map(|(stream_id, state)| P2pNetworkRpcAction::HeartbeatSend { - addr: state.addr, - peer_id: *peer_id, - stream_id: *stream_id, - }) - }) - .collect(); - for action in send_heartbeat_actions { - store.dispatch(action); - } -} - -fn p2p_connection_timeouts(store: &mut Store, meta: &ActionMeta) -where - Store: P2pStore, -{ - use crate::connection::incoming::P2pConnectionIncomingAction; - - let now = meta.time(); - let timeouts = &store.state().config.timeouts; - let p2p_connection_timeouts: Vec<_> = store - .state() - .peers - .iter() - .filter_map(|(peer_id, peer)| { - let s = peer.status.as_connecting()?; - match s.is_timed_out(now, timeouts) { - true => Some((*peer_id, s.as_outgoing().is_some())), - false => None, - } - }) - .collect(); - - for (peer_id, is_outgoing) in p2p_connection_timeouts { - match is_outgoing { - true => store.dispatch(P2pConnectionOutgoingAction::Timeout { peer_id }), - false => store.dispatch(P2pConnectionIncomingAction::Timeout { peer_id }), - }; - } -} - -fn p2p_try_reconnect_disconnected_peers(store: &mut Store, now: redux::Timestamp) -where - Store: P2pStore, -{ - if store.state().already_has_min_peers() { - return; - } - let timeouts = &store.state().config.timeouts; - let reconnect_actions: Vec<_> = store - .state() - .peers - .iter() - .filter_map(|(_, p)| { - if p.can_reconnect(now, timeouts) { - p.dial_opts.clone() - } else { - None - } - }) - .map(|opts| P2pConnectionOutgoingAction::Reconnect { opts, rpc_id: None }) - .collect(); - for action in reconnect_actions { - store.dispatch(action); - } -} - -fn p2p_discovery(store: &mut Store, meta: &redux::ActionMeta) -where - Store: P2pStore, -{ - let now = meta.time(); - let state = store.state(); - let config = &state.config; - if !config.peer_discovery { - return; - } - // ask initial peers - if let Some(_d) = config.timeouts.initial_peers { - // TODO: use RPC to ask initial peers - let _ = now; - } - - #[cfg(feature = "p2p-libp2p")] - if let Some(discovery_state) = state.network.scheduler.discovery_state() { - let my_id = state.my_id(); - match P2pNetworkKadKey::try_from(&my_id) { - Ok(key) => { - if discovery_state - .routing_table - .closest_peers(&key) - .any(|_| true) - && discovery_state.status.can_bootstrap(now, &config.timeouts) - { - store.dispatch(P2pNetworkKademliaAction::StartBootstrap { key: my_id }); - } - } - Err(e) => bug_condition!("p2p_discovery error {:?}", e), - } - } -} +use redux::ActionWithMeta; pub fn p2p_effects(store: &mut Store, action: ActionWithMeta) where diff --git a/p2p/src/p2p_reducer.rs b/p2p/src/p2p_reducer.rs index 6d7f5fc29a..7261a220fa 100644 --- a/p2p/src/p2p_reducer.rs +++ b/p2p/src/p2p_reducer.rs @@ -1,9 +1,18 @@ use crate::{ - channels::P2pChannelsState, connection::P2pConnectionState, - disconnection::P2pDisconnectedState, P2pAction, P2pActionWithMetaRef, P2pNetworkState, - P2pPeerState, P2pState, + channels::{ + rpc::P2pChannelsRpcAction, streaming_rpc::P2pChannelsStreamingRpcAction, P2pChannelsState, + }, + connection::{ + incoming::P2pConnectionIncomingAction, outgoing::P2pConnectionOutgoingAction, + P2pConnectionState, + }, + disconnection::P2pDisconnectedState, + P2pAction, P2pActionWithMetaRef, P2pNetworkKadKey, P2pNetworkKademliaAction, + P2pNetworkPnetAction, P2pNetworkRpcAction, P2pNetworkSelectAction, P2pNetworkState, + P2pPeerState, P2pState, PeerId, }; use openmina_core::{bug_condition, Substate}; +use redux::{ActionMeta, Dispatcher, Timestamp}; impl P2pState { pub fn reducer( @@ -63,4 +72,267 @@ impl P2pState { } } } + + pub fn p2p_timeout_dispatch( + state_context: Substate, + meta: &ActionMeta, + ) -> Result<(), String> + where + State: crate::P2pStateTrait, + Action: crate::P2pActionTrait, + { + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let state: &P2pState = state.substate()?; + let time = meta.time(); + + state.p2p_connection_timeouts_dispatch(dispatcher, time)?; + dispatcher.push(P2pConnectionOutgoingAction::RandomInit); + + state.p2p_try_reconnect_disconnected_peers(dispatcher, time)?; + state.p2p_discovery(dispatcher, time)?; + + #[cfg(feature = "p2p-libp2p")] + { + state.p2p_pnet_timeouts(dispatcher, time)?; + state.p2p_select_timeouts(dispatcher, time)?; + state.p2p_rpc_heartbeats(dispatcher, time)?; + } + + state.rpc_timeouts(dispatcher, time)?; + Ok(()) + } + + fn p2p_connection_timeouts_dispatch( + &self, + dispatcher: &mut Dispatcher, + time: Timestamp, + ) -> Result<(), String> + where + State: crate::P2pStateTrait, + Action: crate::P2pActionTrait, + { + let timeouts = &self.config.timeouts; + + self.peers + .iter() + .filter_map(|(peer_id, peer)| { + let state = peer.status.as_connecting()?; + state + .is_timed_out(time, timeouts) + .then(|| (*peer_id, state.as_outgoing().is_some())) + }) + .for_each(|(peer_id, is_outgoing)| match is_outgoing { + true => dispatcher.push(P2pConnectionOutgoingAction::Timeout { peer_id }), + false => dispatcher.push(P2pConnectionIncomingAction::Timeout { peer_id }), + }); + + Ok(()) + } + + fn p2p_try_reconnect_disconnected_peers( + &self, + dispatcher: &mut Dispatcher, + time: Timestamp, + ) -> Result<(), String> + where + State: crate::P2pStateTrait, + Action: crate::P2pActionTrait, + { + if self.already_has_min_peers() { + return Ok(()); + } + + let timeouts = &self.config.timeouts; + + self.peers + .iter() + .filter_map(|(_, peer)| { + if peer.can_reconnect(time, timeouts) { + peer.dial_opts.clone() + } else { + None + } + }) + .map(|opts| P2pConnectionOutgoingAction::Reconnect { opts, rpc_id: None }) + .for_each(|action| dispatcher.push(action)); + Ok(()) + } + + fn rpc_timeouts( + &self, + dispatcher: &mut Dispatcher, + time: Timestamp, + ) -> Result<(), String> + where + State: crate::P2pStateTrait, + Action: crate::P2pActionTrait, + { + self.peer_rpc_timeouts(time) + .into_iter() + .for_each(|(peer_id, id, is_streaming)| { + if is_streaming { + dispatcher.push(P2pChannelsStreamingRpcAction::Timeout { peer_id, id }); + } else { + dispatcher.push(P2pChannelsRpcAction::Timeout { peer_id, id }); + } + }); + + Ok(()) + } + + fn p2p_discovery( + &self, + dispatcher: &mut Dispatcher, + time: Timestamp, + ) -> Result<(), String> + where + State: crate::P2pStateTrait, + Action: crate::P2pActionTrait, + { + let config = &self.config; + let timeouts = &config.timeouts; + + if !config.peer_discovery { + return Ok(()); + } + + if let Some(_d) = config.timeouts.initial_peers { + // ask initial peers + // TODO: use RPC to ask initial peers + } + + #[cfg(feature = "p2p-libp2p")] + { + if let Some(discovery_state) = self.network.scheduler.discovery_state() { + let my_id = self.my_id(); + + match P2pNetworkKadKey::try_from(&my_id) { + Ok(key) => { + if discovery_state.status.can_bootstrap(time, timeouts) + && discovery_state + .routing_table + .closest_peers(&key) + .any(|_| true) + { + dispatcher + .push(P2pNetworkKademliaAction::StartBootstrap { key: my_id }); + } + } + Err(e) => bug_condition!("p2p discovery error: {:?}", e), + } + } + } + + Ok(()) + } +} + +#[cfg(feature = "p2p-libp2p")] +impl P2pState { + fn p2p_pnet_timeouts( + &self, + dispatcher: &mut Dispatcher, + time: Timestamp, + ) -> Result<(), String> + where + State: crate::P2pStateTrait, + Action: crate::P2pActionTrait, + { + let timeouts = &self.config.timeouts; + + self.network + .scheduler + .connections + .iter() + .filter(|(_, state)| state.pnet.is_timed_out(time, timeouts)) + .map(|(addr, _)| P2pNetworkPnetAction::Timeout { addr: *addr }) + .for_each(|action| dispatcher.push(action)); + + Ok(()) + } + + fn p2p_select_timeouts( + &self, + dispatcher: &mut Dispatcher, + time: Timestamp, + ) -> Result<(), String> + where + State: crate::P2pStateTrait, + Action: crate::P2pActionTrait, + { + let timeouts = &self.config.timeouts; + + self.network + .scheduler + .connections + .iter() + .filter(|(_, state)| state.select_auth.is_timed_out(time, timeouts)) + .map(|(addr, _)| P2pNetworkSelectAction::Timeout { + addr: *addr, + kind: crate::SelectKind::Authentication, + }) + .for_each(|action| dispatcher.push(action)); + + self.network + .scheduler + .connections + .iter() + .filter(|(_, state)| state.select_mux.is_timed_out(time, timeouts)) + .map(|(addr, _)| P2pNetworkSelectAction::Timeout { + addr: *addr, + kind: crate::SelectKind::MultiplexingNoPeerId, + }) + .for_each(|action| dispatcher.push(action)); + + // TODO: better solution for PeerId + let dummy = PeerId::from_bytes([0u8; 32]); + self.network + .scheduler + .connections + .iter() + .flat_map(|(sock_addr, state)| { + state + .streams + .iter() + .filter(|(_, stream)| stream.select.is_timed_out(time, timeouts)) + .map(|(stream_id, _)| (*sock_addr, *stream_id)) + }) + .map(|(addr, stream_id)| P2pNetworkSelectAction::Timeout { + addr, + kind: crate::SelectKind::Stream(dummy, stream_id), + }) + .for_each(|action| dispatcher.push(action)); + + Ok(()) + } + + fn p2p_rpc_heartbeats( + &self, + dispatcher: &mut Dispatcher, + time: Timestamp, + ) -> Result<(), String> + where + State: crate::P2pStateTrait, + Action: crate::P2pActionTrait, + { + let scheduler = &self.network.scheduler; + + scheduler + .rpc_incoming_streams + .iter() + .chain(&scheduler.rpc_outgoing_streams) + .flat_map(|(peer_id, state)| { + state + .iter() + .filter(|(_, s)| s.should_send_heartbeat(time)) + .map(|(stream_id, state)| P2pNetworkRpcAction::HeartbeatSend { + addr: state.addr, + peer_id: *peer_id, + stream_id: *stream_id, + }) + }) + .for_each(|action| dispatcher.push(action)); + + Ok(()) + } } diff --git a/p2p/testing/src/cluster.rs b/p2p/testing/src/cluster.rs index 4262c0bd61..001d3d894d 100644 --- a/p2p/testing/src/cluster.rs +++ b/p2p/testing/src/cluster.rs @@ -15,7 +15,7 @@ use p2p::{ P2pConnectionOutgoingInitOpts, P2pConnectionOutgoingInitOptsParseError, }, identity::SecretKey, - p2p_effects, p2p_timeout_effects, P2pConfig, P2pMeshsubConfig, P2pState, PeerId, + p2p_effects, P2pConfig, P2pMeshsubConfig, P2pState, PeerId, }; use redux::SystemTime; use tokio::sync::mpsc; @@ -382,17 +382,22 @@ impl Cluster { let store = crate::redux::Store::new( |state, action, dispatcher| { - log_action(action.action(), action.meta(), state.0.my_id()); - if let Action::P2p(p2p_action) = action.action() { - let time = action.meta().time(); - let result = P2pState::reducer( - Substate::new(state, dispatcher), - action.meta().clone().with_action(p2p_action), - ); - - if let Err(error) = result { - openmina_core::warn!(time; "error = {error}"); + let meta = action.meta().clone(); + let action = action.action(); + + log_action(action, &meta, state.0.my_id()); + + let time = meta.time(); + let state_context = Substate::new(state, dispatcher); + let result = match action { + Action::P2p(action) => { + P2pState::reducer(state_context, meta.with_action(action)) } + Action::Idle(_) => P2pState::p2p_timeout_dispatch(state_context, &meta), + }; + + if let Err(error) = result { + openmina_core::warn!(time; "error = {error}"); } }, override_fn.unwrap_or(|store, action| { @@ -402,7 +407,9 @@ impl Cluster { p2p_effects(store, meta.with_action(a.clone())); event_mapper_effect(store, a); } - Action::Idle(_) => p2p_timeout_effects(store, &meta), + Action::Idle(_) => { + // handled by reducer + } } }), service, diff --git a/p2p/tests/identify.rs b/p2p/tests/identify.rs index a3f9166c84..381cbdb134 100644 --- a/p2p/tests/identify.rs +++ b/p2p/tests/identify.rs @@ -10,7 +10,7 @@ use p2p::{ stream::P2pNetworkIdentifyStreamState, P2pNetworkIdentify, P2pNetworkIdentifyAction, P2pNetworkIdentifyStreamAction, }, - p2p_effects, p2p_timeout_effects, + p2p_effects, token::{self, DiscoveryAlgorithm}, Data, P2pAction, P2pNetworkAction, P2pNetworkYamuxAction, PeerId, }; @@ -268,7 +268,7 @@ fn bad_node_effects( event_mapper_effect(store, a); } Action::Idle(_) => { - p2p_timeout_effects(store, &meta); + // p2p_timeout_effects(store, &meta); } }; } diff --git a/p2p/tests/kademlia.rs b/p2p/tests/kademlia.rs index 8fc39a082a..c126ff705e 100644 --- a/p2p/tests/kademlia.rs +++ b/p2p/tests/kademlia.rs @@ -1,6 +1,6 @@ use p2p::{ - identity::SecretKey, p2p_effects, p2p_timeout_effects, P2pAction, P2pNetworkAction, - P2pNetworkKadAction, P2pNetworkKadBucket, P2pNetworkKademliaAction, P2pNetworkKademliaRpcReply, + identity::SecretKey, p2p_effects, P2pAction, P2pNetworkAction, P2pNetworkKadAction, + P2pNetworkKadBucket, P2pNetworkKademliaAction, P2pNetworkKademliaRpcReply, P2pNetworkKademliaStreamAction, PeerId, }; use p2p_testing::{ @@ -402,7 +402,7 @@ fn bad_node_effects( event_mapper_effect(store, a); } Action::Idle(_) => { - p2p_timeout_effects(store, &meta); + // p2p_timeout_effects(store, &meta); } }; } From 96e805838fbf2d3ceac5cc5640a18baf5d9ecad5 Mon Sep 17 00:00:00 2001 From: Mimir Date: Mon, 7 Oct 2024 10:12:04 +0200 Subject: [PATCH 2/4] Ported effects --- node/src/action.rs | 4 + node/src/action_kind.rs | 47 +- node/src/consensus/consensus_actions.rs | 6 +- node/src/consensus/consensus_reducer.rs | 14 +- node/src/effects.rs | 3 + node/src/p2p/callbacks/mod.rs | 3 + .../p2p/callbacks/p2p_callbacks_actions.rs | 48 + .../p2p/callbacks/p2p_callbacks_reducer.rs | 381 +++++++ node/src/p2p/mod.rs | 2 + node/src/p2p/p2p_effects.rs | 974 +++++++++--------- node/src/reducer.rs | 3 + node/src/rpc/rpc_actions.rs | 9 +- node/src/rpc/rpc_effects.rs | 11 + node/src/rpc/rpc_reducer.rs | 1 + node/src/state.rs | 154 ++- ...on_frontier_sync_ledger_snarked_actions.rs | 5 + ...on_frontier_sync_ledger_snarked_effects.rs | 2 + ...on_frontier_sync_ledger_snarked_reducer.rs | 96 +- .../transition_frontier_actions.rs | 8 + .../transition_frontier_effects.rs | 1 + .../transition_frontier_reducer.rs | 14 + .../best_tip/p2p_channels_best_tip_reducer.rs | 12 +- .../channels/rpc/p2p_channels_rpc_reducer.rs | 40 +- .../snark/p2p_channels_snark_reducer.rs | 21 +- ...p_channels_snark_job_commitment_reducer.rs | 12 +- .../p2p_channels_streaming_rpc_reducer.rs | 35 +- .../p2p_channels_transaction_actions.rs | 1 + .../p2p_channels_transaction_reducer.rs | 14 +- .../p2p_connection_incoming_reducer.rs | 53 +- .../p2p_connection_outgoing_reducer.rs | 22 +- .../p2p_disconnection_reducer.rs | 15 +- p2p/src/p2p_state.rs | 96 +- p2p/src/peer/p2p_peer_reducer.rs | 6 + p2p/testing/src/cluster.rs | 8 +- 34 files changed, 1571 insertions(+), 550 deletions(-) create mode 100644 node/src/p2p/callbacks/mod.rs create mode 100644 node/src/p2p/callbacks/p2p_callbacks_actions.rs create mode 100644 node/src/p2p/callbacks/p2p_callbacks_reducer.rs diff --git a/node/src/action.rs b/node/src/action.rs index c41c51168f..0669809e9e 100644 --- a/node/src/action.rs +++ b/node/src/action.rs @@ -8,6 +8,7 @@ pub use crate::consensus::ConsensusAction; pub use crate::event_source::EventSourceAction; pub use crate::external_snark_worker::ExternalSnarkWorkerAction; pub use crate::ledger::LedgerAction; +use crate::p2p::callbacks::P2pCallbacksAction; pub use crate::p2p::P2pAction; pub use crate::rpc::RpcAction; pub use crate::snark::SnarkAction; @@ -32,6 +33,8 @@ pub enum Action { EventSource(EventSourceAction), P2p(P2pAction), + P2pCallbacks(P2pCallbacksAction), + Ledger(LedgerAction), Snark(SnarkAction), Consensus(ConsensusAction), @@ -85,6 +88,7 @@ impl redux::EnablingCondition for Action { Action::WatchedAccounts(a) => a.is_enabled(state, time), Action::TransactionPool(a) => a.is_enabled(state, time), Action::TransactionPoolEffect(a) => a.is_enabled(state, time), + Action::P2pCallbacks(a) => a.is_enabled(state, time), } } } diff --git a/node/src/action_kind.rs b/node/src/action_kind.rs index 2db3e5a60c..b614e2cd1f 100644 --- a/node/src/action_kind.rs +++ b/node/src/action_kind.rs @@ -23,6 +23,7 @@ use crate::external_snark_worker::ExternalSnarkWorkerAction; use crate::ledger::read::LedgerReadAction; use crate::ledger::write::LedgerWriteAction; use crate::ledger::LedgerAction; +use crate::p2p::callbacks::P2pCallbacksAction; use crate::p2p::channels::best_tip::P2pChannelsBestTipAction; use crate::p2p::channels::best_tip_effectful::P2pChannelsBestTipEffectfulAction; use crate::p2p::channels::rpc::P2pChannelsRpcAction; @@ -147,6 +148,7 @@ pub enum ActionKind { ConsensusBlockSnarkVerifySuccess, ConsensusDetectForkRange, ConsensusLongRangeForkResolve, + ConsensusP2pBestTipUpdate, ConsensusPrune, ConsensusShortRangeForkResolve, EventSourceNewEvent, @@ -174,6 +176,13 @@ pub enum ActionKind { LedgerWriteInit, LedgerWritePending, LedgerWriteSuccess, + P2pCallbacksP2pChannelsRpcReady, + P2pCallbacksP2pChannelsRpcRequestReceived, + P2pCallbacksP2pChannelsRpcResponseReceived, + P2pCallbacksP2pChannelsRpcTimeout, + P2pCallbacksP2pChannelsStreamingRpcReady, + P2pCallbacksP2pChannelsStreamingRpcResponseReceived, + P2pCallbacksP2pChannelsStreamingRpcTimeout, P2pChannelsBestTipInit, P2pChannelsBestTipPending, P2pChannelsBestTipReady, @@ -427,6 +436,7 @@ pub enum ActionKind { RpcLedgerAccountsGetPending, RpcLedgerAccountsGetSuccess, RpcMessageProgressGet, + RpcP2pConnectionIncomingAnswerReady, RpcP2pConnectionIncomingError, RpcP2pConnectionIncomingInit, RpcP2pConnectionIncomingPending, @@ -510,6 +520,7 @@ pub enum ActionKind { TransactionPoolVerifyError, TransactionPoolEffectfulFetchAccounts, TransitionFrontierGenesisInject, + TransitionFrontierRpcRespondBestTip, TransitionFrontierSyncFailed, TransitionFrontierSynced, TransitionFrontierGenesisLedgerLoadInit, @@ -559,6 +570,7 @@ pub enum ActionKind { TransitionFrontierSyncLedgerSnarkedNumAccountsReceived, TransitionFrontierSyncLedgerSnarkedNumAccountsRejected, TransitionFrontierSyncLedgerSnarkedNumAccountsSuccess, + TransitionFrontierSyncLedgerSnarkedP2pDisconnection, TransitionFrontierSyncLedgerSnarkedPeerQueryAddressError, TransitionFrontierSyncLedgerSnarkedPeerQueryAddressInit, TransitionFrontierSyncLedgerSnarkedPeerQueryAddressPending, @@ -599,7 +611,7 @@ pub enum ActionKind { } impl ActionKind { - pub const COUNT: u16 = 493; + pub const COUNT: u16 = 504; } impl std::fmt::Display for ActionKind { @@ -614,6 +626,7 @@ impl ActionKindGet for Action { Self::CheckTimeouts(a) => a.kind(), Self::EventSource(a) => a.kind(), Self::P2p(a) => a.kind(), + Self::P2pCallbacks(a) => a.kind(), Self::Ledger(a) => a.kind(), Self::Snark(a) => a.kind(), Self::Consensus(a) => a.kind(), @@ -664,6 +677,30 @@ impl ActionKindGet for P2pAction { } } +impl ActionKindGet for P2pCallbacksAction { + fn kind(&self) -> ActionKind { + match self { + Self::P2pChannelsRpcReady { .. } => ActionKind::P2pCallbacksP2pChannelsRpcReady, + Self::P2pChannelsRpcTimeout { .. } => ActionKind::P2pCallbacksP2pChannelsRpcTimeout, + Self::P2pChannelsRpcResponseReceived { .. } => { + ActionKind::P2pCallbacksP2pChannelsRpcResponseReceived + } + Self::P2pChannelsRpcRequestReceived { .. } => { + ActionKind::P2pCallbacksP2pChannelsRpcRequestReceived + } + Self::P2pChannelsStreamingRpcReady => { + ActionKind::P2pCallbacksP2pChannelsStreamingRpcReady + } + Self::P2pChannelsStreamingRpcTimeout { .. } => { + ActionKind::P2pCallbacksP2pChannelsStreamingRpcTimeout + } + Self::P2pChannelsStreamingRpcResponseReceived { .. } => { + ActionKind::P2pCallbacksP2pChannelsStreamingRpcResponseReceived + } + } + } +} + impl ActionKindGet for LedgerAction { fn kind(&self) -> ActionKind { match self { @@ -698,6 +735,7 @@ impl ActionKindGet for ConsensusAction { Self::ShortRangeForkResolve { .. } => ActionKind::ConsensusShortRangeForkResolve, Self::LongRangeForkResolve { .. } => ActionKind::ConsensusLongRangeForkResolve, Self::BestTipUpdate { .. } => ActionKind::ConsensusBestTipUpdate, + Self::P2pBestTipUpdate { .. } => ActionKind::ConsensusP2pBestTipUpdate, Self::Prune => ActionKind::ConsensusPrune, } } @@ -712,6 +750,7 @@ impl ActionKindGet for TransitionFrontierAction { Self::GenesisInject => ActionKind::TransitionFrontierGenesisInject, Self::Synced { .. } => ActionKind::TransitionFrontierSynced, Self::SyncFailed { .. } => ActionKind::TransitionFrontierSyncFailed, + Self::RpcRespondBestTip { .. } => ActionKind::TransitionFrontierRpcRespondBestTip, } } } @@ -857,6 +896,9 @@ impl ActionKindGet for RpcAction { Self::P2pConnectionIncomingRespond { .. } => { ActionKind::RpcP2pConnectionIncomingRespond } + Self::P2pConnectionIncomingAnswerReady { .. } => { + ActionKind::RpcP2pConnectionIncomingAnswerReady + } Self::P2pConnectionIncomingError { .. } => ActionKind::RpcP2pConnectionIncomingError, Self::P2pConnectionIncomingSuccess { .. } => { ActionKind::RpcP2pConnectionIncomingSuccess @@ -1880,6 +1922,9 @@ impl ActionKindGet for TransitionFrontierSyncLedgerSnarkedAction { ActionKind::TransitionFrontierSyncLedgerSnarkedMerkleTreeSyncSuccess } Self::Success => ActionKind::TransitionFrontierSyncLedgerSnarkedSuccess, + Self::P2pDisconnection { .. } => { + ActionKind::TransitionFrontierSyncLedgerSnarkedP2pDisconnection + } } } } diff --git a/node/src/consensus/consensus_actions.rs b/node/src/consensus/consensus_actions.rs index f9b80d7d7f..48a8ea2c50 100644 --- a/node/src/consensus/consensus_actions.rs +++ b/node/src/consensus/consensus_actions.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use mina_p2p_messages::v2::{MinaBlockBlockStableV2, StateHash}; -use openmina_core::block::ArcBlockWithHash; +use openmina_core::block::{ArcBlockWithHash, BlockWithHash}; use openmina_core::{action_event, ActionEvent}; use serde::{Deserialize, Serialize}; use snark::block_verify::SnarkBlockVerifyError; @@ -53,6 +53,9 @@ pub enum ConsensusAction { BestTipUpdate { hash: StateHash, }, + P2pBestTipUpdate { + best_tip: BlockWithHash>, + }, Prune, } @@ -146,6 +149,7 @@ impl redux::EnablingCondition for ConsensusAction { .consensus .is_candidate_decided_to_use_as_tip(hash) }, + ConsensusAction::P2pBestTipUpdate { .. } => true, ConsensusAction::Prune => { state.consensus.best_tip().is_some() }, diff --git a/node/src/consensus/consensus_reducer.rs b/node/src/consensus/consensus_reducer.rs index 0c6e6c99fe..a1cd8fb5a6 100644 --- a/node/src/consensus/consensus_reducer.rs +++ b/node/src/consensus/consensus_reducer.rs @@ -5,7 +5,7 @@ use openmina_core::{ use snark::block_verify::{SnarkBlockVerifyAction, SnarkBlockVerifyError}; use crate::{ - transition_frontier::sync::TransitionFrontierSyncAction, Action, State, WatchedAccountsAction, + transition_frontier::sync::{ledger::{snarked::TransitionFrontierSyncLedgerSnarkedAction, staged::TransitionFrontierSyncLedgerStagedAction}, TransitionFrontierSyncAction}, Action, State, WatchedAccountsAction, }; use super::{ @@ -235,6 +235,18 @@ impl ConsensusState { transition_frontier_new_best_tip_handler(global_state, dispatcher); } + ConsensusAction::P2pBestTipUpdate { best_tip } => { + let dispatcher = state_context.into_dispatcher(); + dispatcher.push(ConsensusAction::BlockReceived { + hash: best_tip.hash.clone(), + block: best_tip.block.clone(), + chain_proof: None, + }); + + dispatcher.push(TransitionFrontierSyncLedgerSnarkedAction::PeersQuery); + dispatcher.push(TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit); + dispatcher.push(TransitionFrontierSyncAction::BlocksPeersQuery); + } ConsensusAction::Prune => { let Some(best_tip_hash) = state.best_tip.clone() else { return; diff --git a/node/src/effects.rs b/node/src/effects.rs index 2ea6f94df6..bc6aa6a205 100644 --- a/node/src/effects.rs +++ b/node/src/effects.rs @@ -92,6 +92,9 @@ pub fn effects(store: &mut Store, action: ActionWithMeta) { Action::WatchedAccounts(_) => { // Handled by reducer } + Action::P2pCallbacks(_) => { + // Handled by reducer + } } } diff --git a/node/src/p2p/callbacks/mod.rs b/node/src/p2p/callbacks/mod.rs new file mode 100644 index 0000000000..c499b75def --- /dev/null +++ b/node/src/p2p/callbacks/mod.rs @@ -0,0 +1,3 @@ +mod p2p_callbacks_actions; +pub use p2p_callbacks_actions::P2pCallbacksAction; +mod p2p_callbacks_reducer; \ No newline at end of file diff --git a/node/src/p2p/callbacks/p2p_callbacks_actions.rs b/node/src/p2p/callbacks/p2p_callbacks_actions.rs new file mode 100644 index 0000000000..976e2746fd --- /dev/null +++ b/node/src/p2p/callbacks/p2p_callbacks_actions.rs @@ -0,0 +1,48 @@ +use openmina_core::ActionEvent; +use p2p::{ + channels::{ + rpc::{P2pRpcId, P2pRpcRequest, P2pRpcResponse}, + streaming_rpc::P2pStreamingRpcResponseFull, + }, + PeerId, +}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Clone, ActionEvent)] +#[action_event(level = debug)] +pub enum P2pCallbacksAction { + P2pChannelsRpcReady { + peer_id: PeerId, + }, + P2pChannelsRpcTimeout { + peer_id: PeerId, + id: P2pRpcId, + }, + P2pChannelsRpcResponseReceived { + peer_id: PeerId, + id: P2pRpcId, + response: Option>, + }, + P2pChannelsRpcRequestReceived { + peer_id: PeerId, + id: P2pRpcId, + request: Box, + }, + + P2pChannelsStreamingRpcReady, + P2pChannelsStreamingRpcTimeout { + peer_id: PeerId, + id: P2pRpcId, + }, + P2pChannelsStreamingRpcResponseReceived { + peer_id: PeerId, + id: P2pRpcId, + response: Option, + }, +} + +impl redux::EnablingCondition for P2pCallbacksAction { + fn is_enabled(&self, state: &crate::State, time: redux::Timestamp) -> bool { + true + } +} diff --git a/node/src/p2p/callbacks/p2p_callbacks_reducer.rs b/node/src/p2p/callbacks/p2p_callbacks_reducer.rs new file mode 100644 index 0000000000..ea77e07935 --- /dev/null +++ b/node/src/p2p/callbacks/p2p_callbacks_reducer.rs @@ -0,0 +1,381 @@ +use ark_ff::fields::arithmetic::InvalidBigInt; +use mina_p2p_messages::v2::{MinaLedgerSyncLedgerAnswerStableV2, StateHash}; +use openmina_core::{block::BlockWithHash, bug_condition}; +use p2p::{ + channels::{rpc::{BestTipWithProof, P2pChannelsRpcAction, P2pRpcRequest, P2pRpcResponse}, streaming_rpc::P2pStreamingRpcResponseFull}, + disconnection::{P2pDisconnectionAction, P2pDisconnectionReason}, +}; +use redux::ActionWithMeta; + +use crate::{ + p2p_ready, + snark_pool::candidate::SnarkPoolCandidateAction, + transition_frontier::sync::{ + ledger::{ + snarked::{ + PeerLedgerQueryError, PeerLedgerQueryResponse, + TransitionFrontierSyncLedgerSnarkedAction, + }, + staged::{PeerStagedLedgerPartsFetchError, TransitionFrontierSyncLedgerStagedAction}, + }, + PeerBlockFetchError, TransitionFrontierSyncAction, + }, + ConsensusAction, +}; + +use super::P2pCallbacksAction; + +impl crate::State { + pub fn p2p_callback_reducer( + state_context: crate::Substate, + action: ActionWithMeta<&P2pCallbacksAction>, + ) { + let (action, meta) = action.split(); + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + + match action { + P2pCallbacksAction::P2pChannelsRpcReady { peer_id } => { + let peer_id = *peer_id; + + dispatcher.push(P2pChannelsRpcAction::RequestSend { + peer_id, + id: 0, + request: Box::new(P2pRpcRequest::BestTipWithProof), + on_init: None, + }); + + dispatcher.push(TransitionFrontierSyncLedgerSnarkedAction::PeersQuery); + dispatcher.push(TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit); + dispatcher.push(TransitionFrontierSyncAction::BlocksPeersQuery); + } + P2pCallbacksAction::P2pChannelsRpcTimeout { peer_id, id } => { + let peer_id = *peer_id; + let rpc_id = *id; + let Some(peer) = state.p2p.get_ready_peer(&peer_id) else { + bug_condition!("get_ready_peer({:?}) returned None", peer_id); + return; + }; + + let Some(rpc_kind) = peer.channels.rpc.pending_local_rpc_kind() else { + bug_condition!("peer: {:?} pending_local_rpc_kind() returned None", peer_id); + return; + }; + + dispatcher.push( + TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressError { + peer_id, + rpc_id, + error: PeerLedgerQueryError::Timeout, + }, + ); + dispatcher.push( + TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchError { + peer_id, + rpc_id, + error: PeerStagedLedgerPartsFetchError::Timeout, + }, + ); + dispatcher.push(TransitionFrontierSyncAction::BlocksPeerQueryError { + peer_id, + rpc_id, + error: PeerBlockFetchError::Timeout, + }); + dispatcher.push(P2pDisconnectionAction::Init { + peer_id, + reason: P2pDisconnectionReason::TransitionFrontierRpcTimeout(rpc_kind), + }); + } + P2pCallbacksAction::P2pChannelsRpcResponseReceived { + peer_id, + id, + response, + } => { + let peer_id = *peer_id; + let id = *id; + + match response.as_deref() { + None => { + dispatcher.push( + TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressError { + peer_id, + rpc_id: id, + error: PeerLedgerQueryError::DataUnavailable, + }, + ); + dispatcher.push( + TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchError { + peer_id, + rpc_id: id, + error: PeerStagedLedgerPartsFetchError::DataUnavailable, + }, + ); + dispatcher.push(TransitionFrontierSyncAction::BlocksPeerQueryError { + peer_id, + rpc_id: id, + error: PeerBlockFetchError::DataUnavailable, + }); + } + Some(P2pRpcResponse::BestTipWithProof(resp)) => { + let (body_hashes, root_block) = &resp.proof; + + let (Ok(best_tip), Ok(root_block)) = ( + BlockWithHash::try_new(resp.best_tip.clone()), + BlockWithHash::try_new(root_block.clone()), + ) else { + openmina_core::error!(meta.time(); "P2pRpcResponse::BestTipWithProof: invalid blocks"); + return; + }; + + // reconstruct hashes + let Ok(hashes) = body_hashes + .iter() + .take(body_hashes.len().saturating_sub(1)) + .scan(root_block.hash.clone(), |pred_hash, body_hash| { + *pred_hash = match StateHash::try_from_hashes(pred_hash, body_hash) + { + Ok(hash) => hash, + Err(_) => return Some(Err(InvalidBigInt)), + }; + Some(Ok(pred_hash.clone())) + }) + .collect::, _>>() + else { + openmina_core::error!(meta.time(); "P2pRpcResponse::BestTipWithProof: invalid hashes"); + return; + }; + + if let Some(pred_hash) = hashes.last() { + let expected_hash = + &best_tip.block.header.protocol_state.previous_state_hash; + if pred_hash != expected_hash { + openmina_core::warn!(meta.time(); + kind = "P2pRpcBestTipHashMismatch", + response = serde_json::to_string(&resp).ok(), + expected_hash = expected_hash.to_string(), + calculated_hash = pred_hash.to_string()); + return; + } + } + dispatcher.push(ConsensusAction::BlockChainProofUpdate { + hash: best_tip.hash, + chain_proof: (hashes, root_block), + }); + } + Some(P2pRpcResponse::LedgerQuery(answer)) => match answer { + MinaLedgerSyncLedgerAnswerStableV2::ChildHashesAre(left, right) => { + dispatcher.push( + TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressSuccess { + peer_id, + rpc_id: id, + response: PeerLedgerQueryResponse::ChildHashes( + left.clone(), + right.clone(), + ), + }, + ); + } + MinaLedgerSyncLedgerAnswerStableV2::ContentsAre(accounts) => { + dispatcher.push( + TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressSuccess { + peer_id, + rpc_id: id, + response: PeerLedgerQueryResponse::ChildAccounts( + accounts.iter().cloned().collect(), + ), + }, + ); + } + MinaLedgerSyncLedgerAnswerStableV2::NumAccounts(count, contents_hash) => { + dispatcher.push( + TransitionFrontierSyncLedgerSnarkedAction::PeerQueryNumAccountsSuccess { + peer_id, + rpc_id: id, + response: PeerLedgerQueryResponse::NumAccounts( + count.as_u64(), contents_hash.clone() + ), + }, + ); + } + }, + Some(P2pRpcResponse::StagedLedgerAuxAndPendingCoinbasesAtBlock(parts)) => { + dispatcher.push( + TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchSuccess { + peer_id, + rpc_id: id, + parts: parts.clone(), + }, + ); + } + Some(P2pRpcResponse::Block(block)) => { + let Ok(block) = BlockWithHash::try_new(block.clone()) else { + openmina_core::error!(meta.time(); "P2pRpcResponse::Block: invalid block"); + return; + }; + dispatcher.push(TransitionFrontierSyncAction::BlocksPeerQuerySuccess { + peer_id, + rpc_id: id, + response: block, + }); + } + Some(P2pRpcResponse::Snark(snark)) => { + dispatcher.push(SnarkPoolCandidateAction::WorkReceived { + peer_id, + work: snark.clone(), + }); + } + Some(P2pRpcResponse::InitialPeers(_)) => {} + } + dispatcher.push(TransitionFrontierSyncLedgerSnarkedAction::PeersQuery); + dispatcher.push(TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit {}); + dispatcher.push(TransitionFrontierSyncAction::BlocksPeersQuery); + } + P2pCallbacksAction::P2pChannelsRpcRequestReceived { + peer_id, + id, + request, + } => { + let peer_id = *peer_id; + let id = *id; + + match *request.clone() { + P2pRpcRequest::BestTipWithProof => { + let best_chain = &state.transition_frontier.best_chain; + let response = None.or_else(|| { + let best_tip = best_chain.last()?; + let mut chain_iter = best_chain.iter(); + let root_block = chain_iter.next()?; + // TODO(binier): cache body hashes + let Ok(body_hashes) = chain_iter + .map(|b| b.header().protocol_state.body.try_hash()) + .collect::>() + else { + openmina_core::error!(meta.time(); "P2pRpcRequest::BestTipWithProof: invalid protocol state"); + return None; + }; + + Some(BestTipWithProof { + best_tip: best_tip.block().clone(), + proof: (body_hashes, root_block.block().clone()), + }) + }); + let response = response.map(P2pRpcResponse::BestTipWithProof).map(Box::new); + dispatcher.push(P2pChannelsRpcAction::ResponseSend { + peer_id, + id, + response, + }); + } + P2pRpcRequest::Block(hash) => { + let best_chain = &state.transition_frontier.best_chain; + let response = best_chain + .iter() + .rev() + .find(|b| b.hash() == &hash) + .map(|b| b.block().clone()) + .map(P2pRpcResponse::Block) + .map(Box::new); + dispatcher.push(P2pChannelsRpcAction::ResponseSend { + peer_id, + id, + response, + }); + } + P2pRpcRequest::LedgerQuery(..) => { + // async ledger request will be triggered + // by `LedgerReadAction::FindTodos`. + } + P2pRpcRequest::StagedLedgerAuxAndPendingCoinbasesAtBlock(..) => { + // async ledger request will be triggered + // by `LedgerReadAction::FindTodos`. + } + P2pRpcRequest::Snark(job_id) => { + let job = state.snark_pool.get(&job_id); + let response = job + .and_then(|job| job.snark.as_ref()) + .map(|snark| snark.work.clone()) + .map(P2pRpcResponse::Snark) + .map(Box::new); + + dispatcher.push(P2pChannelsRpcAction::ResponseSend { + peer_id, + id, + response, + }); + } + P2pRpcRequest::InitialPeers => { + let p2p = p2p_ready!(state.p2p, meta.time()); + let peers = p2p + .peers + .iter() + .filter_map(|(_, v)| v.dial_opts.clone()) + .collect(); + let response = Some(Box::new(P2pRpcResponse::InitialPeers(peers))); + + dispatcher.push(P2pChannelsRpcAction::ResponseSend { + peer_id, + id, + response, + }); + } + } + } + P2pCallbacksAction::P2pChannelsStreamingRpcReady => { + dispatcher.push(TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit); + } + P2pCallbacksAction::P2pChannelsStreamingRpcTimeout { peer_id, id } => { + let peer_id = *peer_id; + let rpc_id = *id; + + let Some(peer) = state.p2p.get_ready_peer(&peer_id) else { + bug_condition!("get_ready_peer({:?}) returned None", peer_id); + return; + }; + let Some(rpc_kind) = peer.channels.streaming_rpc.pending_local_rpc_kind() else { + bug_condition!("peer: {:?} pending_local_rpc_kind() returned None", peer_id); + return; + }; + dispatcher.push( + TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchError { + peer_id, + rpc_id, + error: PeerStagedLedgerPartsFetchError::Timeout, + }, + ); + dispatcher.push(P2pDisconnectionAction::Init { + peer_id, + reason: P2pDisconnectionReason::TransitionFrontierStreamingRpcTimeout(rpc_kind), + }); + } + P2pCallbacksAction::P2pChannelsStreamingRpcResponseReceived { + peer_id, + id, + response, + } => { + let peer_id = *peer_id; + let rpc_id = *id; + + match response { + None => { + dispatcher.push( + TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchError { + peer_id, + rpc_id, + error: PeerStagedLedgerPartsFetchError::DataUnavailable, + }, + ); + } + Some(P2pStreamingRpcResponseFull::StagedLedgerParts(parts)) => { + dispatcher.push( + TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchSuccess { + peer_id, + rpc_id, + parts: parts.clone(), + }, + ); + } + } + dispatcher.push(TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit {}); + } + } + } +} diff --git a/node/src/p2p/mod.rs b/node/src/p2p/mod.rs index bcfe146103..aac743bfb1 100644 --- a/node/src/p2p/mod.rs +++ b/node/src/p2p/mod.rs @@ -13,6 +13,8 @@ pub mod disconnection; pub mod network; pub mod peer; +pub mod callbacks; + mod p2p_effects; pub use p2p_effects::*; use redux::EnablingCondition; diff --git a/node/src/p2p/p2p_effects.rs b/node/src/p2p/p2p_effects.rs index fcaaa45184..ef4f3c7083 100644 --- a/node/src/p2p/p2p_effects.rs +++ b/node/src/p2p/p2p_effects.rs @@ -49,163 +49,167 @@ pub fn node_p2p_effects(store: &mut Store, action: P2pActionWithM store.service().start_mio(); } } - P2pAction::Connection(action) => match action { - P2pConnectionAction::Outgoing(action) => match action { - P2pConnectionOutgoingAction::Error { - ref peer_id, - ref error, - } => { - let p2p = p2p_ready!(store.state().p2p, meta.time()); - if let Some(rpc_id) = p2p.peer_connection_rpc_id(peer_id) { - store.dispatch(RpcAction::P2pConnectionOutgoingError { - rpc_id, - error: error.clone(), - }); - } - } - P2pConnectionOutgoingAction::Success { ref peer_id } => { - let p2p = p2p_ready!(store.state().p2p, meta.time()); - if let Some(rpc_id) = p2p.peer_connection_rpc_id(peer_id) { - store.dispatch(RpcAction::P2pConnectionOutgoingSuccess { rpc_id }); - } - } - _ => {} - }, - P2pConnectionAction::Incoming(action) => match &action { - P2pConnectionIncomingAction::AnswerReady { peer_id, answer } => { - let p2p = p2p_ready!(store.state().p2p, meta.time()); - if let Some(rpc_id) = p2p.peer_connection_rpc_id(peer_id) { - store.dispatch(RpcAction::P2pConnectionIncomingRespond { - rpc_id, - response: P2pConnectionResponse::Accepted(answer.clone()), - }); - store.dispatch(P2pConnectionIncomingAction::AnswerSendSuccess { - peer_id: *peer_id, - }); - } - } - P2pConnectionIncomingAction::Error { peer_id, error } => { - let p2p = p2p_ready!(store.state().p2p, meta.time()); - if let Some(rpc_id) = p2p.peer_connection_rpc_id(peer_id) { - store.dispatch(RpcAction::P2pConnectionIncomingError { - rpc_id, - error: format!("{:?}", error), - }); - } - } - P2pConnectionIncomingAction::Success { peer_id } => { - let p2p = p2p_ready!(store.state().p2p, meta.time()); - if let Some(rpc_id) = p2p.peer_connection_rpc_id(peer_id) { - store.dispatch(RpcAction::P2pConnectionIncomingSuccess { rpc_id }); - } - } - _ => {} - }, - }, - P2pAction::Disconnection(action) => match action { - P2pDisconnectionAction::Init { .. } => {} - P2pDisconnectionAction::Finish { peer_id } => { - if let Some(s) = store.state().transition_frontier.sync.ledger() { - let snarked_ledger_num_accounts_rpc_id = s - .snarked() - .and_then(|s| s.peer_num_accounts_rpc_id(&peer_id)); - let snarked_ledger_address_rpc_ids = s - .snarked() - .map(|s| s.peer_address_query_pending_rpc_ids(&peer_id).collect()) - .unwrap_or(vec![]); - let staged_ledger_parts_fetch_rpc_id = - s.staged().and_then(|s| s.parts_fetch_rpc_id(&peer_id)); + P2pAction::Connection(action) => { + // match action { + // P2pConnectionAction::Outgoing(action) => match action { + // P2pConnectionOutgoingAction::Error { + // ref peer_id, + // ref error, + // } => { + // let p2p = p2p_ready!(store.state().p2p, meta.time()); + // if let Some(rpc_id) = p2p.peer_connection_rpc_id(peer_id) { + // store.dispatch(RpcAction::P2pConnectionOutgoingError { + // rpc_id, + // error: error.clone(), + // }); + // } + // } + // P2pConnectionOutgoingAction::Success { ref peer_id } => { + // let p2p = p2p_ready!(store.state().p2p, meta.time()); + // if let Some(rpc_id) = p2p.peer_connection_rpc_id(peer_id) { + // store.dispatch(RpcAction::P2pConnectionOutgoingSuccess { rpc_id }); + // } + // } + // _ => {} + // }, + // P2pConnectionAction::Incoming(action) => match &action { + // P2pConnectionIncomingAction::AnswerReady { peer_id, answer } => { + // let p2p = p2p_ready!(store.state().p2p, meta.time()); + // if let Some(rpc_id) = p2p.peer_connection_rpc_id(peer_id) { + // store.dispatch(RpcAction::P2pConnectionIncomingRespond { + // rpc_id, + // response: P2pConnectionResponse::Accepted(answer.clone()), + // }); + // store.dispatch(P2pConnectionIncomingAction::AnswerSendSuccess { + // peer_id: *peer_id, + // }); + // } + // } + // P2pConnectionIncomingAction::Error { peer_id, error } => { + // let p2p = p2p_ready!(store.state().p2p, meta.time()); + // if let Some(rpc_id) = p2p.peer_connection_rpc_id(peer_id) { + // store.dispatch(RpcAction::P2pConnectionIncomingError { + // rpc_id, + // error: format!("{:?}", error), + // }); + // } + // } + // P2pConnectionIncomingAction::Success { peer_id } => { + // let p2p = p2p_ready!(store.state().p2p, meta.time()); + // if let Some(rpc_id) = p2p.peer_connection_rpc_id(peer_id) { + // store.dispatch(RpcAction::P2pConnectionIncomingSuccess { rpc_id }); + // } + // } + // _ => {} + // }, + // } + } + P2pAction::Disconnection(action) => { + // match action { + // P2pDisconnectionAction::Init { .. } => {} + // P2pDisconnectionAction::Finish { peer_id } => { + // if let Some(s) = store.state().transition_frontier.sync.ledger() { + // let snarked_ledger_num_accounts_rpc_id = s + // .snarked() + // .and_then(|s| s.peer_num_accounts_rpc_id(&peer_id)); + // let snarked_ledger_address_rpc_ids = s + // .snarked() + // .map(|s| s.peer_address_query_pending_rpc_ids(&peer_id).collect()) + // .unwrap_or(vec![]); + // let staged_ledger_parts_fetch_rpc_id = + // s.staged().and_then(|s| s.parts_fetch_rpc_id(&peer_id)); - for rpc_id in snarked_ledger_address_rpc_ids { - store.dispatch( - TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressError { - peer_id, - rpc_id, - error: PeerLedgerQueryError::Disconnected, - }, - ); - } + // for rpc_id in snarked_ledger_address_rpc_ids { + // store.dispatch( + // TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressError { + // peer_id, + // rpc_id, + // error: PeerLedgerQueryError::Disconnected, + // }, + // ); + // } - if let Some(rpc_id) = snarked_ledger_num_accounts_rpc_id { - store.dispatch( - TransitionFrontierSyncLedgerSnarkedAction::PeerQueryNumAccountsError { - peer_id, - rpc_id, - error: PeerLedgerQueryError::Disconnected, - }, - ); - } + // if let Some(rpc_id) = snarked_ledger_num_accounts_rpc_id { + // store.dispatch( + // TransitionFrontierSyncLedgerSnarkedAction::PeerQueryNumAccountsError { + // peer_id, + // rpc_id, + // error: PeerLedgerQueryError::Disconnected, + // }, + // ); + // } - if let Some(rpc_id) = staged_ledger_parts_fetch_rpc_id { - store.dispatch( - TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchError { - peer_id, - rpc_id, - error: PeerStagedLedgerPartsFetchError::Disconnected, - }, - ); - } - } + // if let Some(rpc_id) = staged_ledger_parts_fetch_rpc_id { + // store.dispatch( + // TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchError { + // peer_id, + // rpc_id, + // error: PeerStagedLedgerPartsFetchError::Disconnected, + // }, + // ); + // } + // } - let blocks_fetch_rpc_ids = store - .state() - .transition_frontier - .sync - .blocks_fetch_from_peer_pending_rpc_ids(&peer_id) - .collect::>(); + // let blocks_fetch_rpc_ids = store + // .state() + // .transition_frontier + // .sync + // .blocks_fetch_from_peer_pending_rpc_ids(&peer_id) + // .collect::>(); - for rpc_id in blocks_fetch_rpc_ids { - store.dispatch(TransitionFrontierSyncAction::BlocksPeerQueryError { - peer_id, - rpc_id, - error: PeerBlockFetchError::Disconnected, - }); - } + // for rpc_id in blocks_fetch_rpc_ids { + // store.dispatch(TransitionFrontierSyncAction::BlocksPeerQueryError { + // peer_id, + // rpc_id, + // error: PeerBlockFetchError::Disconnected, + // }); + // } - let actions = store - .state() - .watched_accounts - .iter() - .filter_map(|(pub_key, a)| match &a.initial_state { - WatchedAccountLedgerInitialState::Pending { - peer_id: account_peer_id, - .. - } => { - if account_peer_id == &peer_id { - Some(WatchedAccountsAction::LedgerInitialStateGetError { - pub_key: pub_key.clone(), - error: - WatchedAccountsLedgerInitialStateGetError::PeerDisconnected, - }) - } else { - None - } - } - _ => None, - }) - .collect::>(); + // let actions = store + // .state() + // .watched_accounts + // .iter() + // .filter_map(|(pub_key, a)| match &a.initial_state { + // WatchedAccountLedgerInitialState::Pending { + // peer_id: account_peer_id, + // .. + // } => { + // if account_peer_id == &peer_id { + // Some(WatchedAccountsAction::LedgerInitialStateGetError { + // pub_key: pub_key.clone(), + // error: + // WatchedAccountsLedgerInitialStateGetError::PeerDisconnected, + // }) + // } else { + // None + // } + // } + // _ => None, + // }) + // .collect::>(); - for action in actions { - store.dispatch(action); - } + // for action in actions { + // store.dispatch(action); + // } - store.dispatch(SnarkPoolCandidateAction::PeerPrune { peer_id }); - } - }, + // store.dispatch(SnarkPoolCandidateAction::PeerPrune { peer_id }); + // } + // } + } P2pAction::DisconnectionEffectful(action) => action.effects(&meta, store), P2pAction::Channels(action) => match action { P2pChannelsAction::MessageReceived(_) => { // handled by reducer } P2pChannelsAction::BestTip(action) => { - if let P2pChannelsBestTipAction::RequestReceived { peer_id } = action { - if let Some(best_tip) = store.state().transition_frontier.best_tip() { - store.dispatch(P2pChannelsBestTipAction::ResponseSend { - peer_id, - best_tip: best_tip.clone(), - }); - } - } + // if let P2pChannelsBestTipAction::RequestReceived { peer_id } = action { + // if let Some(best_tip) = store.state().transition_frontier.best_tip() { + // store.dispatch(P2pChannelsBestTipAction::ResponseSend { + // peer_id, + // best_tip: best_tip.clone(), + // }); + // } + // } } P2pChannelsAction::Transaction(action) => { match action { @@ -220,27 +224,27 @@ pub fn node_p2p_effects(store: &mut Store, action: P2pActionWithM transaction, nonce: _, } => { - store.dispatch(TransactionPoolAction::StartVerify { - // TODO: Take multiple transactions here - commands: [*transaction].into_iter().collect(), - from_rpc: None, - }); + // store.dispatch(TransactionPoolAction::StartVerify { + // // TODO: Take multiple transactions here + // commands: [*transaction].into_iter().collect(), + // from_rpc: None, + // }); } _ => {} } } P2pChannelsAction::Snark(action) => match action { P2pChannelsSnarkAction::Received { peer_id, snark } => { - store.dispatch(SnarkPoolCandidateAction::InfoReceived { - peer_id, - info: *snark, - }); + // store.dispatch(SnarkPoolCandidateAction::InfoReceived { + // peer_id, + // info: *snark, + // }); } P2pChannelsSnarkAction::Libp2pReceived { peer_id, snark, .. } => { - store.dispatch(SnarkPoolCandidateAction::WorkReceived { - peer_id, - work: *snark, - }); + // store.dispatch(SnarkPoolCandidateAction::WorkReceived { + // peer_id, + // work: *snark, + // }); } _ => {} }, @@ -250,305 +254,305 @@ pub fn node_p2p_effects(store: &mut Store, action: P2pActionWithM commitment, } = action { - store.dispatch(SnarkPoolAction::CommitmentAdd { - commitment: *commitment, - sender: peer_id, - }); + // store.dispatch(SnarkPoolAction::CommitmentAdd { + // commitment: *commitment, + // sender: peer_id, + // }); } } P2pChannelsAction::Rpc(action) => { match action { P2pChannelsRpcAction::Ready { peer_id } => { - store.dispatch(P2pChannelsRpcAction::RequestSend { - peer_id, - id: 0, - request: Box::new(P2pRpcRequest::BestTipWithProof), - on_init: None, - }); + // store.dispatch(P2pChannelsRpcAction::RequestSend { + // peer_id, + // id: 0, + // request: Box::new(P2pRpcRequest::BestTipWithProof), + // on_init: None, + // }); - store.dispatch(TransitionFrontierSyncLedgerSnarkedAction::PeersQuery); - store - .dispatch(TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit); - store.dispatch(TransitionFrontierSyncAction::BlocksPeersQuery); + // store.dispatch(TransitionFrontierSyncLedgerSnarkedAction::PeersQuery); + // store + // .dispatch(TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit); + // store.dispatch(TransitionFrontierSyncAction::BlocksPeersQuery); } P2pChannelsRpcAction::Timeout { peer_id, id } => { - let Some(peer) = store.state().p2p.get_ready_peer(&peer_id) else { - bug_condition!("get_ready_peer({:?}) returned None", peer_id); - return; - }; + // let Some(peer) = store.state().p2p.get_ready_peer(&peer_id) else { + // bug_condition!("get_ready_peer({:?}) returned None", peer_id); + // return; + // }; - let Some(rpc_kind) = peer.channels.rpc.pending_local_rpc_kind() else { - bug_condition!( - "peer: {:?} pending_local_rpc_kind() returned None", - peer_id - ); - return; - }; + // let Some(rpc_kind) = peer.channels.rpc.pending_local_rpc_kind() else { + // bug_condition!( + // "peer: {:?} pending_local_rpc_kind() returned None", + // peer_id + // ); + // return; + // }; - store.dispatch( - TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressError { - peer_id, - rpc_id: id, - error: PeerLedgerQueryError::Timeout, - }, - ); - store.dispatch( - TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchError { - peer_id, - rpc_id: id, - error: PeerStagedLedgerPartsFetchError::Timeout, - }, - ); - store.dispatch(TransitionFrontierSyncAction::BlocksPeerQueryError { - peer_id, - rpc_id: id, - error: PeerBlockFetchError::Timeout, - }); - store.dispatch(P2pDisconnectionAction::Init { - peer_id, - reason: P2pDisconnectionReason::TransitionFrontierRpcTimeout(rpc_kind), - }); + // store.dispatch( + // TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressError { + // peer_id, + // rpc_id: id, + // error: PeerLedgerQueryError::Timeout, + // }, + // ); + // store.dispatch( + // TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchError { + // peer_id, + // rpc_id: id, + // error: PeerStagedLedgerPartsFetchError::Timeout, + // }, + // ); + // store.dispatch(TransitionFrontierSyncAction::BlocksPeerQueryError { + // peer_id, + // rpc_id: id, + // error: PeerBlockFetchError::Timeout, + // }); + // store.dispatch(P2pDisconnectionAction::Init { + // peer_id, + // reason: P2pDisconnectionReason::TransitionFrontierRpcTimeout(rpc_kind), + // }); } P2pChannelsRpcAction::ResponseReceived { peer_id, id, response, } => { - match response.as_deref() { - None => { - store.dispatch( - TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressError { - peer_id, - rpc_id: id, - error: PeerLedgerQueryError::DataUnavailable, - }, - ); - store.dispatch( - TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchError { - peer_id, - rpc_id: id, - error: PeerStagedLedgerPartsFetchError::DataUnavailable, - }, - ); - store.dispatch( - TransitionFrontierSyncAction::BlocksPeerQueryError { - peer_id, - rpc_id: id, - error: PeerBlockFetchError::DataUnavailable, - }, - ); - } - Some(P2pRpcResponse::BestTipWithProof(resp)) => { - let (body_hashes, root_block) = &resp.proof; + // match response.as_deref() { + // None => { + // store.dispatch( + // TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressError { + // peer_id, + // rpc_id: id, + // error: PeerLedgerQueryError::DataUnavailable, + // }, + // ); + // store.dispatch( + // TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchError { + // peer_id, + // rpc_id: id, + // error: PeerStagedLedgerPartsFetchError::DataUnavailable, + // }, + // ); + // store.dispatch( + // TransitionFrontierSyncAction::BlocksPeerQueryError { + // peer_id, + // rpc_id: id, + // error: PeerBlockFetchError::DataUnavailable, + // }, + // ); + // } + // Some(P2pRpcResponse::BestTipWithProof(resp)) => { + // let (body_hashes, root_block) = &resp.proof; - let (Ok(best_tip), Ok(root_block)) = ( - BlockWithHash::try_new(resp.best_tip.clone()), - BlockWithHash::try_new(root_block.clone()), - ) else { - openmina_core::error!(meta.time(); "P2pRpcResponse::BestTipWithProof: invalid blocks"); - return; - }; + // let (Ok(best_tip), Ok(root_block)) = ( + // BlockWithHash::try_new(resp.best_tip.clone()), + // BlockWithHash::try_new(root_block.clone()), + // ) else { + // openmina_core::error!(meta.time(); "P2pRpcResponse::BestTipWithProof: invalid blocks"); + // return; + // }; - // reconstruct hashes - let Ok(hashes) = body_hashes - .iter() - .take(body_hashes.len().saturating_sub(1)) - .scan(root_block.hash.clone(), |pred_hash, body_hash| { - *pred_hash = match StateHash::try_from_hashes( - pred_hash, body_hash, - ) { - Ok(hash) => hash, - Err(_) => return Some(Err(InvalidBigInt)), - }; - Some(Ok(pred_hash.clone())) - }) - .collect::, _>>() - else { - openmina_core::error!(meta.time(); "P2pRpcResponse::BestTipWithProof: invalid hashes"); - return; - }; + // // reconstruct hashes + // let Ok(hashes) = body_hashes + // .iter() + // .take(body_hashes.len().saturating_sub(1)) + // .scan(root_block.hash.clone(), |pred_hash, body_hash| { + // *pred_hash = match StateHash::try_from_hashes( + // pred_hash, body_hash, + // ) { + // Ok(hash) => hash, + // Err(_) => return Some(Err(InvalidBigInt)), + // }; + // Some(Ok(pred_hash.clone())) + // }) + // .collect::, _>>() + // else { + // openmina_core::error!(meta.time(); "P2pRpcResponse::BestTipWithProof: invalid hashes"); + // return; + // }; - if let Some(pred_hash) = hashes.last() { - let expected_hash = - &best_tip.block.header.protocol_state.previous_state_hash; - if pred_hash != expected_hash { - openmina_core::warn!(meta.time(); - kind = "P2pRpcBestTipHashMismatch", - response = serde_json::to_string(&resp).ok(), - expected_hash = expected_hash.to_string(), - calculated_hash = pred_hash.to_string()); - return; - } - } - store.dispatch(ConsensusAction::BlockChainProofUpdate { - hash: best_tip.hash, - chain_proof: (hashes, root_block), - }); - } - Some(P2pRpcResponse::LedgerQuery(answer)) => match answer { - MinaLedgerSyncLedgerAnswerStableV2::ChildHashesAre(left, right) => { - store.dispatch( - TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressSuccess { - peer_id, - rpc_id: id, - response: PeerLedgerQueryResponse::ChildHashes( - left.clone(), - right.clone(), - ), - }, - ); - } - MinaLedgerSyncLedgerAnswerStableV2::ContentsAre(accounts) => { - store.dispatch( - TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressSuccess { - peer_id, - rpc_id: id, - response: PeerLedgerQueryResponse::ChildAccounts( - accounts.iter().cloned().collect(), - ), - }, - ); - } - MinaLedgerSyncLedgerAnswerStableV2::NumAccounts( - count, - contents_hash, - ) => { - store.dispatch( - TransitionFrontierSyncLedgerSnarkedAction::PeerQueryNumAccountsSuccess { - peer_id, - rpc_id: id, - response: PeerLedgerQueryResponse::NumAccounts( - count.as_u64(), contents_hash.clone() - ), - }, - ); - } - }, - Some(P2pRpcResponse::StagedLedgerAuxAndPendingCoinbasesAtBlock( - parts, - )) => { - store.dispatch( - TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchSuccess { - peer_id, - rpc_id: id, - parts: parts.clone(), - }, - ); - } - Some(P2pRpcResponse::Block(block)) => { - let Ok(block) = BlockWithHash::try_new(block.clone()) else { - openmina_core::error!(meta.time(); "P2pRpcResponse::Block: invalid block"); - return; - }; - store.dispatch( - TransitionFrontierSyncAction::BlocksPeerQuerySuccess { - peer_id, - rpc_id: id, - response: block, - }, - ); - } - Some(P2pRpcResponse::Snark(snark)) => { - store.dispatch(SnarkPoolCandidateAction::WorkReceived { - peer_id, - work: snark.clone(), - }); - } - Some(P2pRpcResponse::InitialPeers(_)) => {} - } - store.dispatch(TransitionFrontierSyncLedgerSnarkedAction::PeersQuery); - store.dispatch( - TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit {}, - ); - store.dispatch(TransitionFrontierSyncAction::BlocksPeersQuery); + // if let Some(pred_hash) = hashes.last() { + // let expected_hash = + // &best_tip.block.header.protocol_state.previous_state_hash; + // if pred_hash != expected_hash { + // openmina_core::warn!(meta.time(); + // kind = "P2pRpcBestTipHashMismatch", + // response = serde_json::to_string(&resp).ok(), + // expected_hash = expected_hash.to_string(), + // calculated_hash = pred_hash.to_string()); + // return; + // } + // } + // store.dispatch(ConsensusAction::BlockChainProofUpdate { + // hash: best_tip.hash, + // chain_proof: (hashes, root_block), + // }); + // } + // Some(P2pRpcResponse::LedgerQuery(answer)) => match answer { + // MinaLedgerSyncLedgerAnswerStableV2::ChildHashesAre(left, right) => { + // store.dispatch( + // TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressSuccess { + // peer_id, + // rpc_id: id, + // response: PeerLedgerQueryResponse::ChildHashes( + // left.clone(), + // right.clone(), + // ), + // }, + // ); + // } + // MinaLedgerSyncLedgerAnswerStableV2::ContentsAre(accounts) => { + // store.dispatch( + // TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressSuccess { + // peer_id, + // rpc_id: id, + // response: PeerLedgerQueryResponse::ChildAccounts( + // accounts.iter().cloned().collect(), + // ), + // }, + // ); + // } + // MinaLedgerSyncLedgerAnswerStableV2::NumAccounts( + // count, + // contents_hash, + // ) => { + // store.dispatch( + // TransitionFrontierSyncLedgerSnarkedAction::PeerQueryNumAccountsSuccess { + // peer_id, + // rpc_id: id, + // response: PeerLedgerQueryResponse::NumAccounts( + // count.as_u64(), contents_hash.clone() + // ), + // }, + // ); + // } + // }, + // Some(P2pRpcResponse::StagedLedgerAuxAndPendingCoinbasesAtBlock( + // parts, + // )) => { + // store.dispatch( + // TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchSuccess { + // peer_id, + // rpc_id: id, + // parts: parts.clone(), + // }, + // ); + // } + // Some(P2pRpcResponse::Block(block)) => { + // let Ok(block) = BlockWithHash::try_new(block.clone()) else { + // openmina_core::error!(meta.time(); "P2pRpcResponse::Block: invalid block"); + // return; + // }; + // store.dispatch( + // TransitionFrontierSyncAction::BlocksPeerQuerySuccess { + // peer_id, + // rpc_id: id, + // response: block, + // }, + // ); + // } + // Some(P2pRpcResponse::Snark(snark)) => { + // store.dispatch(SnarkPoolCandidateAction::WorkReceived { + // peer_id, + // work: snark.clone(), + // }); + // } + // Some(P2pRpcResponse::InitialPeers(_)) => {} + // } + // store.dispatch(TransitionFrontierSyncLedgerSnarkedAction::PeersQuery); + // store.dispatch( + // TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit {}, + // ); + // store.dispatch(TransitionFrontierSyncAction::BlocksPeersQuery); } P2pChannelsRpcAction::RequestReceived { peer_id, id, request, } => { - match *request { - P2pRpcRequest::BestTipWithProof => { - let best_chain = &store.state().transition_frontier.best_chain; - let response = None.or_else(|| { - let best_tip = best_chain.last()?; - let mut chain_iter = best_chain.iter(); - let root_block = chain_iter.next()?; - // TODO(binier): cache body hashes - let Ok(body_hashes) = chain_iter - .map(|b| b.header().protocol_state.body.try_hash()) - .collect::>() - else { - openmina_core::error!(meta.time(); "P2pRpcRequest::BestTipWithProof: invalid protocol state"); - return None; - }; + // match *request { + // P2pRpcRequest::BestTipWithProof => { + // let best_chain = &store.state().transition_frontier.best_chain; + // let response = None.or_else(|| { + // let best_tip = best_chain.last()?; + // let mut chain_iter = best_chain.iter(); + // let root_block = chain_iter.next()?; + // // TODO(binier): cache body hashes + // let Ok(body_hashes) = chain_iter + // .map(|b| b.header().protocol_state.body.try_hash()) + // .collect::>() + // else { + // openmina_core::error!(meta.time(); "P2pRpcRequest::BestTipWithProof: invalid protocol state"); + // return None; + // }; - Some(BestTipWithProof { - best_tip: best_tip.block().clone(), - proof: (body_hashes, root_block.block().clone()), - }) - }); - let response = - response.map(P2pRpcResponse::BestTipWithProof).map(Box::new); - store.dispatch(P2pChannelsRpcAction::ResponseSend { - peer_id, - id, - response, - }); - } - P2pRpcRequest::Block(hash) => { - let best_chain = &store.state().transition_frontier.best_chain; - let response = best_chain - .iter() - .rev() - .find(|b| b.hash() == &hash) - .map(|b| b.block().clone()) - .map(P2pRpcResponse::Block) - .map(Box::new); - store.dispatch(P2pChannelsRpcAction::ResponseSend { - peer_id, - id, - response, - }); - } - P2pRpcRequest::LedgerQuery(..) => { - // async ledger request will be triggered - // by `LedgerReadAction::FindTodos`. - } - P2pRpcRequest::StagedLedgerAuxAndPendingCoinbasesAtBlock(..) => { - // async ledger request will be triggered - // by `LedgerReadAction::FindTodos`. - } - P2pRpcRequest::Snark(job_id) => { - let job = store.state().snark_pool.get(&job_id); - let response = job - .and_then(|job| job.snark.as_ref()) - .map(|snark| snark.work.clone()) - .map(P2pRpcResponse::Snark) - .map(Box::new); + // Some(BestTipWithProof { + // best_tip: best_tip.block().clone(), + // proof: (body_hashes, root_block.block().clone()), + // }) + // }); + // let response = + // response.map(P2pRpcResponse::BestTipWithProof).map(Box::new); + // store.dispatch(P2pChannelsRpcAction::ResponseSend { + // peer_id, + // id, + // response, + // }); + // } + // P2pRpcRequest::Block(hash) => { + // let best_chain = &store.state().transition_frontier.best_chain; + // let response = best_chain + // .iter() + // .rev() + // .find(|b| b.hash() == &hash) + // .map(|b| b.block().clone()) + // .map(P2pRpcResponse::Block) + // .map(Box::new); + // store.dispatch(P2pChannelsRpcAction::ResponseSend { + // peer_id, + // id, + // response, + // }); + // } + // P2pRpcRequest::LedgerQuery(..) => { + // // async ledger request will be triggered + // // by `LedgerReadAction::FindTodos`. + // } + // P2pRpcRequest::StagedLedgerAuxAndPendingCoinbasesAtBlock(..) => { + // // async ledger request will be triggered + // // by `LedgerReadAction::FindTodos`. + // } + // P2pRpcRequest::Snark(job_id) => { + // let job = store.state().snark_pool.get(&job_id); + // let response = job + // .and_then(|job| job.snark.as_ref()) + // .map(|snark| snark.work.clone()) + // .map(P2pRpcResponse::Snark) + // .map(Box::new); - store.dispatch(P2pChannelsRpcAction::ResponseSend { - peer_id, - id, - response, - }); - } - P2pRpcRequest::InitialPeers => { - let p2p = p2p_ready!(store.state().p2p, meta.time()); - let peers = p2p - .peers - .iter() - .filter_map(|(_, v)| v.dial_opts.clone()) - .collect(); - let response = Some(Box::new(P2pRpcResponse::InitialPeers(peers))); + // store.dispatch(P2pChannelsRpcAction::ResponseSend { + // peer_id, + // id, + // response, + // }); + // } + // P2pRpcRequest::InitialPeers => { + // let p2p = p2p_ready!(store.state().p2p, meta.time()); + // let peers = p2p + // .peers + // .iter() + // .filter_map(|(_, v)| v.dial_opts.clone()) + // .collect(); + // let response = Some(Box::new(P2pRpcResponse::InitialPeers(peers))); - store.dispatch(P2pChannelsRpcAction::ResponseSend { - peer_id, - id, - response, - }); - } - } + // store.dispatch(P2pChannelsRpcAction::ResponseSend { + // peer_id, + // id, + // response, + // }); + // } + // } } P2pChannelsRpcAction::Init { .. } => {} P2pChannelsRpcAction::Pending { .. } => {} @@ -560,64 +564,64 @@ pub fn node_p2p_effects(store: &mut Store, action: P2pActionWithM P2pChannelsAction::StreamingRpc(action) => { match action { P2pChannelsStreamingRpcAction::Ready { .. } => { - store - .dispatch(TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit); + // store + // .dispatch(TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit); } P2pChannelsStreamingRpcAction::Timeout { peer_id, id } => { - let Some(peer) = store.state().p2p.get_ready_peer(&peer_id) else { - bug_condition!("get_ready_peer({:?}) returned None", peer_id); - return; - }; - let Some(rpc_kind) = peer.channels.streaming_rpc.pending_local_rpc_kind() - else { - bug_condition!( - "peer: {:?} pending_local_rpc_kind() returned None", - peer_id - ); - return; - }; - store.dispatch( - TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchError { - peer_id, - rpc_id: id, - error: PeerStagedLedgerPartsFetchError::Timeout, - }, - ); - store.dispatch(P2pDisconnectionAction::Init { - peer_id, - reason: P2pDisconnectionReason::TransitionFrontierStreamingRpcTimeout( - rpc_kind, - ), - }); + // let Some(peer) = store.state().p2p.get_ready_peer(&peer_id) else { + // bug_condition!("get_ready_peer({:?}) returned None", peer_id); + // return; + // }; + // let Some(rpc_kind) = peer.channels.streaming_rpc.pending_local_rpc_kind() + // else { + // bug_condition!( + // "peer: {:?} pending_local_rpc_kind() returned None", + // peer_id + // ); + // return; + // }; + // store.dispatch( + // TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchError { + // peer_id, + // rpc_id: id, + // error: PeerStagedLedgerPartsFetchError::Timeout, + // }, + // ); + // store.dispatch(P2pDisconnectionAction::Init { + // peer_id, + // reason: P2pDisconnectionReason::TransitionFrontierStreamingRpcTimeout( + // rpc_kind, + // ), + // }); } P2pChannelsStreamingRpcAction::ResponseReceived { peer_id, id, response, } => { - match response { - None => { - store.dispatch( - TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchError { - peer_id, - rpc_id: id, - error: PeerStagedLedgerPartsFetchError::DataUnavailable, - }, - ); - } - Some(P2pStreamingRpcResponseFull::StagedLedgerParts(parts)) => { - store.dispatch( - TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchSuccess { - peer_id, - rpc_id: id, - parts, - }, - ); - } - } - store.dispatch( - TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit {}, - ); + // match response { + // None => { + // store.dispatch( + // TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchError { + // peer_id, + // rpc_id: id, + // error: PeerStagedLedgerPartsFetchError::DataUnavailable, + // }, + // ); + // } + // Some(P2pStreamingRpcResponseFull::StagedLedgerParts(parts)) => { + // store.dispatch( + // TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchSuccess { + // peer_id, + // rpc_id: id, + // parts, + // }, + // ); + // } + // } + // store.dispatch( + // TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit {}, + // ); } P2pChannelsStreamingRpcAction::RequestReceived { peer_id: _, @@ -650,14 +654,14 @@ pub fn node_p2p_effects(store: &mut Store, action: P2pActionWithM // handled by reducer } P2pPeerAction::BestTipUpdate { best_tip, .. } => { - store.dispatch(ConsensusAction::BlockReceived { - hash: best_tip.hash, - block: best_tip.block, - chain_proof: None, - }); - store.dispatch(TransitionFrontierSyncLedgerSnarkedAction::PeersQuery); - store.dispatch(TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit); - store.dispatch(TransitionFrontierSyncAction::BlocksPeersQuery); + // store.dispatch(ConsensusAction::BlockReceived { + // hash: best_tip.hash, + // block: best_tip.block, + // chain_proof: None, + // }); + // store.dispatch(TransitionFrontierSyncLedgerSnarkedAction::PeersQuery); + // store.dispatch(TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit); + // store.dispatch(TransitionFrontierSyncAction::BlocksPeersQuery); } }, P2pAction::Identify(_) => { diff --git a/node/src/reducer.rs b/node/src/reducer.rs index 885c09c2ef..e59000ee1d 100644 --- a/node/src/reducer.rs +++ b/node/src/reducer.rs @@ -94,6 +94,9 @@ pub fn reducer( meta.with_action(a), ); } + Action::P2pCallbacks(action) => { + State::p2p_callback_reducer(Substate::new(state, dispatcher), meta.with_action(action)) + } } // must be the last. diff --git a/node/src/rpc/rpc_actions.rs b/node/src/rpc/rpc_actions.rs index 9979149d40..8763f71a4e 100644 --- a/node/src/rpc/rpc_actions.rs +++ b/node/src/rpc/rpc_actions.rs @@ -6,6 +6,7 @@ use openmina_core::block::AppliedBlock; use openmina_core::snark::SnarkJobId; use openmina_core::ActionEvent; use openmina_node_account::AccountPublicKey; +use p2p::PeerId; use serde::{Deserialize, Serialize}; use crate::external_snark_worker::SnarkWorkId; @@ -78,6 +79,11 @@ pub enum RpcAction { rpc_id: RpcId, response: P2pConnectionResponse, }, + P2pConnectionIncomingAnswerReady { + rpc_id: RpcId, + peer_id: PeerId, + answer: P2pConnectionResponse, + }, P2pConnectionIncomingError { rpc_id: RpcId, error: String, @@ -248,7 +254,8 @@ impl redux::EnablingCondition for RpcAction { .requests .get(rpc_id) .map_or(false, |v| v.status.is_init()), - RpcAction::P2pConnectionIncomingRespond { rpc_id, .. } => state + RpcAction::P2pConnectionIncomingRespond { rpc_id, .. } + | RpcAction::P2pConnectionIncomingAnswerReady { rpc_id, .. } => state .rpc .requests .get(rpc_id) diff --git a/node/src/rpc/rpc_effects.rs b/node/src/rpc/rpc_effects.rs index a186637b62..ea15efaf89 100644 --- a/node/src/rpc/rpc_effects.rs +++ b/node/src/rpc/rpc_effects.rs @@ -279,6 +279,17 @@ pub fn rpc_effects(store: &mut Store, action: RpcActionWithMeta) } } RpcAction::P2pConnectionIncomingPending { .. } => {} + RpcAction::P2pConnectionIncomingAnswerReady { + rpc_id, + answer, + peer_id, + } => { + store.dispatch(RpcAction::P2pConnectionIncomingRespond { + rpc_id, + response: answer, + }); + store.dispatch(P2pConnectionIncomingAction::AnswerSendSuccess { peer_id }); + } RpcAction::P2pConnectionIncomingRespond { rpc_id, response } => { let error = match &response { P2pConnectionResponse::Accepted(_) => None, diff --git a/node/src/rpc/rpc_reducer.rs b/node/src/rpc/rpc_reducer.rs index 7fd43e9f8d..3a4c25a4b9 100644 --- a/node/src/rpc/rpc_reducer.rs +++ b/node/src/rpc/rpc_reducer.rs @@ -171,6 +171,7 @@ impl RpcState { RpcAction::BestChain { .. } => {} RpcAction::ConsensusConstantsGet { .. } => {} RpcAction::TransactionStatusGet { .. } => {} + RpcAction::P2pConnectionIncomingAnswerReady { .. } => {}, } } } diff --git a/node/src/state.rs b/node/src/state.rs index 4430b42fd4..fb0b120c34 100644 --- a/node/src/state.rs +++ b/node/src/state.rs @@ -1,9 +1,22 @@ -use openmina_core::block::ArcBlockWithHash; -use openmina_core::consensus::ConsensusConstants; -use openmina_core::{constants::constraint_constants, error, ChainId}; -use p2p::bootstrap::P2pNetworkKadBootstrapState; -use p2p::network::identify::P2pNetworkIdentifyState; -use p2p::{P2pConfig, P2pNetworkSchedulerState, P2pPeerState, P2pPeerStatusReady, PeerId}; +use std::sync::Arc; + +use mina_p2p_messages::v2::{MinaBaseUserCommandStableV2, MinaBlockBlockStableV2}; + +use openmina_core::block::BlockWithHash; +use openmina_core::requests::{RequestId, RpcIdType}; +use openmina_core::snark::{Snark, SnarkInfo}; +use openmina_core::{ + block::ArcBlockWithHash, consensus::ConsensusConstants, constants::constraint_constants, error, + snark::SnarkJobCommitment, ChainId, +}; +use p2p::channels::rpc::{P2pRpcId, P2pRpcRequest, P2pRpcResponse}; +use p2p::channels::streaming_rpc::P2pStreamingRpcResponseFull; +use p2p::connection::outgoing::P2pConnectionOutgoingError; +use p2p::connection::P2pConnectionResponse; +use p2p::{ + bootstrap::P2pNetworkKadBootstrapState, network::identify::P2pNetworkIdentifyState, + P2pCallbacks, P2pConfig, P2pNetworkSchedulerState, P2pPeerState, P2pPeerStatusReady, PeerId, +}; use redux::{ActionMeta, EnablingCondition, Timestamp}; use serde::{Deserialize, Serialize}; use snark::block_verify::SnarkBlockVerifyState; @@ -11,25 +24,33 @@ use snark::user_command_verify::SnarkUserCommandVerifyState; use snark::work_verify::SnarkWorkVerifyState; pub use crate::block_producer::BlockProducerState; -use crate::config::GlobalConfig; pub use crate::consensus::ConsensusState; use crate::external_snark_worker::ExternalSnarkWorkers; pub use crate::ledger::LedgerState; +use crate::p2p::callbacks::P2pCallbacksAction; pub use crate::p2p::P2pState; pub use crate::rpc::RpcState; pub use crate::snark::SnarkState; +use crate::snark_pool::candidate::SnarkPoolCandidateAction; pub use crate::snark_pool::candidate::SnarkPoolCandidatesState; pub use crate::snark_pool::SnarkPoolState; use crate::transaction_pool::TransactionPoolState; use crate::transition_frontier::genesis::TransitionFrontierGenesisState; -use crate::transition_frontier::sync::ledger::snarked::TransitionFrontierSyncLedgerSnarkedState; -use crate::transition_frontier::sync::ledger::staged::TransitionFrontierSyncLedgerStagedState; +use crate::transition_frontier::sync::ledger::snarked::{ + TransitionFrontierSyncLedgerSnarkedAction, TransitionFrontierSyncLedgerSnarkedState, +}; +use crate::transition_frontier::sync::ledger::staged::{ + TransitionFrontierSyncLedgerStagedAction, TransitionFrontierSyncLedgerStagedState, +}; use crate::transition_frontier::sync::ledger::TransitionFrontierSyncLedgerState; use crate::transition_frontier::sync::TransitionFrontierSyncState; pub use crate::transition_frontier::TransitionFrontierState; pub use crate::watched_accounts::WatchedAccountsState; -use crate::ActionWithMeta; pub use crate::Config; +use crate::{config::GlobalConfig, SnarkPoolAction}; +use crate::{ + ActionWithMeta, ConsensusAction, RpcAction, TransactionPoolAction, TransitionFrontierAction, +}; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct State { @@ -54,7 +75,7 @@ pub struct State { } // Substate accessors that will be used in reducers -use openmina_core::impl_substate_access; +use openmina_core::{impl_substate_access, SubstateAccess}; impl_substate_access!(State, SnarkState, snark); impl_substate_access!(State, SnarkBlockVerifyState, snark.block_verify); @@ -164,6 +185,16 @@ impl openmina_core::SubstateAccess for } } +impl SubstateAccess for State { + fn substate(&self) -> openmina_core::SubstateResult<&State> { + Ok(self) + } + + fn substate_mut(&mut self) -> openmina_core::SubstateResult<&mut State> { + Ok(self) + } +} + macro_rules! impl_p2p_state_access { ($state:ty, $substate_type:ty) => { impl openmina_core::SubstateAccess<$substate_type> for $state { @@ -322,7 +353,106 @@ impl P2p { let P2p::Pending(config) = self else { return Err(P2pInitializationError::AlreadyInitialized); }; - *self = P2p::Ready(P2pState::new(config.clone(), chain_id)); + + let callbacks = P2pCallbacks { + on_p2p_channels_transaction_libp2p_received: Some(redux::callback!( + on_p2p_channels_transaction_libp2p_received(transaction: Box) -> crate::Action{ + TransactionPoolAction::StartVerify { commands: std::iter::once(*transaction).collect(), from_rpc: None } + } + )), + on_p2p_channels_snark_job_commitment_received: Some(redux::callback!( + on_p2p_channels_snark_job_commitment_received((peer_id: PeerId, commitment: Box)) -> crate::Action{ + SnarkPoolAction::CommitmentAdd { commitment: *commitment, sender: peer_id } + } + )), + on_p2p_channels_snark_received: Some(redux::callback!( + on_p2p_channels_snark_received((peer_id: PeerId, snark: Box)) -> crate::Action{ + SnarkPoolCandidateAction::InfoReceived { peer_id, info: *snark } + } + )), + on_p2p_channels_snark_libp2p_received: Some(redux::callback!( + on_p2p_channels_snark_received((peer_id: PeerId, snark: Box)) -> crate::Action{ + SnarkPoolCandidateAction::WorkReceived { peer_id, work: *snark } + } + )), + on_p2p_channels_streaming_rpc_ready: Some(redux::callback!( + on_p2p_channels_streaming_rpc_ready(_var: ()) -> crate::Action{ + P2pCallbacksAction::P2pChannelsStreamingRpcReady + } + )), + on_p2p_channels_best_tip_request_received: Some(redux::callback!( + on_p2p_channels_best_tip_request_received(peer_id: PeerId) -> crate::Action{ + TransitionFrontierAction::RpcRespondBestTip{peer_id} + } + )), + on_p2p_disconnection_finish: Some(redux::callback!( + on_p2p_disconnection_finish(peer_id: PeerId) -> crate::Action{ + TransitionFrontierSyncLedgerSnarkedAction::P2pDisconnection { peer_id } + } + )), + on_p2p_connection_outgoing_error: Some(redux::callback!( + on_p2p_connection_outgoing_error((rpc_id: RequestId, error: P2pConnectionOutgoingError)) -> crate::Action{ + RpcAction::P2pConnectionOutgoingError { rpc_id, error } + } + )), + on_p2p_connection_outgoing_success: Some(redux::callback!( + on_p2p_connection_outgoing_success(rpc_id: RequestId) -> crate::Action{ + RpcAction::P2pConnectionOutgoingSuccess { rpc_id } + } + )), + on_p2p_connection_incoming_error: Some(redux::callback!( + on_p2p_connection_incoming_error((rpc_id: RequestId, error: String)) -> crate::Action{ + RpcAction::P2pConnectionIncomingError { rpc_id, error } + } + )), + on_p2p_connection_incoming_success: Some(redux::callback!( + on_p2p_connection_incoming_success(rpc_id: RequestId) -> crate::Action{ + RpcAction::P2pConnectionIncomingSuccess { rpc_id } + } + )), + on_p2p_connection_incoming_answer_ready: Some(redux::callback!( + on_p2p_connection_incoming_answer_ready((rpc_id: RequestId, peer_id: PeerId, answer: P2pConnectionResponse)) -> crate::Action{ + RpcAction::P2pConnectionIncomingAnswerReady { rpc_id, answer, peer_id } + } + )), + on_p2p_peer_best_tip_update: Some(redux::callback!( + on_p2p_peer_best_tip_update(best_tip: BlockWithHash>) -> crate::Action{ + ConsensusAction::P2pBestTipUpdate{best_tip} + } + )), + on_p2p_channels_rpc_ready: Some(redux::callback!( + on_p2p_channels_rpc_ready(peer_id: PeerId) -> crate::Action{ + P2pCallbacksAction::P2pChannelsRpcReady {peer_id} + } + )), + on_p2p_channels_rpc_timeout: Some(redux::callback!( + on_p2p_channels_rpc_timeout((peer_id: PeerId, id: P2pRpcId)) -> crate::Action{ + P2pCallbacksAction::P2pChannelsRpcTimeout { peer_id, id } + } + )), + on_p2p_channels_rpc_response_received: Some(redux::callback!( + on_p2p_channels_rpc_response_received((peer_id: PeerId, id: P2pRpcId, response: Option>)) -> crate::Action{ + P2pCallbacksAction::P2pChannelsRpcResponseReceived {peer_id, id, response} + } + )), + on_p2p_channels_rpc_request_received: Some(redux::callback!( + on_p2p_channels_rpc_request_received((peer_id: PeerId, id: P2pRpcId, request: Box)) -> crate::Action{ + P2pCallbacksAction::P2pChannelsRpcRequestReceived {peer_id, id, request} + } + )), + on_p2p_channels_streaming_rpc_response_received: Some(redux::callback!( + on_p2p_channels_streaming_rpc_response_received((peer_id: PeerId, id: P2pRpcId, response: Option)) -> crate::Action{ + P2pCallbacksAction::P2pChannelsStreamingRpcResponseReceived {peer_id, id, response} + } + )), + on_p2p_channels_streaming_rpc_timeout: Some(redux::callback!( + on_p2p_channels_streaming_rpc_timeout((peer_id: PeerId, id: P2pRpcId)) -> crate::Action{ + P2pCallbacksAction::P2pChannelsStreamingRpcTimeout { peer_id, id } + } + )), + }; + + *self = P2p::Ready(P2pState::new(config.clone(), callbacks, chain_id)); Ok(()) } diff --git a/node/src/transition_frontier/sync/ledger/snarked/transition_frontier_sync_ledger_snarked_actions.rs b/node/src/transition_frontier/sync/ledger/snarked/transition_frontier_sync_ledger_snarked_actions.rs index 331cf7bf08..71da5df805 100644 --- a/node/src/transition_frontier/sync/ledger/snarked/transition_frontier_sync_ledger_snarked_actions.rs +++ b/node/src/transition_frontier/sync/ledger/snarked/transition_frontier_sync_ledger_snarked_actions.rs @@ -130,6 +130,10 @@ pub enum TransitionFrontierSyncLedgerSnarkedAction { MerkleTreeSyncSuccess, #[action_event(level = info)] Success, + + P2pDisconnection { + peer_id: PeerId, + }, } impl redux::EnablingCondition for TransitionFrontierSyncLedgerSnarkedAction { @@ -450,6 +454,7 @@ impl redux::EnablingCondition for TransitionFrontierSyncLedgerSnar TransitionFrontierSyncLedgerSnarkedState::MerkleTreeSyncSuccess { .. } ) }), + TransitionFrontierSyncLedgerSnarkedAction::P2pDisconnection { .. } => true, } } } diff --git a/node/src/transition_frontier/sync/ledger/snarked/transition_frontier_sync_ledger_snarked_effects.rs b/node/src/transition_frontier/sync/ledger/snarked/transition_frontier_sync_ledger_snarked_effects.rs index 51be57d2f8..62146ef96a 100644 --- a/node/src/transition_frontier/sync/ledger/snarked/transition_frontier_sync_ledger_snarked_effects.rs +++ b/node/src/transition_frontier/sync/ledger/snarked/transition_frontier_sync_ledger_snarked_effects.rs @@ -34,6 +34,8 @@ impl TransitionFrontierSyncLedgerSnarkedAction { TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressError { .. } => {} TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressSuccess { .. } => {} + TransitionFrontierSyncLedgerSnarkedAction::P2pDisconnection { .. } => {} + TransitionFrontierSyncLedgerSnarkedAction::ChildHashesReceived { address, hashes: (left_hash, right_hash), diff --git a/node/src/transition_frontier/sync/ledger/snarked/transition_frontier_sync_ledger_snarked_reducer.rs b/node/src/transition_frontier/sync/ledger/snarked/transition_frontier_sync_ledger_snarked_reducer.rs index 0a8bcd0951..1a6d143f6f 100644 --- a/node/src/transition_frontier/sync/ledger/snarked/transition_frontier_sync_ledger_snarked_reducer.rs +++ b/node/src/transition_frontier/sync/ledger/snarked/transition_frontier_sync_ledger_snarked_reducer.rs @@ -11,11 +11,21 @@ use crate::{ ledger::{ ledger_empty_hash_at_depth, tree_height_for_num_accounts, LedgerAddress, LEDGER_DEPTH, }, - Action, State, + snark_pool::candidate::SnarkPoolCandidateAction, + transition_frontier::sync::{ + ledger::staged::{ + PeerStagedLedgerPartsFetchError, TransitionFrontierSyncLedgerStagedAction, + }, + PeerBlockFetchError, TransitionFrontierSyncAction, + }, + watched_accounts::{ + WatchedAccountLedgerInitialState, WatchedAccountsLedgerInitialStateGetError, + }, + Action, State, WatchedAccountsAction, }; use super::{ - LedgerAddressQueryPending, PeerLedgerQueryResponse, PeerRpcState, + LedgerAddressQueryPending, PeerLedgerQueryError, PeerLedgerQueryResponse, PeerRpcState, TransitionFrontierSyncLedgerSnarkedAction, TransitionFrontierSyncLedgerSnarkedActionWithMetaRef, TransitionFrontierSyncLedgerSnarkedState, ACCOUNT_SUBTREE_HEIGHT, @@ -601,6 +611,88 @@ impl TransitionFrontierSyncLedgerSnarkedState { target: target.clone(), }; } + TransitionFrontierSyncLedgerSnarkedAction::P2pDisconnection { peer_id } => { + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let peer_id = *peer_id; + + if let Some(s) = state.transition_frontier.sync.ledger() { + s.snarked() + .map(|s| { + s.peer_address_query_pending_rpc_ids(&peer_id) + .collect::>() + }) + .unwrap_or_default() + .into_iter() + .for_each(|rpc_id| { + dispatcher.push( + TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressError { + peer_id, + rpc_id, + error: PeerLedgerQueryError::Disconnected, + }, + ); + }); + + if let Some(rpc_id) = s + .snarked() + .and_then(|s| s.peer_num_accounts_rpc_id(&peer_id)) + { + dispatcher.push( + TransitionFrontierSyncLedgerSnarkedAction::PeerQueryNumAccountsError { + peer_id, + rpc_id, + error: PeerLedgerQueryError::Disconnected, + }, + ); + } + + if let Some(rpc_id) = s.staged().and_then(|s| s.parts_fetch_rpc_id(&peer_id)) { + dispatcher.push( + TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchError { + peer_id, + rpc_id, + error: PeerStagedLedgerPartsFetchError::Disconnected, + }, + ) + } + } + + state + .transition_frontier + .sync + .blocks_fetch_from_peer_pending_rpc_ids(&peer_id) + .for_each(|rpc_id| { + dispatcher.push(TransitionFrontierSyncAction::BlocksPeerQueryError { + peer_id, + rpc_id, + error: PeerBlockFetchError::Disconnected, + }); + }); + + state + .watched_accounts + .iter() + .filter_map(|(pub_key, a)| match &a.initial_state { + WatchedAccountLedgerInitialState::Pending { + peer_id: account_peer_id, + .. + } => { + if account_peer_id == &peer_id { + Some(WatchedAccountsAction::LedgerInitialStateGetError { + pub_key: pub_key.clone(), + error: + WatchedAccountsLedgerInitialStateGetError::PeerDisconnected, + }) + } else { + None + } + } + _ => None, + }) + .for_each(|action| dispatcher.push(action)); + + dispatcher.push(SnarkPoolCandidateAction::PeerPrune { peer_id }); + } } } } diff --git a/node/src/transition_frontier/transition_frontier_actions.rs b/node/src/transition_frontier/transition_frontier_actions.rs index bdd6c4f73e..6ca2c95498 100644 --- a/node/src/transition_frontier/transition_frontier_actions.rs +++ b/node/src/transition_frontier/transition_frontier_actions.rs @@ -3,6 +3,7 @@ use std::collections::BTreeSet; use mina_p2p_messages::v2::StateHash; use openmina_core::block::ArcBlockWithHash; use openmina_core::ActionEvent; +use p2p::PeerId; use serde::{Deserialize, Serialize}; use super::genesis::TransitionFrontierGenesisAction; @@ -36,6 +37,10 @@ pub enum TransitionFrontierAction { best_tip: ArcBlockWithHash, error: SyncError, }, + + RpcRespondBestTip { + peer_id: PeerId, + }, } impl redux::EnablingCondition for TransitionFrontierAction { @@ -69,6 +74,9 @@ impl redux::EnablingCondition for TransitionFrontierAction { .map_or(false, |s| s.is_apply_error()), } } + TransitionFrontierAction::RpcRespondBestTip { .. } => { + state.transition_frontier.best_tip().is_some() + } } } } diff --git a/node/src/transition_frontier/transition_frontier_effects.rs b/node/src/transition_frontier/transition_frontier_effects.rs index 07428a03ae..83dfee1201 100644 --- a/node/src/transition_frontier/transition_frontier_effects.rs +++ b/node/src/transition_frontier/transition_frontier_effects.rs @@ -266,6 +266,7 @@ pub fn transition_frontier_effects( TransitionFrontierAction::SyncFailed { .. } => { // TODO(SEC): disconnect/blacklist peers that caused this. } + TransitionFrontierAction::RpcRespondBestTip { .. } => {} } } diff --git a/node/src/transition_frontier/transition_frontier_reducer.rs b/node/src/transition_frontier/transition_frontier_reducer.rs index 8aa5d0fedf..0fc048b782 100644 --- a/node/src/transition_frontier/transition_frontier_reducer.rs +++ b/node/src/transition_frontier/transition_frontier_reducer.rs @@ -1,4 +1,6 @@ use openmina_core::block::AppliedBlock; +use openmina_core::bug_condition; +use p2p::channels::best_tip::P2pChannelsBestTipAction; use super::sync::{SyncError, TransitionFrontierSyncState}; use super::{ @@ -101,6 +103,18 @@ impl TransitionFrontierState { } state.sync = TransitionFrontierSyncState::Synced { time: meta.time() }; } + TransitionFrontierAction::RpcRespondBestTip { peer_id } => { + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let Some(best_tip) = state.transition_frontier.best_tip() else { + bug_condition!("Best tip not found"); + return; + }; + + dispatcher.push(P2pChannelsBestTipAction::ResponseSend { + peer_id: *peer_id, + best_tip: best_tip.clone(), + }); + } } } } diff --git a/p2p/src/channels/best_tip/p2p_channels_best_tip_reducer.rs b/p2p/src/channels/best_tip/p2p_channels_best_tip_reducer.rs index 1bb2af3839..2a64f7797f 100644 --- a/p2p/src/channels/best_tip/p2p_channels_best_tip_reducer.rs +++ b/p2p/src/channels/best_tip/p2p_channels_best_tip_reducer.rs @@ -97,7 +97,7 @@ impl P2pChannelsBestTipState { dispatcher.push(P2pChannelsBestTipAction::RequestSend { peer_id }); Ok(()) } - P2pChannelsBestTipAction::RequestReceived { .. } => { + P2pChannelsBestTipAction::RequestReceived { peer_id, .. } => { let Self::Ready { remote, .. } = best_tip_state else { bug_condition!( "Invalid state for `P2pChannelsBestTipAction::RequestReceived`, state: {:?}", @@ -107,6 +107,16 @@ impl P2pChannelsBestTipState { }; *remote = BestTipPropagationState::Requested { time: meta.time() }; + + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let p2p_state: &P2pState = state.substate()?; + + if let Some(callback) = &p2p_state + .callbacks + .on_p2p_channels_best_tip_request_received + { + dispatcher.push_callback(callback.clone(), *peer_id); + } Ok(()) } P2pChannelsBestTipAction::ResponseSend { best_tip, .. } => { diff --git a/p2p/src/channels/rpc/p2p_channels_rpc_reducer.rs b/p2p/src/channels/rpc/p2p_channels_rpc_reducer.rs index 74b9a62e41..c0dbd8a156 100644 --- a/p2p/src/channels/rpc/p2p_channels_rpc_reducer.rs +++ b/p2p/src/channels/rpc/p2p_channels_rpc_reducer.rs @@ -58,6 +58,13 @@ impl P2pChannelsRpcState { last_responded: redux::Timestamp::ZERO, }, }; + + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let p2p_state: &P2pState = state.substate()?; + + if let Some(callback) = &p2p_state.callbacks.on_p2p_channels_rpc_ready { + dispatcher.push_callback(callback.clone(), peer_id); + } Ok(()) } P2pChannelsRpcAction::RequestSend { @@ -108,8 +115,21 @@ impl P2pChannelsRpcState { }); Ok(()) } - P2pChannelsRpcAction::Timeout { .. } => Ok(()), - P2pChannelsRpcAction::ResponseReceived { response, .. } => { + P2pChannelsRpcAction::Timeout { id, .. } => { + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let p2p_state: &P2pState = state.substate()?; + + if let Some(callback) = &p2p_state.callbacks.on_p2p_channels_rpc_timeout { + dispatcher.push_callback(callback.clone(), (peer_id, *id)); + } + + Ok(()) + } + P2pChannelsRpcAction::ResponseReceived { + response, + id: rpc_id, + .. + } => { let Self::Ready { local, .. } = rpc_state else { bug_condition!( "Invalid state for `P2pChannelsRpcAction::ResponseReceived`, state: {:?}", @@ -130,7 +150,9 @@ impl P2pChannelsRpcState { request: std::mem::take(request), }; - let dispatcher = state_context.into_dispatcher(); + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let p2p_state: &P2pState = state.substate()?; + if let Some(P2pRpcResponse::BestTipWithProof(resp)) = response.as_deref() { let Ok(best_tip) = BlockWithHash::try_new(resp.best_tip.clone()) else { error!(meta.time(); "P2pChannelsRpcAction::ResponseReceived: Invalid bigint in block"); @@ -139,6 +161,11 @@ impl P2pChannelsRpcState { dispatcher.push(P2pPeerAction::BestTipUpdate { peer_id, best_tip }); } + + if let Some(callback) = &p2p_state.callbacks.on_p2p_channels_rpc_response_received { + dispatcher + .push_callback(callback.clone(), (peer_id, *rpc_id, response.clone())); + } Ok(()) } P2pChannelsRpcAction::RequestReceived { id, request, .. } => { @@ -157,6 +184,13 @@ impl P2pChannelsRpcState { request: (**request).clone(), is_pending: false, }); + + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let p2p_state: &P2pState = state.substate()?; + + if let Some(callback) = &p2p_state.callbacks.on_p2p_channels_rpc_request_received { + dispatcher.push_callback(callback.clone(), (peer_id, *id, request.clone())); + } Ok(()) } P2pChannelsRpcAction::ResponsePending { id, .. } => { diff --git a/p2p/src/channels/snark/p2p_channels_snark_reducer.rs b/p2p/src/channels/snark/p2p_channels_snark_reducer.rs index e6caf85ac0..c78f8933c2 100644 --- a/p2p/src/channels/snark/p2p_channels_snark_reducer.rs +++ b/p2p/src/channels/snark/p2p_channels_snark_reducer.rs @@ -94,7 +94,7 @@ impl P2pChannelsSnarkState { }; Ok(()) } - P2pChannelsSnarkAction::Received { .. } => { + P2pChannelsSnarkAction::Received { peer_id, snark } => { let state = state.inspect_err(|error| bug_condition!("{}", error))?; let Self::Ready { local, .. } = state else { bug_condition!( @@ -124,6 +124,14 @@ impl P2pChannelsSnarkState { count: *current_count, }; } + + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let p2p_state: &P2pState = state.substate()?; + + if let Some(callback) = &p2p_state.callbacks.on_p2p_channels_snark_received { + dispatcher.push_callback(callback.clone(), (*peer_id, snark.clone())); + } + Ok(()) } P2pChannelsSnarkAction::RequestReceived { limit, .. } => { @@ -191,7 +199,16 @@ impl P2pChannelsSnarkState { } #[cfg(not(feature = "p2p-libp2p"))] P2pChannelsSnarkAction::Libp2pBroadcast { .. } => Ok(()), - P2pChannelsSnarkAction::Libp2pReceived { .. } => Ok(()), + P2pChannelsSnarkAction::Libp2pReceived { peer_id, snark, .. } => { + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let p2p_state: &P2pState = state.substate()?; + + if let Some(callback) = &p2p_state.callbacks.on_p2p_channels_snark_libp2p_received { + dispatcher.push_callback(callback.clone(), (*peer_id, snark.clone())); + } + + Ok(()) + } } } } diff --git a/p2p/src/channels/snark_job_commitment/p2p_channels_snark_job_commitment_reducer.rs b/p2p/src/channels/snark_job_commitment/p2p_channels_snark_job_commitment_reducer.rs index 6dfe23ea21..ab1617a134 100644 --- a/p2p/src/channels/snark_job_commitment/p2p_channels_snark_job_commitment_reducer.rs +++ b/p2p/src/channels/snark_job_commitment/p2p_channels_snark_job_commitment_reducer.rs @@ -110,7 +110,7 @@ impl P2pChannelsSnarkJobCommitmentState { }; Ok(()) } - P2pChannelsSnarkJobCommitmentAction::Received { .. } => { + P2pChannelsSnarkJobCommitmentAction::Received { commitment, .. } => { let Self::Ready { local, .. } = snark_job_state else { bug_condition!( "Invalid state for `P2pChannelsSnarkJobCommitmentAction::Received`, state: {:?}", @@ -140,11 +140,19 @@ impl P2pChannelsSnarkJobCommitmentState { }; } - let dispatcher = state_context.into_dispatcher(); + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let p2p_state: &P2pState = state.substate()?; dispatcher.push(P2pChannelsSnarkJobCommitmentAction::RequestSend { peer_id, limit: LIMIT, }); + + if let Some(callback) = &p2p_state + .callbacks + .on_p2p_channels_snark_job_commitment_received + { + dispatcher.push_callback(callback.clone(), (peer_id, commitment.clone())); + } Ok(()) } P2pChannelsSnarkJobCommitmentAction::RequestReceived { limit, .. } => { diff --git a/p2p/src/channels/streaming_rpc/p2p_channels_streaming_rpc_reducer.rs b/p2p/src/channels/streaming_rpc/p2p_channels_streaming_rpc_reducer.rs index bb069daa49..7b4d425ceb 100644 --- a/p2p/src/channels/streaming_rpc/p2p_channels_streaming_rpc_reducer.rs +++ b/p2p/src/channels/streaming_rpc/p2p_channels_streaming_rpc_reducer.rs @@ -50,6 +50,13 @@ impl P2pChannelsStreamingRpcState { remote: P2pStreamingRpcRemoteState::WaitingForRequest { time: meta.time() }, remote_last_responded: redux::Timestamp::ZERO, }; + + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let p2p_state: &P2pState = state.substate()?; + + if let Some(callback) = &p2p_state.callbacks.on_p2p_channels_streaming_rpc_ready { + dispatcher.push_callback(callback.clone(), ()); + } Ok(()) } P2pChannelsStreamingRpcAction::RequestSend { @@ -86,7 +93,16 @@ impl P2pChannelsStreamingRpcState { }); Ok(()) } - P2pChannelsStreamingRpcAction::Timeout { .. } => Ok(()), + P2pChannelsStreamingRpcAction::Timeout { id, .. } => { + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let p2p_state: &P2pState = state.substate()?; + + if let Some(callback) = &p2p_state.callbacks.on_p2p_channels_streaming_rpc_timeout { + dispatcher.push_callback(callback.clone(), (peer_id, *id)); + } + + Ok(()) + } P2pChannelsStreamingRpcAction::ResponseNextPartGet { id, .. } => { let Self::Ready { local: P2pStreamingRpcLocalState::Requested { progress, .. }, @@ -145,7 +161,11 @@ impl P2pChannelsStreamingRpcState { .push(P2pChannelsStreamingRpcAction::ResponseNextPartGet { peer_id, id: *id }); Ok(()) } - P2pChannelsStreamingRpcAction::ResponseReceived { .. } => { + P2pChannelsStreamingRpcAction::ResponseReceived { + id: rpc_id, + response, + .. + } => { let Self::Ready { local, .. } = streaming_rpc_state else { bug_condition!("{:?} with state {:?}", action, streaming_rpc_state); return Ok(()); @@ -159,6 +179,17 @@ impl P2pChannelsStreamingRpcState { id: *id, request: std::mem::take(request), }; + + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let p2p_state: &P2pState = state.substate()?; + + if let Some(callback) = &p2p_state + .callbacks + .on_p2p_channels_streaming_rpc_response_received + { + dispatcher.push_callback(callback.clone(), (peer_id, *rpc_id, response.clone())) + } + Ok(()) } P2pChannelsStreamingRpcAction::RequestReceived { id, request, .. } => { diff --git a/p2p/src/channels/transaction/p2p_channels_transaction_actions.rs b/p2p/src/channels/transaction/p2p_channels_transaction_actions.rs index 400c6b1091..e4f9851b7b 100644 --- a/p2p/src/channels/transaction/p2p_channels_transaction_actions.rs +++ b/p2p/src/channels/transaction/p2p_channels_transaction_actions.rs @@ -29,6 +29,7 @@ pub enum P2pChannelsTransactionAction { peer_id: PeerId, promised_count: u8, }, + // TODO(binier): propagate tx info received to pool Received { peer_id: PeerId, transaction: Box, diff --git a/p2p/src/channels/transaction/p2p_channels_transaction_reducer.rs b/p2p/src/channels/transaction/p2p_channels_transaction_reducer.rs index 0acc3de62d..5a85bcc568 100644 --- a/p2p/src/channels/transaction/p2p_channels_transaction_reducer.rs +++ b/p2p/src/channels/transaction/p2p_channels_transaction_reducer.rs @@ -180,7 +180,19 @@ impl P2pChannelsTransactionState { }); Ok(()) } - P2pChannelsTransactionAction::Libp2pReceived { .. } => Ok(()), + P2pChannelsTransactionAction::Libp2pReceived { transaction, .. } => { + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let p2p_state: &P2pState = state.substate()?; + + if let Some(callback) = &p2p_state + .callbacks + .on_p2p_channels_transaction_libp2p_received + { + dispatcher.push_callback(callback.clone(), transaction.clone()); + } + + Ok(()) + } #[cfg(not(feature = "p2p-libp2p"))] P2pChannelsTransactionAction::Libp2pBroadcast { .. } => Ok(()), #[cfg(feature = "p2p-libp2p")] diff --git a/p2p/src/connection/incoming/p2p_connection_incoming_reducer.rs b/p2p/src/connection/incoming/p2p_connection_incoming_reducer.rs index 1bc8489bd0..3ba4532fa4 100644 --- a/p2p/src/connection/incoming/p2p_connection_incoming_reducer.rs +++ b/p2p/src/connection/incoming/p2p_connection_incoming_reducer.rs @@ -8,7 +8,7 @@ use crate::{ connection::{ incoming::P2pConnectionIncomingError, incoming_effectful::P2pConnectionIncomingEffectfulAction, - outgoing::P2pConnectionOutgoingInitOpts, P2pConnectionState, + outgoing::P2pConnectionOutgoingInitOpts, P2pConnectionResponse, P2pConnectionState, }, disconnection::{P2pDisconnectionAction, P2pDisconnectionReason}, webrtc::{HttpSignalingInfo, SignalingMethod}, @@ -164,12 +164,29 @@ impl P2pConnectionIncomingState { state ); } - state_context.into_dispatcher().push( - P2pConnectionIncomingEffectfulAction::AnswerSend { - peer_id: *peer_id, - answer: answer.clone(), - }, - ); + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let p2p_state: &P2pState = state.substate()?; + + dispatcher.push(P2pConnectionIncomingEffectfulAction::AnswerSend { + peer_id: *peer_id, + answer: answer.clone(), + }); + + if let Some(rpc_id) = p2p_state.peer_connection_rpc_id(&peer_id) { + if let Some(callback) = + &p2p_state.callbacks.on_p2p_connection_incoming_answer_ready + { + dispatcher.push_callback( + callback.clone(), + ( + rpc_id, + *peer_id, + P2pConnectionResponse::Accepted(answer.clone()), + ), + ); + } + } + Ok(()) } P2pConnectionIncomingAction::AnswerSendSuccess { .. } => { @@ -307,6 +324,17 @@ impl P2pConnectionIncomingState { error: error.clone(), rpc_id, }; + + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let p2p_state: &P2pState = state.substate()?; + + if let Some(rpc_id) = p2p_state.peer_connection_rpc_id(&peer_id) { + if let Some(callback) = &p2p_state.callbacks.on_p2p_connection_incoming_error { + dispatcher + .push_callback(callback.clone(), (rpc_id, format!("{:?}", error))); + } + } + Ok(()) } P2pConnectionIncomingAction::Success { .. } => { @@ -337,11 +365,20 @@ impl P2pConnectionIncomingState { return Ok(()); } - let dispatcher = state_context.into_dispatcher(); + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let p2p_state: &P2pState = state.substate()?; + dispatcher.push(P2pPeerAction::Ready { peer_id, incoming: true, }); + + if let Some(rpc_id) = p2p_state.peer_connection_rpc_id(&peer_id) { + if let Some(callback) = &p2p_state.callbacks.on_p2p_connection_incoming_success + { + dispatcher.push_callback(callback.clone(), rpc_id); + } + } Ok(()) } P2pConnectionIncomingAction::FinalizePendingLibp2p { addr, .. } => { diff --git a/p2p/src/connection/outgoing/p2p_connection_outgoing_reducer.rs b/p2p/src/connection/outgoing/p2p_connection_outgoing_reducer.rs index 157a3d0a3f..6da92bf33b 100644 --- a/p2p/src/connection/outgoing/p2p_connection_outgoing_reducer.rs +++ b/p2p/src/connection/outgoing/p2p_connection_outgoing_reducer.rs @@ -417,11 +417,11 @@ impl P2pConnectionOutgoingState { rpc_id, }; + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let p2p_state: &P2pState = state.substate()?; + #[cfg(feature = "p2p-libp2p")] { - let (dispatcher, state) = state_context.into_dispatcher_and_state(); - let p2p_state: &P2pState = state.substate()?; - if p2p_state .network .scheduler @@ -435,6 +435,12 @@ impl P2pConnectionOutgoingState { }); } } + + if let Some(rpc_id) = p2p_state.peer_connection_rpc_id(peer_id) { + if let Some(callback) = &p2p_state.callbacks.on_p2p_connection_outgoing_error { + dispatcher.push_callback(callback.clone(), (rpc_id, error.clone())); + } + } Ok(()) } P2pConnectionOutgoingAction::Success { peer_id } => { @@ -463,11 +469,19 @@ impl P2pConnectionOutgoingState { return Ok(()); } - let dispatcher = state_context.into_dispatcher(); + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let p2p_state: &P2pState = state.substate()?; dispatcher.push(P2pPeerAction::Ready { peer_id: *peer_id, incoming: false, }); + + if let Some(rpc_id) = p2p_state.peer_connection_rpc_id(peer_id) { + if let Some(callback) = &p2p_state.callbacks.on_p2p_connection_outgoing_success + { + dispatcher.push_callback(callback.clone(), rpc_id); + } + } Ok(()) } } diff --git a/p2p/src/disconnection/p2p_disconnection_reducer.rs b/p2p/src/disconnection/p2p_disconnection_reducer.rs index 630594a978..673327019b 100644 --- a/p2p/src/disconnection/p2p_disconnection_reducer.rs +++ b/p2p/src/disconnection/p2p_disconnection_reducer.rs @@ -59,8 +59,13 @@ impl P2pDisconnectedState { }; peer.status = P2pPeerStatus::Disconnected { time: meta.time() }; - let dispatcher = state_context.into_dispatcher(); + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let p2p_state: &P2pState = state.substate()?; dispatcher.push(P2pPeerAction::Remove { peer_id: *peer_id }); + + if let Some(callback) = &p2p_state.callbacks.on_p2p_disconnection_finish { + dispatcher.push_callback(callback.clone(), *peer_id); + } Ok(()) } #[cfg(feature = "p2p-libp2p")] @@ -83,8 +88,14 @@ impl P2pDisconnectedState { }; peer.status = P2pPeerStatus::Disconnected { time: meta.time() }; - let dispatcher = state_context.into_dispatcher(); + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let p2p_state: &P2pState = state.substate()?; dispatcher.push(P2pPeerAction::Remove { peer_id: *peer_id }); + + if let Some(callback) = &p2p_state.callbacks.on_p2p_disconnection_finish { + dispatcher.push_callback(callback.clone(), *peer_id); + } + Ok(()) } } diff --git a/p2p/src/p2p_state.rs b/p2p/src/p2p_state.rs index 6453dce40a..af50391f4e 100644 --- a/p2p/src/p2p_state.rs +++ b/p2p/src/p2p_state.rs @@ -1,26 +1,40 @@ -use openmina_core::{block::ArcBlockWithHash, ChainId}; -use openmina_core::{impl_substate_access, SubstateAccess}; -use redux::Timestamp; +use openmina_core::{ + block::{ArcBlockWithHash, BlockWithHash}, + impl_substate_access, + requests::{RequestId, RpcId, RpcIdType}, + snark::{Snark, SnarkInfo, SnarkJobCommitment}, + ChainId, SubstateAccess, +}; +use redux::{Callback, Timestamp}; use serde::{Deserialize, Serialize}; -use std::collections::{BTreeMap, BTreeSet}; - -use openmina_core::requests::RpcId; - -use crate::bootstrap::P2pNetworkKadBootstrapState; -use crate::channels::rpc::P2pRpcId; -use crate::channels::streaming_rpc::P2pStreamingRpcId; -use crate::channels::{ChannelId, P2pChannelsState}; -use crate::connection::incoming::P2pConnectionIncomingState; -use crate::connection::outgoing::{P2pConnectionOutgoingInitOpts, P2pConnectionOutgoingState}; -use crate::network::identify::{P2pNetworkIdentify, P2pNetworkIdentifyState}; -use crate::network::P2pNetworkState; +use std::{ + collections::{BTreeMap, BTreeSet}, + sync::Arc, +}; + use crate::{ - is_time_passed, Limit, P2pLimits, P2pNetworkKadState, P2pNetworkPubsubState, + bootstrap::P2pNetworkKadBootstrapState, + channels::{ + rpc::{P2pRpcId, P2pRpcRequest, P2pRpcResponse}, + streaming_rpc::{P2pStreamingRpcId, P2pStreamingRpcResponseFull}, + ChannelId, P2pChannelsState, + }, + connection::{ + incoming::P2pConnectionIncomingState, + outgoing::{ + P2pConnectionOutgoingError, P2pConnectionOutgoingInitOpts, P2pConnectionOutgoingState, + }, + P2pConnectionResponse, P2pConnectionState, + }, + is_time_passed, + network::{ + identify::{P2pNetworkIdentify, P2pNetworkIdentifyState}, + P2pNetworkState, + }, + Limit, P2pConfig, P2pLimits, P2pNetworkKadState, P2pNetworkPubsubState, P2pNetworkSchedulerState, P2pTimeouts, PeerId, }; - -use super::connection::P2pConnectionState; -use super::P2pConfig; +use mina_p2p_messages::v2::{MinaBaseUserCommandStableV2, MinaBlockBlockStableV2}; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct P2pState { @@ -28,10 +42,11 @@ pub struct P2pState { pub config: P2pConfig, pub network: P2pNetworkState, pub peers: BTreeMap, + pub callbacks: P2pCallbacks, } impl P2pState { - pub fn new(config: P2pConfig, chain_id: &ChainId) -> Self { + pub fn new(config: P2pConfig, callbacks: P2pCallbacks, chain_id: &ChainId) -> Self { let addrs = if cfg!(feature = "p2p-libp2p") { config .libp2p_port @@ -104,6 +119,7 @@ impl P2pState { config, network, peers, + callbacks, } } @@ -471,6 +487,46 @@ impl SubstateAccess for P2pState { } } +type OptionalCallback = Option>; + +#[derive(Serialize, Deserialize, Debug, Clone, Default)] +pub struct P2pCallbacks { + pub on_p2p_channels_transaction_libp2p_received: + OptionalCallback>, + pub on_p2p_channels_snark_job_commitment_received: + OptionalCallback<(PeerId, Box)>, + + pub on_p2p_channels_snark_received: OptionalCallback<(PeerId, Box)>, + pub on_p2p_channels_snark_libp2p_received: OptionalCallback<(PeerId, Box)>, + + pub on_p2p_channels_best_tip_request_received: OptionalCallback, + + pub on_p2p_disconnection_finish: OptionalCallback, + + pub on_p2p_connection_outgoing_error: + OptionalCallback<(RequestId, P2pConnectionOutgoingError)>, + pub on_p2p_connection_outgoing_success: OptionalCallback>, + + pub on_p2p_connection_incoming_error: OptionalCallback<(RequestId, String)>, + pub on_p2p_connection_incoming_success: OptionalCallback>, + pub on_p2p_connection_incoming_answer_ready: + OptionalCallback<(RequestId, PeerId, P2pConnectionResponse)>, + + pub on_p2p_peer_best_tip_update: OptionalCallback>>, + + pub on_p2p_channels_rpc_ready: OptionalCallback, + pub on_p2p_channels_rpc_timeout: OptionalCallback<(PeerId, P2pRpcId)>, + pub on_p2p_channels_rpc_response_received: + OptionalCallback<(PeerId, P2pRpcId, Option>)>, + pub on_p2p_channels_rpc_request_received: + OptionalCallback<(PeerId, P2pRpcId, Box)>, + + pub on_p2p_channels_streaming_rpc_ready: OptionalCallback<()>, + pub on_p2p_channels_streaming_rpc_timeout: OptionalCallback<(PeerId, P2pRpcId)>, + pub on_p2p_channels_streaming_rpc_response_received: + OptionalCallback<(PeerId, P2pRpcId, Option)>, +} + impl_substate_access!(P2pState, P2pNetworkState, network); impl_substate_access!(P2pState, P2pNetworkSchedulerState, network.scheduler); impl_substate_access!(P2pState, P2pLimits, config.limits); diff --git a/p2p/src/peer/p2p_peer_reducer.rs b/p2p/src/peer/p2p_peer_reducer.rs index 1f4705dc96..23f5a532dc 100644 --- a/p2p/src/peer/p2p_peer_reducer.rs +++ b/p2p/src/peer/p2p_peer_reducer.rs @@ -63,6 +63,12 @@ impl P2pPeerState { }; peer.best_tip = Some(best_tip.clone()); + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let p2p_state: &P2pState = state.substate()?; + + if let Some(callback) = &p2p_state.callbacks.on_p2p_peer_best_tip_update { + dispatcher.push_callback(callback.clone(), best_tip.clone()); + } Ok(()) } P2pPeerAction::Remove { peer_id } => { diff --git a/p2p/testing/src/cluster.rs b/p2p/testing/src/cluster.rs index 001d3d894d..76544d6a56 100644 --- a/p2p/testing/src/cluster.rs +++ b/p2p/testing/src/cluster.rs @@ -15,7 +15,7 @@ use p2p::{ P2pConnectionOutgoingInitOpts, P2pConnectionOutgoingInitOptsParseError, }, identity::SecretKey, - p2p_effects, P2pConfig, P2pMeshsubConfig, P2pState, PeerId, + p2p_effects, P2pCallbacks, P2pConfig, P2pMeshsubConfig, P2pState, PeerId, }; use redux::SystemTime; use tokio::sync::mpsc; @@ -414,7 +414,11 @@ impl Cluster { }), service, SystemTime::now(), - State(P2pState::new(config, &self.chain_id)), + State(P2pState::new( + config, + P2pCallbacks::default(), + &self.chain_id, + )), ); let node_id = RustNodeId(self.rust_nodes.len()); From d531b0ddd9da9de66ba1f57b90d668bf3cde8856 Mon Sep 17 00:00:00 2001 From: Mimir Date: Tue, 8 Oct 2024 14:01:41 +0200 Subject: [PATCH 3/4] Removed commented code --- node/src/consensus/consensus_reducer.rs | 9 +- node/src/p2p/callbacks/mod.rs | 2 +- .../p2p/callbacks/p2p_callbacks_actions.rs | 12 +- .../p2p/callbacks/p2p_callbacks_reducer.rs | 5 +- node/src/p2p/p2p_effects.rs | 660 +----------------- node/src/rpc/rpc_reducer.rs | 2 +- node/src/state.rs | 17 +- .../p2p_connection_incoming_reducer.rs | 2 +- 8 files changed, 46 insertions(+), 663 deletions(-) diff --git a/node/src/consensus/consensus_reducer.rs b/node/src/consensus/consensus_reducer.rs index a1cd8fb5a6..22da88cd60 100644 --- a/node/src/consensus/consensus_reducer.rs +++ b/node/src/consensus/consensus_reducer.rs @@ -5,7 +5,14 @@ use openmina_core::{ use snark::block_verify::{SnarkBlockVerifyAction, SnarkBlockVerifyError}; use crate::{ - transition_frontier::sync::{ledger::{snarked::TransitionFrontierSyncLedgerSnarkedAction, staged::TransitionFrontierSyncLedgerStagedAction}, TransitionFrontierSyncAction}, Action, State, WatchedAccountsAction, + transition_frontier::sync::{ + ledger::{ + snarked::TransitionFrontierSyncLedgerSnarkedAction, + staged::TransitionFrontierSyncLedgerStagedAction, + }, + TransitionFrontierSyncAction, + }, + Action, State, WatchedAccountsAction, }; use super::{ diff --git a/node/src/p2p/callbacks/mod.rs b/node/src/p2p/callbacks/mod.rs index c499b75def..d5e53fe032 100644 --- a/node/src/p2p/callbacks/mod.rs +++ b/node/src/p2p/callbacks/mod.rs @@ -1,3 +1,3 @@ mod p2p_callbacks_actions; pub use p2p_callbacks_actions::P2pCallbacksAction; -mod p2p_callbacks_reducer; \ No newline at end of file +mod p2p_callbacks_reducer; diff --git a/node/src/p2p/callbacks/p2p_callbacks_actions.rs b/node/src/p2p/callbacks/p2p_callbacks_actions.rs index 976e2746fd..30202dea9e 100644 --- a/node/src/p2p/callbacks/p2p_callbacks_actions.rs +++ b/node/src/p2p/callbacks/p2p_callbacks_actions.rs @@ -42,7 +42,15 @@ pub enum P2pCallbacksAction { } impl redux::EnablingCondition for P2pCallbacksAction { - fn is_enabled(&self, state: &crate::State, time: redux::Timestamp) -> bool { - true + fn is_enabled(&self, _state: &crate::State, _time: redux::Timestamp) -> bool { + match self { + P2pCallbacksAction::P2pChannelsRpcReady { .. } => true, + P2pCallbacksAction::P2pChannelsRpcTimeout { .. } => true, + P2pCallbacksAction::P2pChannelsRpcResponseReceived { .. } => true, + P2pCallbacksAction::P2pChannelsRpcRequestReceived { .. } => true, + P2pCallbacksAction::P2pChannelsStreamingRpcReady => true, + P2pCallbacksAction::P2pChannelsStreamingRpcTimeout { .. } => true, + P2pCallbacksAction::P2pChannelsStreamingRpcResponseReceived { .. } => true, + } } } diff --git a/node/src/p2p/callbacks/p2p_callbacks_reducer.rs b/node/src/p2p/callbacks/p2p_callbacks_reducer.rs index ea77e07935..d41b2dc62e 100644 --- a/node/src/p2p/callbacks/p2p_callbacks_reducer.rs +++ b/node/src/p2p/callbacks/p2p_callbacks_reducer.rs @@ -2,7 +2,10 @@ use ark_ff::fields::arithmetic::InvalidBigInt; use mina_p2p_messages::v2::{MinaLedgerSyncLedgerAnswerStableV2, StateHash}; use openmina_core::{block::BlockWithHash, bug_condition}; use p2p::{ - channels::{rpc::{BestTipWithProof, P2pChannelsRpcAction, P2pRpcRequest, P2pRpcResponse}, streaming_rpc::P2pStreamingRpcResponseFull}, + channels::{ + rpc::{BestTipWithProof, P2pChannelsRpcAction, P2pRpcRequest, P2pRpcResponse}, + streaming_rpc::P2pStreamingRpcResponseFull, + }, disconnection::{P2pDisconnectionAction, P2pDisconnectionReason}, }; use redux::ActionWithMeta; diff --git a/node/src/p2p/p2p_effects.rs b/node/src/p2p/p2p_effects.rs index ef4f3c7083..d6fb9e0146 100644 --- a/node/src/p2p/p2p_effects.rs +++ b/node/src/p2p/p2p_effects.rs @@ -1,43 +1,9 @@ -use ark_ff::fields::arithmetic::InvalidBigInt; -use mina_p2p_messages::v2::{MinaLedgerSyncLedgerAnswerStableV2, StateHash}; -use openmina_core::block::BlockWithHash; -use openmina_core::bug_condition; -use p2p::channels::streaming_rpc::{ - P2pChannelsStreamingRpcAction, P2pStreamingRpcRequest, P2pStreamingRpcResponseFull, +use super::P2pActionWithMeta; +use crate::{P2pAction, Service, Store}; +use p2p::{ + channels::P2pChannelsEffectfulAction, connection::P2pConnectionEffectfulAction, + P2pInitializeAction, }; -use p2p::channels::transaction::P2pChannelsTransactionAction; -use p2p::channels::P2pChannelsEffectfulAction; -use p2p::connection::P2pConnectionEffectfulAction; -use p2p::P2pInitializeAction; - -use crate::consensus::ConsensusAction; -use crate::rpc::RpcAction; -use crate::snark_pool::candidate::SnarkPoolCandidateAction; -use crate::snark_pool::SnarkPoolAction; -use crate::transition_frontier::sync::ledger::snarked::{ - PeerLedgerQueryError, PeerLedgerQueryResponse, TransitionFrontierSyncLedgerSnarkedAction, -}; -use crate::transition_frontier::sync::ledger::staged::{ - PeerStagedLedgerPartsFetchError, TransitionFrontierSyncLedgerStagedAction, -}; -use crate::transition_frontier::sync::{PeerBlockFetchError, TransitionFrontierSyncAction}; -use crate::watched_accounts::{ - WatchedAccountLedgerInitialState, WatchedAccountsAction, - WatchedAccountsLedgerInitialStateGetError, -}; -use crate::{p2p_ready, Service, Store, TransactionPoolAction}; - -use super::channels::best_tip::P2pChannelsBestTipAction; -use super::channels::rpc::{BestTipWithProof, P2pChannelsRpcAction, P2pRpcRequest, P2pRpcResponse}; -use super::channels::snark::P2pChannelsSnarkAction; -use super::channels::snark_job_commitment::P2pChannelsSnarkJobCommitmentAction; -use super::channels::P2pChannelsAction; -use super::connection::incoming::P2pConnectionIncomingAction; -use super::connection::outgoing::P2pConnectionOutgoingAction; -use super::connection::{P2pConnectionAction, P2pConnectionResponse}; -use super::disconnection::{P2pDisconnectionAction, P2pDisconnectionReason}; -use super::peer::P2pPeerAction; -use super::{P2pAction, P2pActionWithMeta}; pub fn node_p2p_effects(store: &mut Store, action: P2pActionWithMeta) { let (action, meta) = action.split(); @@ -49,596 +15,7 @@ pub fn node_p2p_effects(store: &mut Store, action: P2pActionWithM store.service().start_mio(); } } - P2pAction::Connection(action) => { - // match action { - // P2pConnectionAction::Outgoing(action) => match action { - // P2pConnectionOutgoingAction::Error { - // ref peer_id, - // ref error, - // } => { - // let p2p = p2p_ready!(store.state().p2p, meta.time()); - // if let Some(rpc_id) = p2p.peer_connection_rpc_id(peer_id) { - // store.dispatch(RpcAction::P2pConnectionOutgoingError { - // rpc_id, - // error: error.clone(), - // }); - // } - // } - // P2pConnectionOutgoingAction::Success { ref peer_id } => { - // let p2p = p2p_ready!(store.state().p2p, meta.time()); - // if let Some(rpc_id) = p2p.peer_connection_rpc_id(peer_id) { - // store.dispatch(RpcAction::P2pConnectionOutgoingSuccess { rpc_id }); - // } - // } - // _ => {} - // }, - // P2pConnectionAction::Incoming(action) => match &action { - // P2pConnectionIncomingAction::AnswerReady { peer_id, answer } => { - // let p2p = p2p_ready!(store.state().p2p, meta.time()); - // if let Some(rpc_id) = p2p.peer_connection_rpc_id(peer_id) { - // store.dispatch(RpcAction::P2pConnectionIncomingRespond { - // rpc_id, - // response: P2pConnectionResponse::Accepted(answer.clone()), - // }); - // store.dispatch(P2pConnectionIncomingAction::AnswerSendSuccess { - // peer_id: *peer_id, - // }); - // } - // } - // P2pConnectionIncomingAction::Error { peer_id, error } => { - // let p2p = p2p_ready!(store.state().p2p, meta.time()); - // if let Some(rpc_id) = p2p.peer_connection_rpc_id(peer_id) { - // store.dispatch(RpcAction::P2pConnectionIncomingError { - // rpc_id, - // error: format!("{:?}", error), - // }); - // } - // } - // P2pConnectionIncomingAction::Success { peer_id } => { - // let p2p = p2p_ready!(store.state().p2p, meta.time()); - // if let Some(rpc_id) = p2p.peer_connection_rpc_id(peer_id) { - // store.dispatch(RpcAction::P2pConnectionIncomingSuccess { rpc_id }); - // } - // } - // _ => {} - // }, - // } - } - P2pAction::Disconnection(action) => { - // match action { - // P2pDisconnectionAction::Init { .. } => {} - // P2pDisconnectionAction::Finish { peer_id } => { - // if let Some(s) = store.state().transition_frontier.sync.ledger() { - // let snarked_ledger_num_accounts_rpc_id = s - // .snarked() - // .and_then(|s| s.peer_num_accounts_rpc_id(&peer_id)); - // let snarked_ledger_address_rpc_ids = s - // .snarked() - // .map(|s| s.peer_address_query_pending_rpc_ids(&peer_id).collect()) - // .unwrap_or(vec![]); - // let staged_ledger_parts_fetch_rpc_id = - // s.staged().and_then(|s| s.parts_fetch_rpc_id(&peer_id)); - - // for rpc_id in snarked_ledger_address_rpc_ids { - // store.dispatch( - // TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressError { - // peer_id, - // rpc_id, - // error: PeerLedgerQueryError::Disconnected, - // }, - // ); - // } - - // if let Some(rpc_id) = snarked_ledger_num_accounts_rpc_id { - // store.dispatch( - // TransitionFrontierSyncLedgerSnarkedAction::PeerQueryNumAccountsError { - // peer_id, - // rpc_id, - // error: PeerLedgerQueryError::Disconnected, - // }, - // ); - // } - - // if let Some(rpc_id) = staged_ledger_parts_fetch_rpc_id { - // store.dispatch( - // TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchError { - // peer_id, - // rpc_id, - // error: PeerStagedLedgerPartsFetchError::Disconnected, - // }, - // ); - // } - // } - - // let blocks_fetch_rpc_ids = store - // .state() - // .transition_frontier - // .sync - // .blocks_fetch_from_peer_pending_rpc_ids(&peer_id) - // .collect::>(); - - // for rpc_id in blocks_fetch_rpc_ids { - // store.dispatch(TransitionFrontierSyncAction::BlocksPeerQueryError { - // peer_id, - // rpc_id, - // error: PeerBlockFetchError::Disconnected, - // }); - // } - - // let actions = store - // .state() - // .watched_accounts - // .iter() - // .filter_map(|(pub_key, a)| match &a.initial_state { - // WatchedAccountLedgerInitialState::Pending { - // peer_id: account_peer_id, - // .. - // } => { - // if account_peer_id == &peer_id { - // Some(WatchedAccountsAction::LedgerInitialStateGetError { - // pub_key: pub_key.clone(), - // error: - // WatchedAccountsLedgerInitialStateGetError::PeerDisconnected, - // }) - // } else { - // None - // } - // } - // _ => None, - // }) - // .collect::>(); - - // for action in actions { - // store.dispatch(action); - // } - - // store.dispatch(SnarkPoolCandidateAction::PeerPrune { peer_id }); - // } - // } - } P2pAction::DisconnectionEffectful(action) => action.effects(&meta, store), - P2pAction::Channels(action) => match action { - P2pChannelsAction::MessageReceived(_) => { - // handled by reducer - } - P2pChannelsAction::BestTip(action) => { - // if let P2pChannelsBestTipAction::RequestReceived { peer_id } = action { - // if let Some(best_tip) = store.state().transition_frontier.best_tip() { - // store.dispatch(P2pChannelsBestTipAction::ResponseSend { - // peer_id, - // best_tip: best_tip.clone(), - // }); - // } - // } - } - P2pChannelsAction::Transaction(action) => { - match action { - P2pChannelsTransactionAction::Received { - peer_id: _, - transaction: _, - } => { - // TODO(binier): propagate tx info received to pool - } - P2pChannelsTransactionAction::Libp2pReceived { - peer_id: _, - transaction, - nonce: _, - } => { - // store.dispatch(TransactionPoolAction::StartVerify { - // // TODO: Take multiple transactions here - // commands: [*transaction].into_iter().collect(), - // from_rpc: None, - // }); - } - _ => {} - } - } - P2pChannelsAction::Snark(action) => match action { - P2pChannelsSnarkAction::Received { peer_id, snark } => { - // store.dispatch(SnarkPoolCandidateAction::InfoReceived { - // peer_id, - // info: *snark, - // }); - } - P2pChannelsSnarkAction::Libp2pReceived { peer_id, snark, .. } => { - // store.dispatch(SnarkPoolCandidateAction::WorkReceived { - // peer_id, - // work: *snark, - // }); - } - _ => {} - }, - P2pChannelsAction::SnarkJobCommitment(action) => { - if let P2pChannelsSnarkJobCommitmentAction::Received { - peer_id, - commitment, - } = action - { - // store.dispatch(SnarkPoolAction::CommitmentAdd { - // commitment: *commitment, - // sender: peer_id, - // }); - } - } - P2pChannelsAction::Rpc(action) => { - match action { - P2pChannelsRpcAction::Ready { peer_id } => { - // store.dispatch(P2pChannelsRpcAction::RequestSend { - // peer_id, - // id: 0, - // request: Box::new(P2pRpcRequest::BestTipWithProof), - // on_init: None, - // }); - - // store.dispatch(TransitionFrontierSyncLedgerSnarkedAction::PeersQuery); - // store - // .dispatch(TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit); - // store.dispatch(TransitionFrontierSyncAction::BlocksPeersQuery); - } - P2pChannelsRpcAction::Timeout { peer_id, id } => { - // let Some(peer) = store.state().p2p.get_ready_peer(&peer_id) else { - // bug_condition!("get_ready_peer({:?}) returned None", peer_id); - // return; - // }; - - // let Some(rpc_kind) = peer.channels.rpc.pending_local_rpc_kind() else { - // bug_condition!( - // "peer: {:?} pending_local_rpc_kind() returned None", - // peer_id - // ); - // return; - // }; - - // store.dispatch( - // TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressError { - // peer_id, - // rpc_id: id, - // error: PeerLedgerQueryError::Timeout, - // }, - // ); - // store.dispatch( - // TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchError { - // peer_id, - // rpc_id: id, - // error: PeerStagedLedgerPartsFetchError::Timeout, - // }, - // ); - // store.dispatch(TransitionFrontierSyncAction::BlocksPeerQueryError { - // peer_id, - // rpc_id: id, - // error: PeerBlockFetchError::Timeout, - // }); - // store.dispatch(P2pDisconnectionAction::Init { - // peer_id, - // reason: P2pDisconnectionReason::TransitionFrontierRpcTimeout(rpc_kind), - // }); - } - P2pChannelsRpcAction::ResponseReceived { - peer_id, - id, - response, - } => { - // match response.as_deref() { - // None => { - // store.dispatch( - // TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressError { - // peer_id, - // rpc_id: id, - // error: PeerLedgerQueryError::DataUnavailable, - // }, - // ); - // store.dispatch( - // TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchError { - // peer_id, - // rpc_id: id, - // error: PeerStagedLedgerPartsFetchError::DataUnavailable, - // }, - // ); - // store.dispatch( - // TransitionFrontierSyncAction::BlocksPeerQueryError { - // peer_id, - // rpc_id: id, - // error: PeerBlockFetchError::DataUnavailable, - // }, - // ); - // } - // Some(P2pRpcResponse::BestTipWithProof(resp)) => { - // let (body_hashes, root_block) = &resp.proof; - - // let (Ok(best_tip), Ok(root_block)) = ( - // BlockWithHash::try_new(resp.best_tip.clone()), - // BlockWithHash::try_new(root_block.clone()), - // ) else { - // openmina_core::error!(meta.time(); "P2pRpcResponse::BestTipWithProof: invalid blocks"); - // return; - // }; - - // // reconstruct hashes - // let Ok(hashes) = body_hashes - // .iter() - // .take(body_hashes.len().saturating_sub(1)) - // .scan(root_block.hash.clone(), |pred_hash, body_hash| { - // *pred_hash = match StateHash::try_from_hashes( - // pred_hash, body_hash, - // ) { - // Ok(hash) => hash, - // Err(_) => return Some(Err(InvalidBigInt)), - // }; - // Some(Ok(pred_hash.clone())) - // }) - // .collect::, _>>() - // else { - // openmina_core::error!(meta.time(); "P2pRpcResponse::BestTipWithProof: invalid hashes"); - // return; - // }; - - // if let Some(pred_hash) = hashes.last() { - // let expected_hash = - // &best_tip.block.header.protocol_state.previous_state_hash; - // if pred_hash != expected_hash { - // openmina_core::warn!(meta.time(); - // kind = "P2pRpcBestTipHashMismatch", - // response = serde_json::to_string(&resp).ok(), - // expected_hash = expected_hash.to_string(), - // calculated_hash = pred_hash.to_string()); - // return; - // } - // } - // store.dispatch(ConsensusAction::BlockChainProofUpdate { - // hash: best_tip.hash, - // chain_proof: (hashes, root_block), - // }); - // } - // Some(P2pRpcResponse::LedgerQuery(answer)) => match answer { - // MinaLedgerSyncLedgerAnswerStableV2::ChildHashesAre(left, right) => { - // store.dispatch( - // TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressSuccess { - // peer_id, - // rpc_id: id, - // response: PeerLedgerQueryResponse::ChildHashes( - // left.clone(), - // right.clone(), - // ), - // }, - // ); - // } - // MinaLedgerSyncLedgerAnswerStableV2::ContentsAre(accounts) => { - // store.dispatch( - // TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressSuccess { - // peer_id, - // rpc_id: id, - // response: PeerLedgerQueryResponse::ChildAccounts( - // accounts.iter().cloned().collect(), - // ), - // }, - // ); - // } - // MinaLedgerSyncLedgerAnswerStableV2::NumAccounts( - // count, - // contents_hash, - // ) => { - // store.dispatch( - // TransitionFrontierSyncLedgerSnarkedAction::PeerQueryNumAccountsSuccess { - // peer_id, - // rpc_id: id, - // response: PeerLedgerQueryResponse::NumAccounts( - // count.as_u64(), contents_hash.clone() - // ), - // }, - // ); - // } - // }, - // Some(P2pRpcResponse::StagedLedgerAuxAndPendingCoinbasesAtBlock( - // parts, - // )) => { - // store.dispatch( - // TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchSuccess { - // peer_id, - // rpc_id: id, - // parts: parts.clone(), - // }, - // ); - // } - // Some(P2pRpcResponse::Block(block)) => { - // let Ok(block) = BlockWithHash::try_new(block.clone()) else { - // openmina_core::error!(meta.time(); "P2pRpcResponse::Block: invalid block"); - // return; - // }; - // store.dispatch( - // TransitionFrontierSyncAction::BlocksPeerQuerySuccess { - // peer_id, - // rpc_id: id, - // response: block, - // }, - // ); - // } - // Some(P2pRpcResponse::Snark(snark)) => { - // store.dispatch(SnarkPoolCandidateAction::WorkReceived { - // peer_id, - // work: snark.clone(), - // }); - // } - // Some(P2pRpcResponse::InitialPeers(_)) => {} - // } - // store.dispatch(TransitionFrontierSyncLedgerSnarkedAction::PeersQuery); - // store.dispatch( - // TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit {}, - // ); - // store.dispatch(TransitionFrontierSyncAction::BlocksPeersQuery); - } - P2pChannelsRpcAction::RequestReceived { - peer_id, - id, - request, - } => { - // match *request { - // P2pRpcRequest::BestTipWithProof => { - // let best_chain = &store.state().transition_frontier.best_chain; - // let response = None.or_else(|| { - // let best_tip = best_chain.last()?; - // let mut chain_iter = best_chain.iter(); - // let root_block = chain_iter.next()?; - // // TODO(binier): cache body hashes - // let Ok(body_hashes) = chain_iter - // .map(|b| b.header().protocol_state.body.try_hash()) - // .collect::>() - // else { - // openmina_core::error!(meta.time(); "P2pRpcRequest::BestTipWithProof: invalid protocol state"); - // return None; - // }; - - // Some(BestTipWithProof { - // best_tip: best_tip.block().clone(), - // proof: (body_hashes, root_block.block().clone()), - // }) - // }); - // let response = - // response.map(P2pRpcResponse::BestTipWithProof).map(Box::new); - // store.dispatch(P2pChannelsRpcAction::ResponseSend { - // peer_id, - // id, - // response, - // }); - // } - // P2pRpcRequest::Block(hash) => { - // let best_chain = &store.state().transition_frontier.best_chain; - // let response = best_chain - // .iter() - // .rev() - // .find(|b| b.hash() == &hash) - // .map(|b| b.block().clone()) - // .map(P2pRpcResponse::Block) - // .map(Box::new); - // store.dispatch(P2pChannelsRpcAction::ResponseSend { - // peer_id, - // id, - // response, - // }); - // } - // P2pRpcRequest::LedgerQuery(..) => { - // // async ledger request will be triggered - // // by `LedgerReadAction::FindTodos`. - // } - // P2pRpcRequest::StagedLedgerAuxAndPendingCoinbasesAtBlock(..) => { - // // async ledger request will be triggered - // // by `LedgerReadAction::FindTodos`. - // } - // P2pRpcRequest::Snark(job_id) => { - // let job = store.state().snark_pool.get(&job_id); - // let response = job - // .and_then(|job| job.snark.as_ref()) - // .map(|snark| snark.work.clone()) - // .map(P2pRpcResponse::Snark) - // .map(Box::new); - - // store.dispatch(P2pChannelsRpcAction::ResponseSend { - // peer_id, - // id, - // response, - // }); - // } - // P2pRpcRequest::InitialPeers => { - // let p2p = p2p_ready!(store.state().p2p, meta.time()); - // let peers = p2p - // .peers - // .iter() - // .filter_map(|(_, v)| v.dial_opts.clone()) - // .collect(); - // let response = Some(Box::new(P2pRpcResponse::InitialPeers(peers))); - - // store.dispatch(P2pChannelsRpcAction::ResponseSend { - // peer_id, - // id, - // response, - // }); - // } - // } - } - P2pChannelsRpcAction::Init { .. } => {} - P2pChannelsRpcAction::Pending { .. } => {} - P2pChannelsRpcAction::RequestSend { .. } => {} - P2pChannelsRpcAction::ResponsePending { .. } => {} - P2pChannelsRpcAction::ResponseSend { .. } => {} - } - } - P2pChannelsAction::StreamingRpc(action) => { - match action { - P2pChannelsStreamingRpcAction::Ready { .. } => { - // store - // .dispatch(TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit); - } - P2pChannelsStreamingRpcAction::Timeout { peer_id, id } => { - // let Some(peer) = store.state().p2p.get_ready_peer(&peer_id) else { - // bug_condition!("get_ready_peer({:?}) returned None", peer_id); - // return; - // }; - // let Some(rpc_kind) = peer.channels.streaming_rpc.pending_local_rpc_kind() - // else { - // bug_condition!( - // "peer: {:?} pending_local_rpc_kind() returned None", - // peer_id - // ); - // return; - // }; - // store.dispatch( - // TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchError { - // peer_id, - // rpc_id: id, - // error: PeerStagedLedgerPartsFetchError::Timeout, - // }, - // ); - // store.dispatch(P2pDisconnectionAction::Init { - // peer_id, - // reason: P2pDisconnectionReason::TransitionFrontierStreamingRpcTimeout( - // rpc_kind, - // ), - // }); - } - P2pChannelsStreamingRpcAction::ResponseReceived { - peer_id, - id, - response, - } => { - // match response { - // None => { - // store.dispatch( - // TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchError { - // peer_id, - // rpc_id: id, - // error: PeerStagedLedgerPartsFetchError::DataUnavailable, - // }, - // ); - // } - // Some(P2pStreamingRpcResponseFull::StagedLedgerParts(parts)) => { - // store.dispatch( - // TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchSuccess { - // peer_id, - // rpc_id: id, - // parts, - // }, - // ); - // } - // } - // store.dispatch( - // TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit {}, - // ); - } - P2pChannelsStreamingRpcAction::RequestReceived { - peer_id: _, - id: _, - request, - } => { - match *request { - P2pStreamingRpcRequest::StagedLedgerParts(..) => { - // async ledger request will be triggered - // by `LedgerReadAction::FindTodos`. - } - } - } - _ => {} - } - } - }, P2pAction::ChannelsEffectful(action) => match action { P2pChannelsEffectfulAction::BestTip(action) => action.effects(&meta, store), P2pChannelsEffectfulAction::Transaction(action) => action.effects(&meta, store), @@ -647,26 +24,6 @@ pub fn node_p2p_effects(store: &mut Store, action: P2pActionWithM P2pChannelsEffectfulAction::Rpc(action) => action.effects(&meta, store), P2pChannelsEffectfulAction::SnarkJobCommitment(action) => action.effects(&meta, store), }, - P2pAction::Peer(action) => match action { - P2pPeerAction::Discovered { .. } - | P2pPeerAction::Ready { .. } - | P2pPeerAction::Remove { .. } => { - // handled by reducer - } - P2pPeerAction::BestTipUpdate { best_tip, .. } => { - // store.dispatch(ConsensusAction::BlockReceived { - // hash: best_tip.hash, - // block: best_tip.block, - // chain_proof: None, - // }); - // store.dispatch(TransitionFrontierSyncLedgerSnarkedAction::PeersQuery); - // store.dispatch(TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit); - // store.dispatch(TransitionFrontierSyncAction::BlocksPeersQuery); - } - }, - P2pAction::Identify(_) => { - // handled by reducer - } P2pAction::Network(_action) => { #[cfg(feature = "p2p-libp2p")] _action.effects(&meta, store); @@ -675,5 +32,12 @@ pub fn node_p2p_effects(store: &mut Store, action: P2pActionWithM P2pConnectionEffectfulAction::Outgoing(action) => action.effects(&meta, store), P2pConnectionEffectfulAction::Incoming(action) => action.effects(&meta, store), }, + P2pAction::Peer(_) + | P2pAction::Channels(_) + | P2pAction::Connection(_) + | P2pAction::Disconnection(_) + | P2pAction::Identify(_) => { + // handled by reducer + } } } diff --git a/node/src/rpc/rpc_reducer.rs b/node/src/rpc/rpc_reducer.rs index 3a4c25a4b9..ce4051cdbc 100644 --- a/node/src/rpc/rpc_reducer.rs +++ b/node/src/rpc/rpc_reducer.rs @@ -171,7 +171,7 @@ impl RpcState { RpcAction::BestChain { .. } => {} RpcAction::ConsensusConstantsGet { .. } => {} RpcAction::TransactionStatusGet { .. } => {} - RpcAction::P2pConnectionIncomingAnswerReady { .. } => {}, + RpcAction::P2pConnectionIncomingAnswerReady { .. } => {} } } } diff --git a/node/src/state.rs b/node/src/state.rs index fb0b120c34..fc02c7e15b 100644 --- a/node/src/state.rs +++ b/node/src/state.rs @@ -39,9 +39,7 @@ use crate::transition_frontier::genesis::TransitionFrontierGenesisState; use crate::transition_frontier::sync::ledger::snarked::{ TransitionFrontierSyncLedgerSnarkedAction, TransitionFrontierSyncLedgerSnarkedState, }; -use crate::transition_frontier::sync::ledger::staged::{ - TransitionFrontierSyncLedgerStagedAction, TransitionFrontierSyncLedgerStagedState, -}; +use crate::transition_frontier::sync::ledger::staged::TransitionFrontierSyncLedgerStagedState; use crate::transition_frontier::sync::ledger::TransitionFrontierSyncLedgerState; use crate::transition_frontier::sync::TransitionFrontierSyncState; pub use crate::transition_frontier::TransitionFrontierState; @@ -354,7 +352,13 @@ impl P2p { return Err(P2pInitializationError::AlreadyInitialized); }; - let callbacks = P2pCallbacks { + let callbacks = Self::p2p_callbacks(); + *self = P2p::Ready(P2pState::new(config.clone(), callbacks, chain_id)); + Ok(()) + } + + fn p2p_callbacks() -> P2pCallbacks { + P2pCallbacks { on_p2p_channels_transaction_libp2p_received: Some(redux::callback!( on_p2p_channels_transaction_libp2p_received(transaction: Box) -> crate::Action{ TransactionPoolAction::StartVerify { commands: std::iter::once(*transaction).collect(), from_rpc: None } @@ -450,10 +454,7 @@ impl P2p { P2pCallbacksAction::P2pChannelsStreamingRpcTimeout { peer_id, id } } )), - }; - - *self = P2p::Ready(P2pState::new(config.clone(), callbacks, chain_id)); - Ok(()) + } } pub fn ready(&self) -> Option<&P2pState> { diff --git a/p2p/src/connection/incoming/p2p_connection_incoming_reducer.rs b/p2p/src/connection/incoming/p2p_connection_incoming_reducer.rs index 3ba4532fa4..5eab82f470 100644 --- a/p2p/src/connection/incoming/p2p_connection_incoming_reducer.rs +++ b/p2p/src/connection/incoming/p2p_connection_incoming_reducer.rs @@ -172,7 +172,7 @@ impl P2pConnectionIncomingState { answer: answer.clone(), }); - if let Some(rpc_id) = p2p_state.peer_connection_rpc_id(&peer_id) { + if let Some(rpc_id) = p2p_state.peer_connection_rpc_id(peer_id) { if let Some(callback) = &p2p_state.callbacks.on_p2p_connection_incoming_answer_ready { From f43b87df04b5d4f104994aa27b0128c4d7428d5d Mon Sep 17 00:00:00 2001 From: Mimir Date: Tue, 8 Oct 2024 16:45:02 +0200 Subject: [PATCH 4/4] Review fixes --- node/src/action_kind.rs | 10 +- .../p2p/callbacks/p2p_callbacks_actions.rs | 14 +- .../p2p/callbacks/p2p_callbacks_reducer.rs | 565 +++++++++++------- node/src/state.rs | 24 +- ...on_frontier_sync_ledger_snarked_actions.rs | 5 - ...on_frontier_sync_ledger_snarked_effects.rs | 2 - ...on_frontier_sync_ledger_snarked_reducer.rs | 96 +-- .../transition_frontier_actions.rs | 8 - .../transition_frontier_effects.rs | 1 - .../transition_frontier_reducer.rs | 17 +- p2p/src/p2p_state.rs | 36 +- 11 files changed, 400 insertions(+), 378 deletions(-) diff --git a/node/src/action_kind.rs b/node/src/action_kind.rs index b614e2cd1f..ff2b74ec88 100644 --- a/node/src/action_kind.rs +++ b/node/src/action_kind.rs @@ -183,6 +183,8 @@ pub enum ActionKind { P2pCallbacksP2pChannelsStreamingRpcReady, P2pCallbacksP2pChannelsStreamingRpcResponseReceived, P2pCallbacksP2pChannelsStreamingRpcTimeout, + P2pCallbacksP2pDisconnection, + P2pCallbacksRpcRespondBestTip, P2pChannelsBestTipInit, P2pChannelsBestTipPending, P2pChannelsBestTipReady, @@ -520,7 +522,6 @@ pub enum ActionKind { TransactionPoolVerifyError, TransactionPoolEffectfulFetchAccounts, TransitionFrontierGenesisInject, - TransitionFrontierRpcRespondBestTip, TransitionFrontierSyncFailed, TransitionFrontierSynced, TransitionFrontierGenesisLedgerLoadInit, @@ -570,7 +571,6 @@ pub enum ActionKind { TransitionFrontierSyncLedgerSnarkedNumAccountsReceived, TransitionFrontierSyncLedgerSnarkedNumAccountsRejected, TransitionFrontierSyncLedgerSnarkedNumAccountsSuccess, - TransitionFrontierSyncLedgerSnarkedP2pDisconnection, TransitionFrontierSyncLedgerSnarkedPeerQueryAddressError, TransitionFrontierSyncLedgerSnarkedPeerQueryAddressInit, TransitionFrontierSyncLedgerSnarkedPeerQueryAddressPending, @@ -697,6 +697,8 @@ impl ActionKindGet for P2pCallbacksAction { Self::P2pChannelsStreamingRpcResponseReceived { .. } => { ActionKind::P2pCallbacksP2pChannelsStreamingRpcResponseReceived } + Self::P2pDisconnection { .. } => ActionKind::P2pCallbacksP2pDisconnection, + Self::RpcRespondBestTip { .. } => ActionKind::P2pCallbacksRpcRespondBestTip, } } } @@ -750,7 +752,6 @@ impl ActionKindGet for TransitionFrontierAction { Self::GenesisInject => ActionKind::TransitionFrontierGenesisInject, Self::Synced { .. } => ActionKind::TransitionFrontierSynced, Self::SyncFailed { .. } => ActionKind::TransitionFrontierSyncFailed, - Self::RpcRespondBestTip { .. } => ActionKind::TransitionFrontierRpcRespondBestTip, } } } @@ -1922,9 +1923,6 @@ impl ActionKindGet for TransitionFrontierSyncLedgerSnarkedAction { ActionKind::TransitionFrontierSyncLedgerSnarkedMerkleTreeSyncSuccess } Self::Success => ActionKind::TransitionFrontierSyncLedgerSnarkedSuccess, - Self::P2pDisconnection { .. } => { - ActionKind::TransitionFrontierSyncLedgerSnarkedP2pDisconnection - } } } } diff --git a/node/src/p2p/callbacks/p2p_callbacks_actions.rs b/node/src/p2p/callbacks/p2p_callbacks_actions.rs index 30202dea9e..92078b9fac 100644 --- a/node/src/p2p/callbacks/p2p_callbacks_actions.rs +++ b/node/src/p2p/callbacks/p2p_callbacks_actions.rs @@ -39,10 +39,17 @@ pub enum P2pCallbacksAction { id: P2pRpcId, response: Option, }, + + P2pDisconnection { + peer_id: PeerId, + }, + RpcRespondBestTip { + peer_id: PeerId, + }, } impl redux::EnablingCondition for P2pCallbacksAction { - fn is_enabled(&self, _state: &crate::State, _time: redux::Timestamp) -> bool { + fn is_enabled(&self, state: &crate::State, _time: redux::Timestamp) -> bool { match self { P2pCallbacksAction::P2pChannelsRpcReady { .. } => true, P2pCallbacksAction::P2pChannelsRpcTimeout { .. } => true, @@ -51,6 +58,11 @@ impl redux::EnablingCondition for P2pCallbacksAction { P2pCallbacksAction::P2pChannelsStreamingRpcReady => true, P2pCallbacksAction::P2pChannelsStreamingRpcTimeout { .. } => true, P2pCallbacksAction::P2pChannelsStreamingRpcResponseReceived { .. } => true, + P2pCallbacksAction::P2pDisconnection { .. } => true, + // TODO: what if we don't have best tip? + P2pCallbacksAction::RpcRespondBestTip { .. } => { + state.transition_frontier.best_tip().is_some() + } } } } diff --git a/node/src/p2p/callbacks/p2p_callbacks_reducer.rs b/node/src/p2p/callbacks/p2p_callbacks_reducer.rs index d41b2dc62e..fcabd0d449 100644 --- a/node/src/p2p/callbacks/p2p_callbacks_reducer.rs +++ b/node/src/p2p/callbacks/p2p_callbacks_reducer.rs @@ -3,12 +3,14 @@ use mina_p2p_messages::v2::{MinaLedgerSyncLedgerAnswerStableV2, StateHash}; use openmina_core::{block::BlockWithHash, bug_condition}; use p2p::{ channels::{ + best_tip::P2pChannelsBestTipAction, rpc::{BestTipWithProof, P2pChannelsRpcAction, P2pRpcRequest, P2pRpcResponse}, streaming_rpc::P2pStreamingRpcResponseFull, }, disconnection::{P2pDisconnectionAction, P2pDisconnectionReason}, + PeerId, }; -use redux::ActionWithMeta; +use redux::{ActionMeta, ActionWithMeta, Dispatcher}; use crate::{ p2p_ready, @@ -23,7 +25,10 @@ use crate::{ }, PeerBlockFetchError, TransitionFrontierSyncAction, }, - ConsensusAction, + watched_accounts::{ + WatchedAccountLedgerInitialState, WatchedAccountsLedgerInitialStateGetError, + }, + Action, ConsensusAction, State, WatchedAccountsAction, }; use super::P2pCallbacksAction; @@ -93,143 +98,9 @@ impl crate::State { id, response, } => { - let peer_id = *peer_id; - let id = *id; - - match response.as_deref() { - None => { - dispatcher.push( - TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressError { - peer_id, - rpc_id: id, - error: PeerLedgerQueryError::DataUnavailable, - }, - ); - dispatcher.push( - TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchError { - peer_id, - rpc_id: id, - error: PeerStagedLedgerPartsFetchError::DataUnavailable, - }, - ); - dispatcher.push(TransitionFrontierSyncAction::BlocksPeerQueryError { - peer_id, - rpc_id: id, - error: PeerBlockFetchError::DataUnavailable, - }); - } - Some(P2pRpcResponse::BestTipWithProof(resp)) => { - let (body_hashes, root_block) = &resp.proof; - - let (Ok(best_tip), Ok(root_block)) = ( - BlockWithHash::try_new(resp.best_tip.clone()), - BlockWithHash::try_new(root_block.clone()), - ) else { - openmina_core::error!(meta.time(); "P2pRpcResponse::BestTipWithProof: invalid blocks"); - return; - }; - - // reconstruct hashes - let Ok(hashes) = body_hashes - .iter() - .take(body_hashes.len().saturating_sub(1)) - .scan(root_block.hash.clone(), |pred_hash, body_hash| { - *pred_hash = match StateHash::try_from_hashes(pred_hash, body_hash) - { - Ok(hash) => hash, - Err(_) => return Some(Err(InvalidBigInt)), - }; - Some(Ok(pred_hash.clone())) - }) - .collect::, _>>() - else { - openmina_core::error!(meta.time(); "P2pRpcResponse::BestTipWithProof: invalid hashes"); - return; - }; - - if let Some(pred_hash) = hashes.last() { - let expected_hash = - &best_tip.block.header.protocol_state.previous_state_hash; - if pred_hash != expected_hash { - openmina_core::warn!(meta.time(); - kind = "P2pRpcBestTipHashMismatch", - response = serde_json::to_string(&resp).ok(), - expected_hash = expected_hash.to_string(), - calculated_hash = pred_hash.to_string()); - return; - } - } - dispatcher.push(ConsensusAction::BlockChainProofUpdate { - hash: best_tip.hash, - chain_proof: (hashes, root_block), - }); - } - Some(P2pRpcResponse::LedgerQuery(answer)) => match answer { - MinaLedgerSyncLedgerAnswerStableV2::ChildHashesAre(left, right) => { - dispatcher.push( - TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressSuccess { - peer_id, - rpc_id: id, - response: PeerLedgerQueryResponse::ChildHashes( - left.clone(), - right.clone(), - ), - }, - ); - } - MinaLedgerSyncLedgerAnswerStableV2::ContentsAre(accounts) => { - dispatcher.push( - TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressSuccess { - peer_id, - rpc_id: id, - response: PeerLedgerQueryResponse::ChildAccounts( - accounts.iter().cloned().collect(), - ), - }, - ); - } - MinaLedgerSyncLedgerAnswerStableV2::NumAccounts(count, contents_hash) => { - dispatcher.push( - TransitionFrontierSyncLedgerSnarkedAction::PeerQueryNumAccountsSuccess { - peer_id, - rpc_id: id, - response: PeerLedgerQueryResponse::NumAccounts( - count.as_u64(), contents_hash.clone() - ), - }, - ); - } - }, - Some(P2pRpcResponse::StagedLedgerAuxAndPendingCoinbasesAtBlock(parts)) => { - dispatcher.push( - TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchSuccess { - peer_id, - rpc_id: id, - parts: parts.clone(), - }, - ); - } - Some(P2pRpcResponse::Block(block)) => { - let Ok(block) = BlockWithHash::try_new(block.clone()) else { - openmina_core::error!(meta.time(); "P2pRpcResponse::Block: invalid block"); - return; - }; - dispatcher.push(TransitionFrontierSyncAction::BlocksPeerQuerySuccess { - peer_id, - rpc_id: id, - response: block, - }); - } - Some(P2pRpcResponse::Snark(snark)) => { - dispatcher.push(SnarkPoolCandidateAction::WorkReceived { - peer_id, - work: snark.clone(), - }); - } - Some(P2pRpcResponse::InitialPeers(_)) => {} - } + State::handle_rpc_channels_response(dispatcher, meta, *id, *peer_id, response); dispatcher.push(TransitionFrontierSyncLedgerSnarkedAction::PeersQuery); - dispatcher.push(TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit {}); + dispatcher.push(TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit); dispatcher.push(TransitionFrontierSyncAction::BlocksPeersQuery); } P2pCallbacksAction::P2pChannelsRpcRequestReceived { @@ -237,90 +108,14 @@ impl crate::State { id, request, } => { - let peer_id = *peer_id; - let id = *id; - - match *request.clone() { - P2pRpcRequest::BestTipWithProof => { - let best_chain = &state.transition_frontier.best_chain; - let response = None.or_else(|| { - let best_tip = best_chain.last()?; - let mut chain_iter = best_chain.iter(); - let root_block = chain_iter.next()?; - // TODO(binier): cache body hashes - let Ok(body_hashes) = chain_iter - .map(|b| b.header().protocol_state.body.try_hash()) - .collect::>() - else { - openmina_core::error!(meta.time(); "P2pRpcRequest::BestTipWithProof: invalid protocol state"); - return None; - }; - - Some(BestTipWithProof { - best_tip: best_tip.block().clone(), - proof: (body_hashes, root_block.block().clone()), - }) - }); - let response = response.map(P2pRpcResponse::BestTipWithProof).map(Box::new); - dispatcher.push(P2pChannelsRpcAction::ResponseSend { - peer_id, - id, - response, - }); - } - P2pRpcRequest::Block(hash) => { - let best_chain = &state.transition_frontier.best_chain; - let response = best_chain - .iter() - .rev() - .find(|b| b.hash() == &hash) - .map(|b| b.block().clone()) - .map(P2pRpcResponse::Block) - .map(Box::new); - dispatcher.push(P2pChannelsRpcAction::ResponseSend { - peer_id, - id, - response, - }); - } - P2pRpcRequest::LedgerQuery(..) => { - // async ledger request will be triggered - // by `LedgerReadAction::FindTodos`. - } - P2pRpcRequest::StagedLedgerAuxAndPendingCoinbasesAtBlock(..) => { - // async ledger request will be triggered - // by `LedgerReadAction::FindTodos`. - } - P2pRpcRequest::Snark(job_id) => { - let job = state.snark_pool.get(&job_id); - let response = job - .and_then(|job| job.snark.as_ref()) - .map(|snark| snark.work.clone()) - .map(P2pRpcResponse::Snark) - .map(Box::new); - - dispatcher.push(P2pChannelsRpcAction::ResponseSend { - peer_id, - id, - response, - }); - } - P2pRpcRequest::InitialPeers => { - let p2p = p2p_ready!(state.p2p, meta.time()); - let peers = p2p - .peers - .iter() - .filter_map(|(_, v)| v.dial_opts.clone()) - .collect(); - let response = Some(Box::new(P2pRpcResponse::InitialPeers(peers))); - - dispatcher.push(P2pChannelsRpcAction::ResponseSend { - peer_id, - id, - response, - }); - } - } + State::handle_rpc_channels_request( + dispatcher, + state, + meta, + *request.clone(), + *peer_id, + *id, + ); } P2pCallbacksAction::P2pChannelsStreamingRpcReady => { dispatcher.push(TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit); @@ -377,8 +172,332 @@ impl crate::State { ); } } - dispatcher.push(TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit {}); + dispatcher.push(TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit); + } + P2pCallbacksAction::P2pDisconnection { peer_id } => { + let peer_id = *peer_id; + + if let Some(s) = state.transition_frontier.sync.ledger() { + s.snarked() + .map(|s| { + s.peer_address_query_pending_rpc_ids(&peer_id) + .collect::>() + }) + .unwrap_or_default() + .into_iter() + .for_each(|rpc_id| { + dispatcher.push( + TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressError { + peer_id, + rpc_id, + error: PeerLedgerQueryError::Disconnected, + }, + ); + }); + + if let Some(rpc_id) = s + .snarked() + .and_then(|s| s.peer_num_accounts_rpc_id(&peer_id)) + { + dispatcher.push( + TransitionFrontierSyncLedgerSnarkedAction::PeerQueryNumAccountsError { + peer_id, + rpc_id, + error: PeerLedgerQueryError::Disconnected, + }, + ); + } + + if let Some(rpc_id) = s.staged().and_then(|s| s.parts_fetch_rpc_id(&peer_id)) { + dispatcher.push( + TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchError { + peer_id, + rpc_id, + error: PeerStagedLedgerPartsFetchError::Disconnected, + }, + ) + } + } + + state + .transition_frontier + .sync + .blocks_fetch_from_peer_pending_rpc_ids(&peer_id) + .for_each(|rpc_id| { + dispatcher.push(TransitionFrontierSyncAction::BlocksPeerQueryError { + peer_id, + rpc_id, + error: PeerBlockFetchError::Disconnected, + }); + }); + + state + .watched_accounts + .iter() + .filter_map(|(pub_key, a)| match &a.initial_state { + WatchedAccountLedgerInitialState::Pending { + peer_id: account_peer_id, + .. + } => { + if account_peer_id == &peer_id { + Some(WatchedAccountsAction::LedgerInitialStateGetError { + pub_key: pub_key.clone(), + error: + WatchedAccountsLedgerInitialStateGetError::PeerDisconnected, + }) + } else { + None + } + } + _ => None, + }) + .for_each(|action| dispatcher.push(action)); + + dispatcher.push(SnarkPoolCandidateAction::PeerPrune { peer_id }); + } + P2pCallbacksAction::RpcRespondBestTip { peer_id } => { + let Some(best_tip) = state.transition_frontier.best_tip() else { + bug_condition!("Best tip not found"); + return; + }; + + dispatcher.push(P2pChannelsBestTipAction::ResponseSend { + peer_id: *peer_id, + best_tip: best_tip.clone(), + }); + } + } + } + + fn handle_rpc_channels_request( + dispatcher: &mut Dispatcher, + state: &State, + meta: ActionMeta, + request: P2pRpcRequest, + peer_id: PeerId, + id: u64, + ) { + match request { + P2pRpcRequest::BestTipWithProof => { + let best_chain = &state.transition_frontier.best_chain; + let response = None.or_else(|| { + let best_tip = best_chain.last()?; + let mut chain_iter = best_chain.iter(); + let root_block = chain_iter.next()?; + // TODO(binier): cache body hashes + let Ok(body_hashes) = chain_iter + .map(|b| b.header().protocol_state.body.try_hash()) + .collect::>() + else { + openmina_core::error!(meta.time(); "P2pRpcRequest::BestTipWithProof: invalid protocol state"); + return None; + }; + + Some(BestTipWithProof { + best_tip: best_tip.block().clone(), + proof: (body_hashes, root_block.block().clone()), + }) + }); + let response = response.map(P2pRpcResponse::BestTipWithProof).map(Box::new); + dispatcher.push(P2pChannelsRpcAction::ResponseSend { + peer_id, + id, + response, + }); + } + P2pRpcRequest::Block(hash) => { + let best_chain = &state.transition_frontier.best_chain; + let response = best_chain + .iter() + .rev() + .find(|b| b.hash() == &hash) + .map(|b| b.block().clone()) + .map(P2pRpcResponse::Block) + .map(Box::new); + dispatcher.push(P2pChannelsRpcAction::ResponseSend { + peer_id, + id, + response, + }); + } + P2pRpcRequest::LedgerQuery(..) => { + // async ledger request will be triggered + // by `LedgerReadAction::FindTodos`. + } + P2pRpcRequest::StagedLedgerAuxAndPendingCoinbasesAtBlock(..) => { + // async ledger request will be triggered + // by `LedgerReadAction::FindTodos`. + } + P2pRpcRequest::Snark(job_id) => { + let job = state.snark_pool.get(&job_id); + let response = job + .and_then(|job| job.snark.as_ref()) + .map(|snark| snark.work.clone()) + .map(P2pRpcResponse::Snark) + .map(Box::new); + + dispatcher.push(P2pChannelsRpcAction::ResponseSend { + peer_id, + id, + response, + }); + } + P2pRpcRequest::InitialPeers => { + let p2p = p2p_ready!(state.p2p, meta.time()); + let peers = p2p + .peers + .iter() + .filter_map(|(_, v)| v.dial_opts.clone()) + .collect(); + let response = Some(Box::new(P2pRpcResponse::InitialPeers(peers))); + + dispatcher.push(P2pChannelsRpcAction::ResponseSend { + peer_id, + id, + response, + }); + } + } + } + + fn handle_rpc_channels_response( + dispatcher: &mut Dispatcher, + meta: ActionMeta, + id: u64, + peer_id: PeerId, + response: &Option>, + ) { + match response.as_deref() { + None => { + dispatcher.push( + TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressError { + peer_id, + rpc_id: id, + error: PeerLedgerQueryError::DataUnavailable, + }, + ); + dispatcher.push( + TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchError { + peer_id, + rpc_id: id, + error: PeerStagedLedgerPartsFetchError::DataUnavailable, + }, + ); + dispatcher.push(TransitionFrontierSyncAction::BlocksPeerQueryError { + peer_id, + rpc_id: id, + error: PeerBlockFetchError::DataUnavailable, + }); + } + Some(P2pRpcResponse::BestTipWithProof(resp)) => { + let (body_hashes, root_block) = &resp.proof; + + let (Ok(best_tip), Ok(root_block)) = ( + BlockWithHash::try_new(resp.best_tip.clone()), + BlockWithHash::try_new(root_block.clone()), + ) else { + openmina_core::error!(meta.time(); "P2pRpcResponse::BestTipWithProof: invalid blocks"); + return; + }; + + // reconstruct hashes + let Ok(hashes) = body_hashes + .iter() + .take(body_hashes.len().saturating_sub(1)) + .scan(root_block.hash.clone(), |pred_hash, body_hash| { + *pred_hash = match StateHash::try_from_hashes(pred_hash, body_hash) { + Ok(hash) => hash, + Err(_) => return Some(Err(InvalidBigInt)), + }; + Some(Ok(pred_hash.clone())) + }) + .collect::, _>>() + else { + openmina_core::error!(meta.time(); "P2pRpcResponse::BestTipWithProof: invalid hashes"); + return; + }; + + if let Some(pred_hash) = hashes.last() { + let expected_hash = &best_tip.block.header.protocol_state.previous_state_hash; + + if pred_hash != expected_hash { + openmina_core::warn!(meta.time(); + kind = "P2pRpcBestTipHashMismatch", + response = serde_json::to_string(&resp).ok(), + expected_hash = expected_hash.to_string(), + calculated_hash = pred_hash.to_string()); + return; + } + } + dispatcher.push(ConsensusAction::BlockChainProofUpdate { + hash: best_tip.hash, + chain_proof: (hashes, root_block), + }); + } + Some(P2pRpcResponse::LedgerQuery(answer)) => match answer { + MinaLedgerSyncLedgerAnswerStableV2::ChildHashesAre(left, right) => { + dispatcher.push( + TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressSuccess { + peer_id, + rpc_id: id, + response: PeerLedgerQueryResponse::ChildHashes( + left.clone(), + right.clone(), + ), + }, + ); + } + MinaLedgerSyncLedgerAnswerStableV2::ContentsAre(accounts) => { + dispatcher.push( + TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressSuccess { + peer_id, + rpc_id: id, + response: PeerLedgerQueryResponse::ChildAccounts( + accounts.iter().cloned().collect(), + ), + }, + ); + } + MinaLedgerSyncLedgerAnswerStableV2::NumAccounts(count, contents_hash) => { + dispatcher.push( + TransitionFrontierSyncLedgerSnarkedAction::PeerQueryNumAccountsSuccess { + peer_id, + rpc_id: id, + response: PeerLedgerQueryResponse::NumAccounts( + count.as_u64(), + contents_hash.clone(), + ), + }, + ); + } + }, + Some(P2pRpcResponse::StagedLedgerAuxAndPendingCoinbasesAtBlock(parts)) => { + dispatcher.push( + TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchSuccess { + peer_id, + rpc_id: id, + parts: parts.clone(), + }, + ); + } + Some(P2pRpcResponse::Block(block)) => { + let Ok(block) = BlockWithHash::try_new(block.clone()) else { + openmina_core::error!(meta.time(); "P2pRpcResponse::Block: invalid block"); + return; + }; + dispatcher.push(TransitionFrontierSyncAction::BlocksPeerQuerySuccess { + peer_id, + rpc_id: id, + response: block, + }); + } + Some(P2pRpcResponse::Snark(snark)) => { + dispatcher.push(SnarkPoolCandidateAction::WorkReceived { + peer_id, + work: snark.clone(), + }); } + Some(P2pRpcResponse::InitialPeers(_)) => {} } } } diff --git a/node/src/state.rs b/node/src/state.rs index fc02c7e15b..20c124f1da 100644 --- a/node/src/state.rs +++ b/node/src/state.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use mina_p2p_messages::v2::{MinaBaseUserCommandStableV2, MinaBlockBlockStableV2}; use openmina_core::block::BlockWithHash; -use openmina_core::requests::{RequestId, RpcIdType}; +use openmina_core::requests::RpcId; use openmina_core::snark::{Snark, SnarkInfo}; use openmina_core::{ block::ArcBlockWithHash, consensus::ConsensusConstants, constants::constraint_constants, error, @@ -36,9 +36,7 @@ pub use crate::snark_pool::candidate::SnarkPoolCandidatesState; pub use crate::snark_pool::SnarkPoolState; use crate::transaction_pool::TransactionPoolState; use crate::transition_frontier::genesis::TransitionFrontierGenesisState; -use crate::transition_frontier::sync::ledger::snarked::{ - TransitionFrontierSyncLedgerSnarkedAction, TransitionFrontierSyncLedgerSnarkedState, -}; +use crate::transition_frontier::sync::ledger::snarked::TransitionFrontierSyncLedgerSnarkedState; use crate::transition_frontier::sync::ledger::staged::TransitionFrontierSyncLedgerStagedState; use crate::transition_frontier::sync::ledger::TransitionFrontierSyncLedgerState; use crate::transition_frontier::sync::TransitionFrontierSyncState; @@ -46,9 +44,7 @@ pub use crate::transition_frontier::TransitionFrontierState; pub use crate::watched_accounts::WatchedAccountsState; pub use crate::Config; use crate::{config::GlobalConfig, SnarkPoolAction}; -use crate::{ - ActionWithMeta, ConsensusAction, RpcAction, TransactionPoolAction, TransitionFrontierAction, -}; +use crate::{ActionWithMeta, ConsensusAction, RpcAction, TransactionPoolAction}; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct State { @@ -386,36 +382,36 @@ impl P2p { )), on_p2p_channels_best_tip_request_received: Some(redux::callback!( on_p2p_channels_best_tip_request_received(peer_id: PeerId) -> crate::Action{ - TransitionFrontierAction::RpcRespondBestTip{peer_id} + P2pCallbacksAction::RpcRespondBestTip { peer_id } } )), on_p2p_disconnection_finish: Some(redux::callback!( on_p2p_disconnection_finish(peer_id: PeerId) -> crate::Action{ - TransitionFrontierSyncLedgerSnarkedAction::P2pDisconnection { peer_id } + P2pCallbacksAction::P2pDisconnection { peer_id } } )), on_p2p_connection_outgoing_error: Some(redux::callback!( - on_p2p_connection_outgoing_error((rpc_id: RequestId, error: P2pConnectionOutgoingError)) -> crate::Action{ + on_p2p_connection_outgoing_error((rpc_id: RpcId, error: P2pConnectionOutgoingError)) -> crate::Action{ RpcAction::P2pConnectionOutgoingError { rpc_id, error } } )), on_p2p_connection_outgoing_success: Some(redux::callback!( - on_p2p_connection_outgoing_success(rpc_id: RequestId) -> crate::Action{ + on_p2p_connection_outgoing_success(rpc_id: RpcId) -> crate::Action{ RpcAction::P2pConnectionOutgoingSuccess { rpc_id } } )), on_p2p_connection_incoming_error: Some(redux::callback!( - on_p2p_connection_incoming_error((rpc_id: RequestId, error: String)) -> crate::Action{ + on_p2p_connection_incoming_error((rpc_id: RpcId, error: String)) -> crate::Action{ RpcAction::P2pConnectionIncomingError { rpc_id, error } } )), on_p2p_connection_incoming_success: Some(redux::callback!( - on_p2p_connection_incoming_success(rpc_id: RequestId) -> crate::Action{ + on_p2p_connection_incoming_success(rpc_id: RpcId) -> crate::Action{ RpcAction::P2pConnectionIncomingSuccess { rpc_id } } )), on_p2p_connection_incoming_answer_ready: Some(redux::callback!( - on_p2p_connection_incoming_answer_ready((rpc_id: RequestId, peer_id: PeerId, answer: P2pConnectionResponse)) -> crate::Action{ + on_p2p_connection_incoming_answer_ready((rpc_id: RpcId, peer_id: PeerId, answer: P2pConnectionResponse)) -> crate::Action{ RpcAction::P2pConnectionIncomingAnswerReady { rpc_id, answer, peer_id } } )), diff --git a/node/src/transition_frontier/sync/ledger/snarked/transition_frontier_sync_ledger_snarked_actions.rs b/node/src/transition_frontier/sync/ledger/snarked/transition_frontier_sync_ledger_snarked_actions.rs index 71da5df805..331cf7bf08 100644 --- a/node/src/transition_frontier/sync/ledger/snarked/transition_frontier_sync_ledger_snarked_actions.rs +++ b/node/src/transition_frontier/sync/ledger/snarked/transition_frontier_sync_ledger_snarked_actions.rs @@ -130,10 +130,6 @@ pub enum TransitionFrontierSyncLedgerSnarkedAction { MerkleTreeSyncSuccess, #[action_event(level = info)] Success, - - P2pDisconnection { - peer_id: PeerId, - }, } impl redux::EnablingCondition for TransitionFrontierSyncLedgerSnarkedAction { @@ -454,7 +450,6 @@ impl redux::EnablingCondition for TransitionFrontierSyncLedgerSnar TransitionFrontierSyncLedgerSnarkedState::MerkleTreeSyncSuccess { .. } ) }), - TransitionFrontierSyncLedgerSnarkedAction::P2pDisconnection { .. } => true, } } } diff --git a/node/src/transition_frontier/sync/ledger/snarked/transition_frontier_sync_ledger_snarked_effects.rs b/node/src/transition_frontier/sync/ledger/snarked/transition_frontier_sync_ledger_snarked_effects.rs index 62146ef96a..51be57d2f8 100644 --- a/node/src/transition_frontier/sync/ledger/snarked/transition_frontier_sync_ledger_snarked_effects.rs +++ b/node/src/transition_frontier/sync/ledger/snarked/transition_frontier_sync_ledger_snarked_effects.rs @@ -34,8 +34,6 @@ impl TransitionFrontierSyncLedgerSnarkedAction { TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressError { .. } => {} TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressSuccess { .. } => {} - TransitionFrontierSyncLedgerSnarkedAction::P2pDisconnection { .. } => {} - TransitionFrontierSyncLedgerSnarkedAction::ChildHashesReceived { address, hashes: (left_hash, right_hash), diff --git a/node/src/transition_frontier/sync/ledger/snarked/transition_frontier_sync_ledger_snarked_reducer.rs b/node/src/transition_frontier/sync/ledger/snarked/transition_frontier_sync_ledger_snarked_reducer.rs index 1a6d143f6f..0a8bcd0951 100644 --- a/node/src/transition_frontier/sync/ledger/snarked/transition_frontier_sync_ledger_snarked_reducer.rs +++ b/node/src/transition_frontier/sync/ledger/snarked/transition_frontier_sync_ledger_snarked_reducer.rs @@ -11,21 +11,11 @@ use crate::{ ledger::{ ledger_empty_hash_at_depth, tree_height_for_num_accounts, LedgerAddress, LEDGER_DEPTH, }, - snark_pool::candidate::SnarkPoolCandidateAction, - transition_frontier::sync::{ - ledger::staged::{ - PeerStagedLedgerPartsFetchError, TransitionFrontierSyncLedgerStagedAction, - }, - PeerBlockFetchError, TransitionFrontierSyncAction, - }, - watched_accounts::{ - WatchedAccountLedgerInitialState, WatchedAccountsLedgerInitialStateGetError, - }, - Action, State, WatchedAccountsAction, + Action, State, }; use super::{ - LedgerAddressQueryPending, PeerLedgerQueryError, PeerLedgerQueryResponse, PeerRpcState, + LedgerAddressQueryPending, PeerLedgerQueryResponse, PeerRpcState, TransitionFrontierSyncLedgerSnarkedAction, TransitionFrontierSyncLedgerSnarkedActionWithMetaRef, TransitionFrontierSyncLedgerSnarkedState, ACCOUNT_SUBTREE_HEIGHT, @@ -611,88 +601,6 @@ impl TransitionFrontierSyncLedgerSnarkedState { target: target.clone(), }; } - TransitionFrontierSyncLedgerSnarkedAction::P2pDisconnection { peer_id } => { - let (dispatcher, state) = state_context.into_dispatcher_and_state(); - let peer_id = *peer_id; - - if let Some(s) = state.transition_frontier.sync.ledger() { - s.snarked() - .map(|s| { - s.peer_address_query_pending_rpc_ids(&peer_id) - .collect::>() - }) - .unwrap_or_default() - .into_iter() - .for_each(|rpc_id| { - dispatcher.push( - TransitionFrontierSyncLedgerSnarkedAction::PeerQueryAddressError { - peer_id, - rpc_id, - error: PeerLedgerQueryError::Disconnected, - }, - ); - }); - - if let Some(rpc_id) = s - .snarked() - .and_then(|s| s.peer_num_accounts_rpc_id(&peer_id)) - { - dispatcher.push( - TransitionFrontierSyncLedgerSnarkedAction::PeerQueryNumAccountsError { - peer_id, - rpc_id, - error: PeerLedgerQueryError::Disconnected, - }, - ); - } - - if let Some(rpc_id) = s.staged().and_then(|s| s.parts_fetch_rpc_id(&peer_id)) { - dispatcher.push( - TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchError { - peer_id, - rpc_id, - error: PeerStagedLedgerPartsFetchError::Disconnected, - }, - ) - } - } - - state - .transition_frontier - .sync - .blocks_fetch_from_peer_pending_rpc_ids(&peer_id) - .for_each(|rpc_id| { - dispatcher.push(TransitionFrontierSyncAction::BlocksPeerQueryError { - peer_id, - rpc_id, - error: PeerBlockFetchError::Disconnected, - }); - }); - - state - .watched_accounts - .iter() - .filter_map(|(pub_key, a)| match &a.initial_state { - WatchedAccountLedgerInitialState::Pending { - peer_id: account_peer_id, - .. - } => { - if account_peer_id == &peer_id { - Some(WatchedAccountsAction::LedgerInitialStateGetError { - pub_key: pub_key.clone(), - error: - WatchedAccountsLedgerInitialStateGetError::PeerDisconnected, - }) - } else { - None - } - } - _ => None, - }) - .for_each(|action| dispatcher.push(action)); - - dispatcher.push(SnarkPoolCandidateAction::PeerPrune { peer_id }); - } } } } diff --git a/node/src/transition_frontier/transition_frontier_actions.rs b/node/src/transition_frontier/transition_frontier_actions.rs index 6ca2c95498..bdd6c4f73e 100644 --- a/node/src/transition_frontier/transition_frontier_actions.rs +++ b/node/src/transition_frontier/transition_frontier_actions.rs @@ -3,7 +3,6 @@ use std::collections::BTreeSet; use mina_p2p_messages::v2::StateHash; use openmina_core::block::ArcBlockWithHash; use openmina_core::ActionEvent; -use p2p::PeerId; use serde::{Deserialize, Serialize}; use super::genesis::TransitionFrontierGenesisAction; @@ -37,10 +36,6 @@ pub enum TransitionFrontierAction { best_tip: ArcBlockWithHash, error: SyncError, }, - - RpcRespondBestTip { - peer_id: PeerId, - }, } impl redux::EnablingCondition for TransitionFrontierAction { @@ -74,9 +69,6 @@ impl redux::EnablingCondition for TransitionFrontierAction { .map_or(false, |s| s.is_apply_error()), } } - TransitionFrontierAction::RpcRespondBestTip { .. } => { - state.transition_frontier.best_tip().is_some() - } } } } diff --git a/node/src/transition_frontier/transition_frontier_effects.rs b/node/src/transition_frontier/transition_frontier_effects.rs index 83dfee1201..07428a03ae 100644 --- a/node/src/transition_frontier/transition_frontier_effects.rs +++ b/node/src/transition_frontier/transition_frontier_effects.rs @@ -266,7 +266,6 @@ pub fn transition_frontier_effects( TransitionFrontierAction::SyncFailed { .. } => { // TODO(SEC): disconnect/blacklist peers that caused this. } - TransitionFrontierAction::RpcRespondBestTip { .. } => {} } } diff --git a/node/src/transition_frontier/transition_frontier_reducer.rs b/node/src/transition_frontier/transition_frontier_reducer.rs index 0fc048b782..284846ff3e 100644 --- a/node/src/transition_frontier/transition_frontier_reducer.rs +++ b/node/src/transition_frontier/transition_frontier_reducer.rs @@ -1,11 +1,8 @@ -use openmina_core::block::AppliedBlock; -use openmina_core::bug_condition; -use p2p::channels::best_tip::P2pChannelsBestTipAction; - use super::sync::{SyncError, TransitionFrontierSyncState}; use super::{ TransitionFrontierAction, TransitionFrontierActionWithMetaRef, TransitionFrontierState, }; +use openmina_core::block::AppliedBlock; impl TransitionFrontierState { pub fn reducer( @@ -103,18 +100,6 @@ impl TransitionFrontierState { } state.sync = TransitionFrontierSyncState::Synced { time: meta.time() }; } - TransitionFrontierAction::RpcRespondBestTip { peer_id } => { - let (dispatcher, state) = state_context.into_dispatcher_and_state(); - let Some(best_tip) = state.transition_frontier.best_tip() else { - bug_condition!("Best tip not found"); - return; - }; - - dispatcher.push(P2pChannelsBestTipAction::ResponseSend { - peer_id: *peer_id, - best_tip: best_tip.clone(), - }); - } } } } diff --git a/p2p/src/p2p_state.rs b/p2p/src/p2p_state.rs index af50391f4e..e03d4b1906 100644 --- a/p2p/src/p2p_state.rs +++ b/p2p/src/p2p_state.rs @@ -1,7 +1,7 @@ use openmina_core::{ block::{ArcBlockWithHash, BlockWithHash}, impl_substate_access, - requests::{RequestId, RpcId, RpcIdType}, + requests::RpcId, snark::{Snark, SnarkInfo, SnarkJobCommitment}, ChainId, SubstateAccess, }; @@ -491,38 +491,58 @@ type OptionalCallback = Option>; #[derive(Serialize, Deserialize, Debug, Clone, Default)] pub struct P2pCallbacks { + /// Callback for [`P2pChannelsTransactionAction::Libp2pReceived`] pub on_p2p_channels_transaction_libp2p_received: OptionalCallback>, + /// Callback for [`P2pChannelsSnarkJobCommitmentAction::Received`] pub on_p2p_channels_snark_job_commitment_received: OptionalCallback<(PeerId, Box)>, + /// Callback for [`P2pChannelsSnarkAction::Received`] pub on_p2p_channels_snark_received: OptionalCallback<(PeerId, Box)>, + /// Callback for [`P2pChannelsSnarkAction::Libp2pReceived`] pub on_p2p_channels_snark_libp2p_received: OptionalCallback<(PeerId, Box)>, + /// Callback for [`P2pChannelsBestTipAction::RequestReceived`] pub on_p2p_channels_best_tip_request_received: OptionalCallback, + /// Callback for [`P2pDisconnectionAction::Finish`] pub on_p2p_disconnection_finish: OptionalCallback, - pub on_p2p_connection_outgoing_error: - OptionalCallback<(RequestId, P2pConnectionOutgoingError)>, - pub on_p2p_connection_outgoing_success: OptionalCallback>, - - pub on_p2p_connection_incoming_error: OptionalCallback<(RequestId, String)>, - pub on_p2p_connection_incoming_success: OptionalCallback>, + /// TODO: these 2 should be set by `P2pConnectionOutgoingAction::Init` + /// Callback for [`P2pConnectionOutgoingAction::Error`] + pub on_p2p_connection_outgoing_error: OptionalCallback<(RpcId, P2pConnectionOutgoingError)>, + /// Callback for [`P2pConnectionOutgoingAction::Success`] + pub on_p2p_connection_outgoing_success: OptionalCallback, + + /// TODO: these 3 should be set by `P2pConnectionIncomingAction::Init` + /// Callback for [`P2pConnectionIncomingAction::Error`] + pub on_p2p_connection_incoming_error: OptionalCallback<(RpcId, String)>, + /// Callback for [`P2pConnectionIncomingAction::Success`] + pub on_p2p_connection_incoming_success: OptionalCallback, + /// Callback for [`P2pConnectionIncomingAction::AnswerReady`] pub on_p2p_connection_incoming_answer_ready: - OptionalCallback<(RequestId, PeerId, P2pConnectionResponse)>, + OptionalCallback<(RpcId, PeerId, P2pConnectionResponse)>, + /// Callback for [`P2pPeerAction::BestTipUpdate`] pub on_p2p_peer_best_tip_update: OptionalCallback>>, + /// Callback for [`P2pChannelsRpcAction::Ready`] pub on_p2p_channels_rpc_ready: OptionalCallback, + /// Callback for [`P2pChannelsRpcAction::Timeout`] pub on_p2p_channels_rpc_timeout: OptionalCallback<(PeerId, P2pRpcId)>, + /// Callback for [`P2pChannelsRpcAction::ResponseReceived`] pub on_p2p_channels_rpc_response_received: OptionalCallback<(PeerId, P2pRpcId, Option>)>, + /// Callback for [`P2pChannelsRpcAction::RequestReceived`] pub on_p2p_channels_rpc_request_received: OptionalCallback<(PeerId, P2pRpcId, Box)>, + /// Callback for [`P2pChannelsStreamingRpcAction::Ready`] pub on_p2p_channels_streaming_rpc_ready: OptionalCallback<()>, + /// Callback for [`P2pChannelsStreamingRpcAction::Timeout`] pub on_p2p_channels_streaming_rpc_timeout: OptionalCallback<(PeerId, P2pRpcId)>, + /// Callback for [`P2pChannelsStreamingRpcAction::ResponseReceived`] pub on_p2p_channels_streaming_rpc_response_received: OptionalCallback<(PeerId, P2pRpcId, Option)>, }