1use super::*;
17
18use snarkos_node_network::harden_socket;
19use snarkos_node_router::messages::{
20 BlockRequest,
21 DisconnectReason,
22 Message,
23 MessageCodec,
24 Ping,
25 Pong,
26 PuzzleRequest,
27 UnconfirmedTransaction,
28};
29use snarkos_node_tcp::{ConnectError, Connection, ConnectionSide, Tcp, connections::DisconnectOrigin};
30use snarkvm::{
31 console::network::{ConsensusVersion, Network},
32 ledger::block::Transaction,
33 prelude::{Field, Zero},
34 utilities::into_io_error,
35};
36
37use std::{io, net::SocketAddr};
38
39impl<N: Network, C: ConsensusStorage<N>> P2P for Prover<N, C> {
40 fn tcp(&self) -> &Tcp {
42 self.router.tcp()
43 }
44}
45
46#[async_trait]
47impl<N: Network, C: ConsensusStorage<N>> Handshake for Prover<N, C> {
48 async fn perform_handshake(&self, mut connection: Connection) -> Result<Connection, ConnectError> {
50 let peer_addr = connection.addr();
52 let conn_side = connection.side();
53 let stream = self.borrow_stream(&mut connection);
54 harden_socket(stream)?;
56 let genesis_header = *self.genesis.header();
57 let restrictions_id = Field::zero(); self.router
60 .handshake(peer_addr, stream, conn_side, genesis_header, restrictions_id)
61 .await
62 .map_err(into_io_error)?;
63
64 Ok(connection)
65 }
66}
67
68#[async_trait]
69impl<N: Network, C: ConsensusStorage<N>> OnConnect for Prover<N, C>
70where
71 Self: Outbound<N>,
72{
73 async fn on_connect(&self, peer_addr: SocketAddr) {
74 if let Some(listener_addr) = self.router().resolve_to_listener(peer_addr)
76 && let Some(peer) = self.router().get_connected_peer(listener_addr)
77 && peer.node_type != NodeType::BootstrapClient
78 {
79 self.ping.on_peer_connected(listener_addr);
81 }
82 }
83}
84
85#[async_trait]
86impl<N: Network, C: ConsensusStorage<N>> Disconnect for Prover<N, C> {
87 async fn handle_disconnect(&self, peer_addr: SocketAddr, origin: DisconnectOrigin) {
89 debug!("Physically disconnecting from {peer_addr}; origin: {origin:?}");
90
91 if let Some(peer_ip) = self.router.resolve_to_listener(peer_addr) {
92 let was_fully_connected = self.router.downgrade_peer_to_candidate(peer_ip);
93 if was_fully_connected {
95 self.sync.remove_peer(&peer_ip);
96 }
97 self.router.cache().clear_peer_entries(peer_ip);
99 #[cfg(feature = "metrics")]
100 self.router.update_metrics();
101 } else {
102 warn!("Got disconnect for a peer '{peer_addr}' that is not in the peer pool");
103 }
104 }
105}
106
107#[async_trait]
108impl<N: Network, C: ConsensusStorage<N>> Reading for Prover<N, C> {
109 type Codec = MessageCodec<N>;
110 type Message = Message<N>;
111
112 fn codec(&self, _peer_addr: SocketAddr, _side: ConnectionSide) -> Self::Codec {
115 Default::default()
116 }
117
118 async fn process_message(&self, peer_addr: SocketAddr, message: Self::Message) -> io::Result<()> {
120 if let Err(error) = self.inbound(peer_addr, message).await
122 && let Some(peer_ip) = self.router().resolve_to_listener(peer_addr)
123 {
124 warn!("Disconnecting from '{peer_addr}' - {error}");
125 self.router().send(peer_ip, Message::Disconnect(DisconnectReason::ProtocolViolation.into()));
126 self.router().disconnect(peer_ip);
128 }
129 Ok(())
130 }
131}
132
133#[async_trait]
134impl<N: Network, C: ConsensusStorage<N>> Routing<N> for Prover<N, C> {}
135
136impl<N: Network, C: ConsensusStorage<N>> Heartbeat<N> for Prover<N, C> {
137 fn handle_puzzle_request(&self) {
139 if let Some((sync_peers, _)) = self.sync.find_sync_peers() {
141 if let Some((peer_ip, _)) = sync_peers.into_iter().max_by_key(|(_, height)| *height) {
143 self.router().send(peer_ip, Message::PuzzleRequest(PuzzleRequest));
145 }
146 }
147 }
148}
149
150impl<N: Network, C: ConsensusStorage<N>> Outbound<N> for Prover<N, C> {
151 fn router(&self) -> &Router<N> {
153 &self.router
154 }
155
156 fn is_block_synced(&self) -> bool {
158 true
159 }
160
161 fn num_blocks_behind(&self) -> Option<u32> {
164 Some(0)
166 }
167
168 fn get_sync_speed(&self) -> f64 {
170 0.0
171 }
172}
173
174#[async_trait]
175impl<N: Network, C: ConsensusStorage<N>> Inbound<N> for Prover<N, C> {
176 fn is_valid_message_version(&self, message_version: u32) -> bool {
178 self.router().is_valid_message_version(message_version)
179 }
180
181 fn block_request(&self, peer_ip: SocketAddr, _message: BlockRequest) -> bool {
183 debug!("Disconnecting '{peer_ip}' for the following reason - {}", DisconnectReason::ProtocolViolation);
184 false
185 }
186
187 fn block_response(
189 &self,
190 peer_ip: SocketAddr,
191 _blocks: Vec<Block<N>>,
192 _latest_consensus_version: Option<ConsensusVersion>,
193 ) -> bool {
194 debug!("Disconnecting '{peer_ip}' for the following reason - {}", DisconnectReason::ProtocolViolation);
195 false
196 }
197
198 fn ping(&self, peer_ip: SocketAddr, message: Ping<N>) -> bool {
200 if let Some(block_locators) = message.block_locators {
202 if let Err(error) = self.sync.update_peer_locators(peer_ip, &block_locators) {
204 warn!("Peer '{peer_ip}' sent invalid block locators: {error}");
205 return false;
206 }
207 }
208
209 self.router().send(peer_ip, Message::Pong(Pong { is_fork: Some(false) }));
211 true
212 }
213
214 fn pong(&self, peer_ip: SocketAddr, _message: Pong) -> bool {
216 self.ping.on_pong_received(peer_ip);
217 true
218 }
219
220 fn puzzle_request(&self, peer_ip: SocketAddr) -> bool {
222 debug!("Disconnecting '{peer_ip}' for the following reason - {}", DisconnectReason::ProtocolViolation);
223 false
224 }
225
226 fn puzzle_response(&self, peer_ip: SocketAddr, epoch_hash: N::BlockHash, header: Header<N>) -> bool {
228 let block_height = header.height();
230
231 info!(
232 "Puzzle (Block {block_height}, Coinbase Target {}, Proof Target {})",
233 header.coinbase_target(),
234 header.proof_target()
235 );
236
237 self.latest_epoch_hash.write().replace(epoch_hash);
239 self.latest_block_header.write().replace(header);
241
242 trace!("Received 'PuzzleResponse' from '{peer_ip}' (Block {block_height})");
243 true
244 }
245
246 async fn unconfirmed_solution(
248 &self,
249 peer_ip: SocketAddr,
250 serialized: UnconfirmedSolution<N>,
251 solution: Solution<N>,
252 ) -> bool {
253 let epoch_hash = *self.latest_epoch_hash.read();
255 let proof_target = self.latest_block_header.read().as_ref().map(|header| header.proof_target());
257
258 if let (Some(epoch_hash), Some(proof_target)) = (epoch_hash, proof_target) {
259 let puzzle = self.puzzle.clone();
261 let is_valid =
262 tokio::task::spawn_blocking(move || puzzle.check_solution(&solution, epoch_hash, proof_target)).await;
263
264 match is_valid {
265 Ok(Ok(())) => {
267 let message = Message::UnconfirmedSolution(serialized);
268 self.propagate(message, &[peer_ip]);
270 }
271 Ok(Err(_)) => {
272 trace!("Invalid solution '{}' for the proof target.", solution.id())
273 }
274 Err(error) => {
276 if let Some(height) = self.latest_block_header.read().as_ref().map(|header| header.height())
277 && height % N::NUM_BLOCKS_PER_EPOCH > 10
278 {
279 warn!("Failed to verify the solution - {error}")
280 }
281 }
282 }
283 }
284 true
285 }
286
287 async fn unconfirmed_transaction(
289 &self,
290 _peer_ip: SocketAddr,
291 _serialized: UnconfirmedTransaction<N>,
292 _transaction: Transaction<N>,
293 ) -> bool {
294 true
295 }
296}