Explorar o código

darkfid: hardenned syncing of stale forks

skoupidi hai 1 ano
pai
achega
2b72f9d93a

+ 1 - 1
bin/darkfid/src/proto/protocol_proposal.rs

@@ -207,7 +207,7 @@ async fn handle_receive_proposal(
             |res| async move {
                 match res {
                     Ok(()) | Err(Error::DetachedTaskStopped) => { _tasks.write().await.remove(&_task); }
-                    Err(e) => error!(target: "darkfid::proto::protocol_proposal::start", "Failed starting ProtocolProposal handler task: {e}"),
+                    Err(e) => error!(target: "darkfid::proto::protocol_proposal::start", "Failed starting unknown proposal handler task: {e}"),
                 }
             },
             Error::DetachedTaskStopped,

+ 124 - 17
bin/darkfid/src/proto/protocol_sync.rs

@@ -478,7 +478,16 @@ async fn handle_receive_tip_request(
                         target: "darkfid::proto::protocol_sync::handle_receive_tip_request",
                         "Node doesn't follow request sequence"
                     );
-                    handler.send_action(channel, ProtocolGenericAction::Skip).await;
+                    handler
+                        .send_action(
+                            channel,
+                            ProtocolGenericAction::Response(TipResponse {
+                                synced: true,
+                                height: None,
+                                hash: None,
+                            }),
+                        )
+                        .await;
                     continue
                 }
             }
@@ -738,13 +747,42 @@ async fn handle_receive_fork_header_hash_request(
             }
         };
 
+        // Send response if header was found
+        if fork_header.is_some() {
+            handler
+                .send_action(
+                    channel,
+                    ProtocolGenericAction::Response(ForkHeaderHashResponse { fork_header }),
+                )
+                .await;
+            continue
+        }
+
+        // If header wasn't found in a fork, check canonical
+        if let Err(e) = validator.blockchain.headers.get(&[request.fork_header], true) {
+            debug!(
+                target: "darkfid::proto::protocol_sync::handle_receive_fork_header_hash_request",
+                "Getting fork header hash failed: {}",
+                e
+            );
+            handler.send_action(channel, ProtocolGenericAction::Skip).await;
+            continue
+        };
+
+        let response = match validator.blockchain.blocks.get_order(&[request.height], false) {
+            Ok(h) => ProtocolGenericAction::Response(ForkHeaderHashResponse { fork_header: h[0] }),
+            Err(e) => {
+                debug!(
+                    target: "darkfid::proto::protocol_sync::handle_receive_fork_header_hash_request",
+                    "Getting fork header hash failed: {}",
+                    e
+                );
+                ProtocolGenericAction::Skip
+            }
+        };
+
         // Send response
-        handler
-            .send_action(
-                channel,
-                ProtocolGenericAction::Response(ForkHeaderHashResponse { fork_header }),
-            )
-            .await;
+        handler.send_action(channel, response).await;
     }
 }
 
@@ -807,10 +845,44 @@ async fn handle_receive_fork_headers_request(
             }
         };
 
+        // Send response if headers were found
+        if !headers.is_empty() {
+            handler
+                .send_action(
+                    channel,
+                    ProtocolGenericAction::Response(ForkHeadersResponse { headers }),
+                )
+                .await;
+            continue
+        }
+
+        // If headers weren't found in a fork, check canonical
+        if let Err(e) = validator.blockchain.headers.get(&[request.fork_header], true) {
+            debug!(
+                target: "darkfid::proto::protocol_sync::handle_receive_fork_headers_request",
+                "Getting fork header hash failed: {}",
+                e
+            );
+            handler.send_action(channel, ProtocolGenericAction::Skip).await;
+            continue
+        };
+
+        let response = match validator.blockchain.headers.get(&request.headers, true) {
+            Ok(h) => ProtocolGenericAction::Response(ForkHeadersResponse {
+                headers: h.iter().map(|x| x.clone().unwrap()).collect(),
+            }),
+            Err(e) => {
+                debug!(
+                    target: "darkfid::proto::protocol_sync::handle_receive_fork_headers_request",
+                    "Getting fork headers failed: {}",
+                    e
+                );
+                ProtocolGenericAction::Skip
+            }
+        };
+
         // Send response
-        handler
-            .send_action(channel, ProtocolGenericAction::Response(ForkHeadersResponse { headers }))
-            .await;
+        handler.send_action(channel, response).await;
     }
 }
 
@@ -855,7 +927,7 @@ async fn handle_receive_fork_proposals_request(
 
         debug!(target: "darkfid::proto::protocol_sync::handle_receive_fork_proposals_request", "Received request: {request:?}");
 
-        // Retrieve fork headers
+        // Retrieve fork proposals
         let proposals = match validator
             .consensus
             .get_fork_proposals(&request.headers, &request.fork_header)
@@ -873,12 +945,47 @@ async fn handle_receive_fork_proposals_request(
             }
         };
 
+        // Send response if proposals were found
+        if !proposals.is_empty() {
+            handler
+                .send_action(
+                    channel,
+                    ProtocolGenericAction::Response(ForkProposalsResponse { proposals }),
+                )
+                .await;
+            continue
+        }
+
+        // If proposals weren't found in a fork, check canonical
+        if let Err(e) = validator.blockchain.headers.get(&[request.fork_header], true) {
+            debug!(
+                target: "darkfid::proto::protocol_sync::handle_receive_fork_proposals_request",
+                "Getting fork header hash failed: {}",
+                e
+            );
+            handler.send_action(channel, ProtocolGenericAction::Skip).await;
+            continue
+        };
+
+        let response = match validator.blockchain.get_blocks_by_hash(&request.headers) {
+            Ok(blocks) => {
+                let mut proposals = Vec::with_capacity(blocks.len());
+                for block in blocks {
+                    proposals.push(Proposal::new(block));
+                }
+                ProtocolGenericAction::Response(ForkProposalsResponse { proposals })
+            }
+            Err(e) => {
+                debug!(
+                    target: "darkfid::proto::protocol_sync::handle_receive_fork_proposals_request",
+                    "Getting fork proposals failed: {}",
+                    e
+                );
+                ProtocolGenericAction::Skip
+            }
+        };
+
         // Send response
-        handler
-            .send_action(
-                channel,
-                ProtocolGenericAction::Response(ForkProposalsResponse { proposals }),
-            )
-            .await;
+        handler.send_action(channel, response).await;
     }
 }

+ 18 - 2
bin/darkfid/src/task/sync.rs

@@ -73,6 +73,14 @@ pub async fn sync_task(node: &DarkfiNodePtr, checkpoint: Option<(u32, HeaderHash
     let (mut common_tip_height, mut common_tip_peers) =
         most_common_tip(node, &last.1, checkpoint).await;
 
+    // If the most common tip is the genesis height(0), we skip syncing
+    // further and will reorg if needed when a new proposal arrives.
+    if common_tip_height == 0 {
+        *node.validator.synced.write().await = true;
+        info!(target: "darkfid::task::sync_task", "Blockchain synced!");
+        return Ok(())
+    }
+
     // If last known block header is before the checkpoint, we sync until that first.
     if let Some(checkpoint) = checkpoint {
         if checkpoint.0 > last.0 {
@@ -194,8 +202,16 @@ async fn synced_peers(
             };
 
             // Handle response
-            if response.synced && response.height.is_some() && response.hash.is_some() {
-                let tip = (response.height.unwrap(), *response.hash.unwrap().inner());
+            if response.synced {
+                // Grab response tip
+                let tip = if response.height.is_some() && response.hash.is_some() {
+                    (response.height.unwrap(), *response.hash.unwrap().inner())
+                } else {
+                    // Empty response while synced means the peer is on an
+                    // entirely different chain/fork, so we keep track of
+                    // them in the empty tip reference.
+                    (0, [0u8; 32])
+                };
                 let Some(tip_peers) = tips.get_mut(&tip) else {
                     tips.insert(tip, vec![peer.clone()]);
                     continue