Bläddra i källkod

blockchain: store each block db state diff

skoupidi 1 år sedan
förälder
incheckning
d753f8d700
6 ändrade filer med 104 tillägg och 5 borttagningar
  1. 1 0
      Cargo.lock
  2. 1 1
      Cargo.toml
  3. 68 3
      src/blockchain/block_store.rs
  4. 2 1
      src/blockchain/mod.rs
  5. 3 0
      src/error.rs
  6. 29 0
      src/validator/mod.rs

+ 1 - 0
Cargo.lock

@@ -6495,6 +6495,7 @@ version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "da80cd420e7885005fa73029986c0d861f0f4f84bae5ba4d3f4d04c89e361c4a"
 dependencies = [
+ "darkfi-serial",
  "sled",
 ]
 

+ 1 - 1
Cargo.toml

@@ -149,7 +149,7 @@ async-sdk = [
 ]
 
 blockchain = [
-    "sled-overlay",
+    "sled-overlay/serial",
     "num-bigint",
 
     "darkfi-serial/num-bigint",

+ 68 - 3
src/blockchain/block_store.rs

@@ -28,7 +28,7 @@ use darkfi_sdk::{
 use darkfi_serial::async_trait;
 use darkfi_serial::{deserialize, serialize, SerialDecodable, SerialEncodable};
 use num_bigint::BigUint;
-use sled_overlay::sled;
+use sled_overlay::{sled, SledDbOverlayStateDiff};
 
 use crate::{tx::Transaction, util::time::Timestamp, Error, Result};
 
@@ -235,6 +235,7 @@ impl BlockDifficulty {
 pub const SLED_BLOCK_TREE: &[u8] = b"_blocks";
 pub const SLED_BLOCK_ORDER_TREE: &[u8] = b"_block_order";
 pub const SLED_BLOCK_DIFFICULTY_TREE: &[u8] = b"_block_difficulty";
+pub const SLED_BLOCK_STATE_DIFF_TREE: &[u8] = b"_block_state_diff";
 
 /// The `BlockStore` is a structure representing all `sled` trees related
 /// to storing the blockchain's blocks information.
@@ -247,10 +248,14 @@ pub struct BlockStore {
     /// where the key is the height number, and the value is the blocks'
     /// hash.
     pub order: sled::Tree,
-    /// The `sled` tree storing the the difficulty information of the
+    /// The `sled` tree storing the difficulty information of the
     /// blockchain's blocks, where the key is the block height number,
     /// and the value is the blocks' hash.
     pub difficulty: sled::Tree,
+    /// The `sled` tree storing each blocks' full database state changes,
+    /// where the key is the block height number, and the value is the
+    /// serialized database diff.
+    pub state_diff: sled::Tree,
 }
 
 impl BlockStore {
@@ -259,7 +264,8 @@ impl BlockStore {
         let main = db.open_tree(SLED_BLOCK_TREE)?;
         let order = db.open_tree(SLED_BLOCK_ORDER_TREE)?;
         let difficulty = db.open_tree(SLED_BLOCK_DIFFICULTY_TREE)?;
-        Ok(Self { main, order, difficulty })
+        let state_diff = db.open_tree(SLED_BLOCK_STATE_DIFF_TREE)?;
+        Ok(Self { main, order, difficulty, state_diff })
     }
 
     /// Insert a slice of [`Block`] into the store's main tree.
@@ -285,6 +291,18 @@ impl BlockStore {
         Ok(())
     }
 
+    /// Insert a slice of `u32` and block diffs into the store's
+    /// database diffs tree.
+    pub fn insert_state_diff(
+        &self,
+        heights: &[u32],
+        diffs: &[SledDbOverlayStateDiff],
+    ) -> Result<()> {
+        let batch = self.insert_batch_state_diff(heights, diffs);
+        self.state_diff.apply_batch(batch)?;
+        Ok(())
+    }
+
     /// Generate the sled batch corresponding to an insert to the main
     /// tree, so caller can handle the write operation.
     /// The block's hash() function output is used as the key,
@@ -330,6 +348,24 @@ impl BlockStore {
         batch
     }
 
+    /// Generate the sled batch corresponding to an insert to the database diffs
+    /// tree, so caller can handle the write operation.
+    /// The block height is used as the key, and the serialized database diff is
+    /// used as value.
+    pub fn insert_batch_state_diff(
+        &self,
+        heights: &[u32],
+        diffs: &[SledDbOverlayStateDiff],
+    ) -> sled::Batch {
+        let mut batch = sled::Batch::default();
+
+        for (i, height) in heights.iter().enumerate() {
+            batch.insert(&height.to_be_bytes(), serialize(&diffs[i]));
+        }
+
+        batch
+    }
+
     /// Check if the store's main tree contains a given block hash.
     pub fn contains(&self, blockhash: &HeaderHash) -> Result<bool> {
         Ok(self.main.contains_key(blockhash.inner())?)
@@ -414,6 +450,34 @@ impl BlockStore {
         Ok(ret)
     }
 
+    /// Fetch given block height numbers from the store's state diffs tree.
+    /// The resulting vector contains `Option`, which is `Some` if the block
+    /// height number was found in the block database diffs store, and otherwise
+    /// it is `None`, if it has not.
+    /// The second parameter is a boolean which tells the function to fail in
+    /// case at least one block height number was not found.
+    pub fn get_state_diff(
+        &self,
+        heights: &[u32],
+        strict: bool,
+    ) -> Result<Vec<Option<SledDbOverlayStateDiff>>> {
+        let mut ret = Vec::with_capacity(heights.len());
+
+        for height in heights {
+            if let Some(found) = self.state_diff.get(height.to_be_bytes())? {
+                let state_diff = deserialize(&found)?;
+                ret.push(Some(state_diff));
+                continue
+            }
+            if strict {
+                return Err(Error::BlockStateDiffNotFound(*height))
+            }
+            ret.push(None);
+        }
+
+        Ok(ret)
+    }
+
     /// Retrieve all blocks from the store's main tree in the form of a
     /// tuple (`hash`, `block`).
     /// Be careful as this will try to load everything in memory.
@@ -551,6 +615,7 @@ impl BlockStoreOverlay {
         overlay.lock().unwrap().open_tree(SLED_BLOCK_TREE, true)?;
         overlay.lock().unwrap().open_tree(SLED_BLOCK_ORDER_TREE, true)?;
         overlay.lock().unwrap().open_tree(SLED_BLOCK_DIFFICULTY_TREE, true)?;
+        overlay.lock().unwrap().open_tree(SLED_BLOCK_STATE_DIFF_TREE, true)?;
         Ok(Self(overlay.clone()))
     }
 

+ 2 - 1
src/blockchain/mod.rs

@@ -29,7 +29,7 @@ use crate::{tx::Transaction, util::time::Timestamp, Error, Result};
 pub mod block_store;
 pub use block_store::{
     Block, BlockDifficulty, BlockInfo, BlockStore, BlockStoreOverlay, SLED_BLOCK_DIFFICULTY_TREE,
-    SLED_BLOCK_ORDER_TREE, SLED_BLOCK_TREE,
+    SLED_BLOCK_ORDER_TREE, SLED_BLOCK_STATE_DIFF_TREE, SLED_BLOCK_TREE,
 };
 
 /// Header definition and storage implementation
@@ -362,6 +362,7 @@ impl BlockchainOverlay {
             SLED_BLOCK_TREE,
             SLED_BLOCK_ORDER_TREE,
             SLED_BLOCK_DIFFICULTY_TREE,
+            SLED_BLOCK_STATE_DIFF_TREE,
             SLED_HEADER_TREE,
             SLED_SYNC_HEADER_TREE,
             SLED_TX_TREE,

+ 3 - 0
src/error.rs

@@ -368,6 +368,9 @@ pub enum Error {
     #[error("Block difficulty for height number {0} not found in database")]
     BlockDifficultyNotFound(u32),
 
+    #[error("Block state diff for height number {0} not found in database")]
+    BlockStateDiffNotFound(u32),
+
     #[error("Block {0} contains 0 transactions")]
     BlockContainsNoTransactions(String),
 

+ 29 - 0
src/validator/mod.rs

@@ -378,6 +378,8 @@ impl Validator {
         // Apply finalized proposals diffs and update PoW module
         let mut module = self.consensus.module.write().await;
         let mut finalized_txs = vec![];
+        let mut state_diffs_heights = vec![];
+        let mut state_diffs = vec![];
         info!(target: "validator::finalization", "Finalizing proposals:");
         for (index, proposal) in finalized_proposals.iter().enumerate() {
             info!(target: "validator::finalization", "\t{} - {}", proposal, finalized_blocks[index].header.height);
@@ -385,10 +387,15 @@ impl Validator {
             let next_difficulty = module.next_difficulty()?;
             module.append(finalized_blocks[index].header.timestamp, &next_difficulty);
             finalized_txs.extend_from_slice(&finalized_blocks[index].txs);
+            state_diffs_heights.push(finalized_blocks[index].header.height);
+            state_diffs.push(diffs[index].clone());
         }
         drop(module);
         drop(forks);
 
+        // Store the block diffs
+        self.blockchain.blocks.insert_state_diff(&state_diffs_heights, &state_diffs)?;
+
         // Reset forks starting with the finalized blocks
         self.consensus.reset_forks(&finalized_proposals, &finalized_fork, &finalized_txs).await?;
         info!(target: "validator::finalization", "Finalization completed!");
@@ -430,6 +437,10 @@ impl Validator {
         // Keep track of all blocks transactions to remove them from pending txs store
         let mut removed_txs = vec![];
 
+        // Keep track of all block database state diffs
+        let mut diffs_heights = vec![];
+        let mut diffs = vec![];
+
         // Validate and insert each block
         for (index, block) in blocks.iter().enumerate() {
             // Verify block
@@ -476,11 +487,18 @@ impl Validator {
             for tx in &block.txs {
                 removed_txs.push(tx.clone());
             }
+
+            // Store block database state diff
+            diffs_heights.push(block.header.height);
+            diffs.push(overlay.lock().unwrap().overlay.lock().unwrap().diff(&diffs)?);
         }
 
         debug!(target: "validator::add_checkpoint_blocks", "Applying overlay changes");
         overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
 
+        // Store the block diffs
+        self.blockchain.blocks.insert_state_diff(&diffs_heights, &diffs)?;
+
         // Remove blocks transactions from pending txs store
         self.blockchain.remove_pending_txs(&removed_txs)?;
 
@@ -515,6 +533,10 @@ impl Validator {
         // Keep track of all blocks transactions to remove them from pending txs store
         let mut removed_txs = vec![];
 
+        // Keep track of all block database state diffs
+        let mut diffs_heights = vec![];
+        let mut diffs = vec![];
+
         // Validate and insert each block
         for block in blocks {
             // Verify block
@@ -565,6 +587,10 @@ impl Validator {
                 removed_txs.push(tx.clone());
             }
 
+            // Store block database state diff
+            diffs_heights.push(block.header.height);
+            diffs.push(overlay.lock().unwrap().overlay.lock().unwrap().diff(&diffs)?);
+
             // Use last inserted block as next iteration previous
             previous = block;
         }
@@ -572,6 +598,9 @@ impl Validator {
         debug!(target: "validator::add_test_blocks", "Applying overlay changes");
         overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
 
+        // Store the block diffs
+        self.blockchain.blocks.insert_state_diff(&diffs_heights, &diffs)?;
+
         // Purge pending erroneous txs since canonical state has been changed
         self.blockchain.remove_pending_txs(&removed_txs)?;
         self.purge_pending_txs().await?;