Просмотр исходного кода

darkfid: aux state management actions added

skoupidi 1 год назад
Родитель
Сommit
26f2198e96
3 измененных файлов с 154 добавлено и 16 удалено
  1. 41 0
      bin/darkfid/src/main.rs
  2. 108 9
      src/validator/mod.rs
  3. 5 7
      src/validator/verification.rs

+ 41 - 0
bin/darkfid/src/main.rs

@@ -65,6 +65,18 @@ struct Args {
     /// Reset validator state to given block height
     reset: Option<u32>,
 
+    #[structopt(short, long)]
+    /// Purge pending sync headers
+    purge_sync: bool,
+
+    #[structopt(short, long)]
+    /// Fully validates existing blockchain state
+    validate: bool,
+
+    #[structopt(long)]
+    /// Fully rebuild the difficulties database based on existing blockchain state
+    rebuild_difficulties: bool,
+
     #[structopt(short, long)]
     /// Set log file to ouput into
     log: Option<String>,
@@ -209,6 +221,35 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
         return Ok(())
     }
 
+    // Check if sync headers purge was requested
+    if args.purge_sync {
+        info!(target: "darkfid", "Node will purge all pending sync headers.");
+        let validator = Validator::new(&sled_db, &config).await?;
+        validator.blockchain.headers.remove_all_sync()?;
+        info!(target: "darkfid", "Validator pending sync headers purged successfully!");
+        return Ok(())
+    }
+
+    // Check if validate was requested
+    if args.validate {
+        info!(target: "darkfid", "Node will validate existing blockchain state.");
+        let validator = Validator::new(&sled_db, &config).await?;
+        validator.validate_blockchain(config.pow_target, config.pow_fixed_difficulty).await?;
+        info!(target: "darkfid", "Validator blockchain state validated successfully!");
+        return Ok(())
+    }
+
+    // Check if rebuild difficulties was requested
+    if args.rebuild_difficulties {
+        info!(target: "darkfid", "Node will rebuild difficulties of existing blockchain state.");
+        let validator = Validator::new(&sled_db, &config).await?;
+        validator
+            .rebuild_block_difficulties(config.pow_target, config.pow_fixed_difficulty)
+            .await?;
+        info!(target: "darkfid", "Validator difficulties rebuilt successfully!");
+        return Ok(())
+    }
+
     // Generate the daemon
     let daemon = Darkfid::init(
         &sled_db,

+ 108 - 9
src/validator/mod.rs

@@ -751,10 +751,11 @@ impl Validator {
         pow_target: u32,
         pow_fixed_difficulty: Option<BigUint>,
     ) -> Result<()> {
-        let blocks = self.blockchain.get_all()?;
-
         // An empty blockchain is considered valid
-        if blocks.is_empty() {
+        let mut blocks_count = self.blockchain.len() as u32;
+        info!(target: "validator::validate_blockchain", "Validating {blocks_count} blocks...");
+        if blocks_count == 0 {
+            info!(target: "validator::validate_blockchain", "Blockchain validated successfully!");
             return Ok(())
         }
 
@@ -764,32 +765,39 @@ impl Validator {
         let overlay = BlockchainOverlay::new(&blockchain)?;
 
         // Set previous
-        let mut previous = &blocks[0];
+        let mut previous = self.blockchain.genesis_block()?;
 
         // Deploy native wasm contracts
         deploy_native_contracts(&overlay, pow_target).await?;
 
         // Validate genesis block
-        verify_genesis_block(&overlay, previous, pow_target).await?;
+        verify_genesis_block(&overlay, &previous, pow_target).await?;
+        info!(target: "validator::validate_blockchain", "Genesis block validated successfully!");
 
         // Write the changes to the in memory db
         overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
 
         // Create a PoW module to validate each block
-        let mut module = PoWModule::new(blockchain, pow_target, pow_fixed_difficulty, None)?;
+        let mut module = PoWModule::new(blockchain, pow_target, pow_fixed_difficulty, Some(0))?;
 
         // Grab current contracts states monotree to validate each block
         let mut state_monotree = overlay.lock().unwrap().get_state_monotree()?;
 
         // Validate and insert each block
-        for block in &blocks[1..] {
+        info!(target: "validator::validate_blockchain", "Validating rest blocks...");
+        blocks_count -= 1;
+        let mut index = 1;
+        while index <= blocks_count {
+            // Grab block
+            let block = self.blockchain.get_blocks_by_heights(&[index])?[0].clone();
+
             // Verify block
             if verify_block(
                 &overlay,
                 &module,
                 &mut state_monotree,
-                block,
-                previous,
+                &block,
+                &previous,
                 self.verify_fees,
             )
             .await
@@ -805,8 +813,12 @@ impl Validator {
 
             // Use last inserted block as next iteration previous
             previous = block;
+
+            info!(target: "validator::validate_blockchain", "Block {index}/{blocks_count} validated successfully!");
+            index += 1;
         }
 
+        info!(target: "validator::validate_blockchain", "Blockchain validated successfully!");
         Ok(())
     }
 
@@ -843,4 +855,91 @@ impl Validator {
 
         Ok(())
     }
+
+    /// Auxiliary function to rebuild the block difficulties database
+    /// based on current validator blockchain.
+    /// Be careful as this will try to load everything in memory.
+    pub async fn rebuild_block_difficulties(
+        &self,
+        pow_target: u32,
+        pow_fixed_difficulty: Option<BigUint>,
+    ) -> Result<()> {
+        info!(target: "validator::rebuild_block_difficulties", "Rebuilding validator block difficulties...");
+        // Grab append lock so no new proposals can be appended while we execute the rebuild
+        let append_lock = self.consensus.append_lock.write().await;
+
+        // Clear the block difficulties tree
+        self.blockchain.blocks.difficulty.clear()?;
+
+        // An empty blockchain doesn't have difficulty records
+        let mut blocks_count = self.blockchain.len() as u32;
+        info!(target: "validator::rebuild_block_difficulties", "Rebuilding {blocks_count} block difficulties...");
+        if blocks_count == 0 {
+            info!(target: "validator::reset_to_height", "Validator block difficulties rebuilt successfully!");
+            return Ok(())
+        }
+
+        // Create a PoW module and an in memory overlay to compute each
+        // block difficulty.
+        let mut module =
+            PoWModule::new(self.blockchain.clone(), pow_target, pow_fixed_difficulty, Some(0))?;
+
+        // Grab genesis block difficulty to access current ranks
+        let genesis_block = self.blockchain.genesis_block()?;
+        let last_difficulty = BlockDifficulty::genesis(genesis_block.header.timestamp);
+        let mut targets_rank = last_difficulty.ranks.targets_rank;
+        let mut hashes_rank = last_difficulty.ranks.hashes_rank;
+
+        // Grab each block to compute its difficulty
+        blocks_count -= 1;
+        let mut index = 1;
+        while index <= blocks_count {
+            // Grab block
+            let block = self.blockchain.get_blocks_by_heights(&[index])?[0].clone();
+
+            // Grab next mine target and difficulty
+            let (next_target, next_difficulty) = module.next_mine_target_and_difficulty()?;
+
+            // Calculate block rank
+            let (target_distance_sq, hash_distance_sq) = block_rank(&block, &next_target);
+
+            // Update chain ranks
+            targets_rank += target_distance_sq.clone();
+            hashes_rank += hash_distance_sq.clone();
+
+            // Generate block difficulty and update PoW module
+            let cumulative_difficulty =
+                module.cumulative_difficulty.clone() + next_difficulty.clone();
+            let ranks = BlockRanks::new(
+                target_distance_sq,
+                targets_rank.clone(),
+                hash_distance_sq,
+                hashes_rank.clone(),
+            );
+            let block_difficulty = BlockDifficulty::new(
+                block.header.height,
+                block.header.timestamp,
+                next_difficulty,
+                cumulative_difficulty,
+                ranks,
+            );
+            module.append(block_difficulty.timestamp, &block_difficulty.difficulty);
+
+            // Add difficulty to database
+            self.blockchain.blocks.insert_difficulty(&[block_difficulty])?;
+
+            info!(target: "validator::validate_blockchain", "Block {index}/{blocks_count} difficulty added successfully!");
+            index += 1;
+        }
+
+        // Flush the database
+        self.blockchain.sled_db.flush()?;
+
+        // Release append lock
+        drop(append_lock);
+
+        info!(target: "validator::reset_to_height", "Validator block difficulties rebuilt successfully!");
+
+        Ok(())
+    }
 }

+ 5 - 7
src/validator/verification.rs

@@ -185,7 +185,7 @@ pub fn validate_blockchain(
     pow_fixed_difficulty: Option<BigUint>,
 ) -> Result<()> {
     // Generate a PoW module
-    let mut module = PoWModule::new(blockchain.clone(), pow_target, pow_fixed_difficulty, None)?;
+    let mut module = PoWModule::new(blockchain.clone(), pow_target, pow_fixed_difficulty, Some(0))?;
     // We use block order store here so we have all blocks in order
     let blocks = blockchain.blocks.get_all_order()?;
     for (index, block) in blocks[1..].iter().enumerate() {
@@ -1085,7 +1085,7 @@ pub async fn verify_proposal(
     let previous = fork.overlay.lock().unwrap().last_block()?;
 
     // Verify proposal block (2)
-    if verify_block(
+    if let Err(e) = verify_block(
         &fork.overlay,
         &fork.module,
         &mut fork.state_monotree,
@@ -1094,9 +1094,8 @@ pub async fn verify_proposal(
         verify_fees,
     )
     .await
-    .is_err()
     {
-        error!(target: "validator::verification::verify_proposal", "Erroneous proposal block found");
+        error!(target: "validator::verification::verify_proposal", "Erroneous proposal block found: {e}");
         fork.overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
         return Err(Error::BlockIsInvalid(proposal.hash.as_string()))
     };
@@ -1129,7 +1128,7 @@ pub async fn verify_fork_proposal(
     let previous = fork.overlay.lock().unwrap().last_block()?;
 
     // Verify proposal block (2)
-    if verify_block(
+    if let Err(e) = verify_block(
         &fork.overlay,
         &fork.module,
         &mut fork.state_monotree,
@@ -1138,9 +1137,8 @@ pub async fn verify_fork_proposal(
         verify_fees,
     )
     .await
-    .is_err()
     {
-        error!(target: "validator::verification::verify_fork_proposal", "Erroneous proposal block found");
+        error!(target: "validator::verification::verify_fork_proposal", "Erroneous proposal block found: {e}");
         fork.overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
         return Err(Error::BlockIsInvalid(proposal.hash.as_string()))
     };