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

blockchain: Implement some general query functions.

parazyd 4 лет назад
Родитель
Сommit
1ae263b4de
5 измененных файлов с 143 добавлено и 17 удалено
  1. 27 3
      src/blockchain/blockstore.rs
  2. 25 1
      src/blockchain/metadatastore.rs
  3. 57 12
      src/blockchain/mod.rs
  4. 22 1
      src/blockchain/txstore.rs
  5. 12 0
      src/error.rs

+ 27 - 3
src/blockchain/blockstore.rs

@@ -3,7 +3,7 @@ use sled::Batch;
 use crate::{
     consensus2::{util::Timestamp, Block},
     util::serial::{deserialize, serialize},
-    Result,
+    Error, Result,
 };
 
 const SLED_BLOCK_TREE: &[u8] = b"_blocks";
@@ -46,14 +46,18 @@ impl BlockStore {
     /// Fetch given blockhashes from the blockstore.
     /// The resulting vector contains `Option` which is `Some` if the block
     /// was found in the blockstore, and `None`, if it has not.
-    pub fn get(&self, blockhashes: &[blake3::Hash]) -> Result<Vec<Option<Block>>> {
-        let mut ret: Vec<Option<Block>> = Vec::with_capacity(blockhashes.len());
+    pub fn get(&self, blockhashes: &[blake3::Hash], strict: bool) -> Result<Vec<Option<Block>>> {
+        let mut ret = Vec::with_capacity(blockhashes.len());
 
         for i in blockhashes {
             if let Some(found) = self.0.get(i.as_bytes())? {
                 let block = deserialize(&found)?;
                 ret.push(Some(block));
             } else {
+                if strict {
+                    let s = i.to_hex().as_str().to_string();
+                    return Err(Error::BlockNotFound(s))
+                }
                 ret.push(None);
             }
         }
@@ -113,6 +117,26 @@ impl BlockOrderStore {
         Ok(())
     }
 
+    /// Retrieve all hashes given slots.
+    pub fn get(&self, slots: &[u64], strict: bool) -> Result<Vec<Option<blake3::Hash>>> {
+        let mut ret = Vec::with_capacity(slots.len());
+
+        for i in slots {
+            if let Some(found) = self.0.get(i.to_be_bytes())? {
+                let hash_bytes: [u8; 32] = found.as_ref().try_into().unwrap();
+                let hash = blake3::Hash::from(hash_bytes);
+                ret.push(Some(hash));
+            } else {
+                if strict {
+                    return Err(Error::SlotNotFound(*i))
+                }
+                ret.push(None);
+            }
+        }
+
+        Ok(ret)
+    }
+
     /// Retrieve the last block hash in the tree, based on the Ord
     /// implementation for Vec<u8>.
     pub fn get_last(&self) -> Result<Option<(u64, blake3::Hash)>> {

+ 25 - 1
src/blockchain/metadatastore.rs

@@ -3,7 +3,7 @@ use sled::Batch;
 use crate::{
     consensus2::StreamletMetadata,
     util::serial::{deserialize, serialize},
-    Result,
+    Error, Result,
 };
 
 const SLED_STREAMLET_METADATA_TREE: &[u8] = b"_streamlet_metadata";
@@ -31,6 +31,30 @@ impl StreamletMetadataStore {
         Ok(())
     }
 
+    /// Retrieve `StreamletMetadata` by given blockhashes.
+    pub fn get(
+        &self,
+        blockhashes: &[blake3::Hash],
+        strict: bool,
+    ) -> Result<Vec<Option<StreamletMetadata>>> {
+        let mut ret = Vec::with_capacity(blockhashes.len());
+
+        for i in blockhashes {
+            if let Some(found) = self.0.get(i.as_bytes())? {
+                let sm = deserialize(&found)?;
+                ret.push(Some(sm));
+            } else {
+                if strict {
+                    let s = i.to_hex().as_str().to_string();
+                    return Err(Error::BlockMetadataNotFound(s))
+                }
+                ret.push(None);
+            }
+        }
+
+        Ok(ret)
+    }
+
     /// Retrieve all `StreamletMetadata`.
     /// Be careful as this will try to lead everything in memory.
     pub fn get_all(&self) -> Result<Vec<Option<(blake3::Hash, StreamletMetadata)>>> {

+ 57 - 12
src/blockchain/mod.rs

@@ -1,7 +1,7 @@
 use std::io;
 
 use crate::{
-    consensus2::{block::BlockProposal, util::Timestamp, Block},
+    consensus2::{block::BlockInfo, util::Timestamp, Block, BlockProposal},
     impl_vec,
     util::serial::{Decodable, Encodable, ReadExt, VarInt, WriteExt},
     Result,
@@ -49,31 +49,76 @@ impl Blockchain {
         Ok(Self { blocks, order, transactions, streamlet_metadata, nullifiers, merkle_roots })
     }
 
-    /// Batch insert [`BlockProposal`]s.
-    pub fn add(&mut self, proposals: &[BlockProposal]) -> Result<Vec<blake3::Hash>> {
-        // TODO: Engineer this function in a better way.
-        let mut ret = Vec::with_capacity(proposals.len());
+    /// Batch insert [`BlockInfo`]s.
+    pub fn add(&mut self, blocks: &[BlockInfo]) -> Result<Vec<blake3::Hash>> {
+        let mut ret = Vec::with_capacity(blocks.len());
 
-        for prop in proposals {
+        for block in blocks {
             // Store transactions
-            let tx_hashes = self.transactions.insert(&prop.txs)?;
+            let tx_hashes = self.transactions.insert(&block.txs)?;
 
             // Store block
-            let block =
-                Block { st: prop.st, sl: prop.sl, txs: tx_hashes, metadata: prop.metadata.clone() };
-            let blockhash = self.blocks.insert(&[block.clone()])?;
+            let _block = Block::new(block.st, block.sl, tx_hashes, block.metadata.clone());
+            let blockhash = self.blocks.insert(&[_block])?;
             ret.push(blockhash[0]);
 
             // Store block order
             self.order.insert(&[block.sl], &[blockhash[0]])?;
 
-            // Store streamlet metadata
-            self.streamlet_metadata.insert(&[blockhash[0]], &[prop.sm.clone()])?;
+            // Store Streamlet metadata
+            self.streamlet_metadata.insert(&[blockhash[0]], &[block.sm.clone()])?;
         }
 
         Ok(ret)
     }
 
+    /// Retrieve blocks by given hashes. Fails if any of them are not found.
+    pub fn get_blocks_by_hash(&self, blockhashes: &[blake3::Hash]) -> Result<Vec<BlockInfo>> {
+        let mut ret = Vec::with_capacity(blockhashes.len());
+
+        let blocks = self.blocks.get(blockhashes, true)?;
+        let metadata = self.streamlet_metadata.get(blockhashes, true)?;
+
+        for (i, block) in blocks.iter().enumerate() {
+            let block = block.clone().unwrap();
+            let sm = metadata[i].clone().unwrap();
+
+            let txs = self.transactions.get(&block.txs, true)?;
+            let txs = txs.iter().map(|x| x.clone().unwrap()).collect();
+
+            let info = BlockInfo::new(block.st, block.sl, txs, block.metadata.clone(), sm);
+            ret.push(info);
+        }
+
+        Ok(ret)
+    }
+
+    /// Retrieve blocks by given slots. Fails if any of them are not found.
+    pub fn get_blocks_by_slot(&self, slots: &[u64]) -> Result<Vec<BlockInfo>> {
+        let blockhashes = self.order.get(slots, true)?;
+        let blockhashes: Vec<blake3::Hash> = blockhashes.iter().map(|x| x.unwrap()).collect();
+        self.get_blocks_by_hash(&blockhashes)
+    }
+
+    /// Check if the given [`BlockInfo`] is in the database
+    pub fn has_block(&self, info: &BlockInfo) -> Result<bool> {
+        let hashes = self.order.get(&[info.sl], true)?;
+        if hashes.is_empty() {
+            return Ok(false)
+        }
+
+        if let Some(found) = &hashes[0] {
+            // Check provided info produces the same hash
+            // TODO: This BlockProposal::to_proposal_hash function should be in a better place.
+            let blockhash =
+                BlockProposal::to_proposal_hash(info.st, info.sl, &info.txs, &info.metadata);
+
+            return Ok(&blockhash == found)
+        }
+
+        Ok(false)
+    }
+
     /// Retrieve the last block slot and hash
     pub fn last(&self) -> Result<Option<(u64, blake3::Hash)>> {
         self.order.get_last()

+ 22 - 1
src/blockchain/txstore.rs

@@ -3,7 +3,7 @@ use sled::Batch;
 use crate::{
     consensus2::Tx,
     util::serial::{deserialize, serialize},
-    Result,
+    Error, Result,
 };
 
 const SLED_TX_TREE: &[u8] = b"_transactions";
@@ -35,6 +35,27 @@ impl TxStore {
         Ok(ret)
     }
 
+    /// Fetch requested transactions from the txstore. The `strict` param
+    /// will make the function fail if a transaction has not been found.
+    pub fn get(&self, tx_hashes: &[blake3::Hash], strict: bool) -> Result<Vec<Option<Tx>>> {
+        let mut ret: Vec<Option<Tx>> = Vec::with_capacity(tx_hashes.len());
+
+        for i in tx_hashes {
+            if let Some(found) = self.0.get(i.as_bytes())? {
+                let tx = deserialize(&found)?;
+                ret.push(Some(tx));
+            } else {
+                if strict {
+                    let s = i.to_hex().as_str().to_string();
+                    return Err(Error::TransactionNotFound(s))
+                }
+                ret.push(None);
+            }
+        }
+
+        Ok(ret)
+    }
+
     /// Retrieve all transactions.
     /// Be careful as this will try to load everything in memory.
     pub fn get_all(&self) -> Result<Vec<Option<(blake3::Hash, Tx)>>> {

+ 12 - 0
src/error.rs

@@ -184,6 +184,18 @@ pub enum Error {
     #[error(transparent)]
     SledError(#[from] sled::Error),
 
+    #[error("Transaction {0} not found in database")]
+    TransactionNotFound(String),
+
+    #[error("Block {0} not found in database")]
+    BlockNotFound(String),
+
+    #[error("Block in slot {0} not found in database")]
+    SlotNotFound(u64),
+
+    #[error("Block {0} metadata not found in database")]
+    BlockMetadataNotFound(String),
+
     // =============
     // Wallet errors
     // =============