diff --git a/bin/darkfid/src/tests/forks.rs b/bin/darkfid/src/tests/forks.rs index da2c5e02b..2827a4b06 100644 --- a/bin/darkfid/src/tests/forks.rs +++ b/bin/darkfid/src/tests/forks.rs @@ -42,12 +42,12 @@ fn forks() -> Result<()> { let fork = Fork::new(blockchain.clone(), module).await?; // Add a dummy record to fork - fork.overlay.lock().unwrap().order.insert(&[1], &[record1])?; + fork.overlay.lock().unwrap().blocks.insert_order(&[1], &[record1])?; // Verify blockchain doesn't contain the record - assert_eq!(blockchain.order.get(&[0, 1], false)?, [Some(genesis_block_hash), None]); + assert_eq!(blockchain.blocks.get_order(&[0, 1], false)?, [Some(genesis_block_hash), None]); assert_eq!( - fork.overlay.lock().unwrap().order.get(&[0, 1], true)?, + fork.overlay.lock().unwrap().blocks.get_order(&[0, 1], true)?, [Some(genesis_block_hash), Some(record1)] ); @@ -56,24 +56,24 @@ fn forks() -> Result<()> { // Verify it contains the original records assert_eq!( - fork_clone.overlay.lock().unwrap().order.get(&[0, 1], true)?, + fork_clone.overlay.lock().unwrap().blocks.get_order(&[0, 1], true)?, [Some(genesis_block_hash), Some(record1)] ); // Add another dummy record to cloned fork - fork_clone.overlay.lock().unwrap().order.insert(&[2], &[record2])?; + fork_clone.overlay.lock().unwrap().blocks.insert_order(&[2], &[record2])?; // Verify blockchain and original fork don't contain the second record assert_eq!( - blockchain.order.get(&[0, 1, 2], false)?, + blockchain.blocks.get_order(&[0, 1, 2], false)?, [Some(genesis_block_hash), None, None] ); assert_eq!( - fork.overlay.lock().unwrap().order.get(&[0, 1, 2], false)?, + fork.overlay.lock().unwrap().blocks.get_order(&[0, 1, 2], false)?, [Some(genesis_block_hash), Some(record1), None] ); assert_eq!( - fork_clone.overlay.lock().unwrap().order.get(&[0, 1, 2], true)?, + fork_clone.overlay.lock().unwrap().blocks.get_order(&[0, 1, 2], true)?, [Some(genesis_block_hash), Some(record1), Some(record2)] ); diff --git a/src/blockchain/block_store.rs b/src/blockchain/block_store.rs index e1795b962..3dbf657b4 100644 --- a/src/blockchain/block_store.rs +++ b/src/blockchain/block_store.rs @@ -139,141 +139,6 @@ impl BlockInfo { } } -/// [`Block`] sled tree -const SLED_BLOCK_TREE: &[u8] = b"_blocks"; - -/// The `BlockStore` is a `sled` tree storing all the blockchain's blocks -/// where the key is the blocks' hash, and value is the serialized block. -#[derive(Clone)] -pub struct BlockStore(pub sled::Tree); - -impl BlockStore { - /// Opens a new or existing `BlockStore` on the given sled database. - pub fn new(db: &sled::Db) -> Result { - let tree = db.open_tree(SLED_BLOCK_TREE)?; - Ok(Self(tree)) - } - - /// Insert a slice of [`Block`] into the store. - pub fn insert(&self, blocks: &[Block]) -> Result> { - let (batch, ret) = self.insert_batch(blocks)?; - self.0.apply_batch(batch)?; - Ok(ret) - } - - /// Generate the sled batch corresponding to an insert, so caller - /// can handle the write operation. - /// The block's hash() function output is used as the key, - /// while value is the serialized [`Block`] itself. - /// On success, the function returns the block hashes in the same order. - pub fn insert_batch(&self, blocks: &[Block]) -> Result<(sled::Batch, Vec)> { - let mut ret = Vec::with_capacity(blocks.len()); - let mut batch = sled::Batch::default(); - - for block in blocks { - let blockhash = block.hash(); - batch.insert(blockhash.as_bytes(), serialize(block)); - ret.push(blockhash); - } - - Ok((batch, ret)) - } - - /// Check if the block store contains a given block hash. - pub fn contains(&self, blockhash: &blake3::Hash) -> Result { - Ok(self.0.contains_key(blockhash.as_bytes())?) - } - - /// Fetch given block hashes from the block store. - /// The resulting vector contains `Option`, which is `Some` if the block - /// was found in the block 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 was not found. - pub fn get(&self, block_hashes: &[blake3::Hash], strict: bool) -> Result>> { - let mut ret = Vec::with_capacity(block_hashes.len()); - - for hash in block_hashes { - if let Some(found) = self.0.get(hash.as_bytes())? { - let block = deserialize(&found)?; - ret.push(Some(block)); - continue - } - if strict { - let s = hash.to_hex().as_str().to_string(); - return Err(Error::BlockNotFound(s)) - } - ret.push(None); - } - - Ok(ret) - } - - /// Retrieve all blocks from the block store in the form of a tuple - /// (`hash`, `block`). - /// Be careful as this will try to load everything in memory. - pub fn get_all(&self) -> Result> { - let mut blocks = vec![]; - - for block in self.0.iter() { - blocks.push(parse_record(block.unwrap())?); - } - - Ok(blocks) - } -} - -/// Overlay structure over a [`BlockStore`] instance. -pub struct BlockStoreOverlay(SledDbOverlayPtr); - -impl BlockStoreOverlay { - pub fn new(overlay: &SledDbOverlayPtr) -> Result { - overlay.lock().unwrap().open_tree(SLED_BLOCK_TREE)?; - Ok(Self(overlay.clone())) - } - - /// Insert a slice of [`Block`] into the overlay. - /// The block's hash() function output is used as the key, - /// while value is the serialized [`Block`] itself. - /// On success, the function returns the block hashes in the same order. - pub fn insert(&self, blocks: &[Block]) -> Result> { - let mut ret = Vec::with_capacity(blocks.len()); - let mut lock = self.0.lock().unwrap(); - - for block in blocks { - let blockhash = block.hash(); - lock.insert(SLED_BLOCK_TREE, blockhash.as_bytes(), &serialize(block))?; - ret.push(blockhash); - } - - Ok(ret) - } - - /// Fetch given block hashes from the overlay. - /// The resulting vector contains `Option`, which is `Some` if the block - /// was found in the overlay, 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 was not found. - pub fn get(&self, block_hashes: &[blake3::Hash], strict: bool) -> Result>> { - let mut ret = Vec::with_capacity(block_hashes.len()); - let lock = self.0.lock().unwrap(); - - for hash in block_hashes { - if let Some(found) = lock.get(SLED_BLOCK_TREE, hash.as_bytes())? { - let block = deserialize(&found)?; - ret.push(Some(block)); - continue - } - if strict { - let s = hash.to_hex().as_str().to_string(); - return Err(Error::BlockNotFound(s)) - } - ret.push(None); - } - - Ok(ret) - } -} - /// Auxiliary structure used to keep track of blocks order. #[derive(Debug, SerialEncodable, SerialDecodable)] pub struct BlockOrder { @@ -283,209 +148,6 @@ pub struct BlockOrder { pub block: blake3::Hash, } -/// [`BlockOrder`] sled tree -const SLED_BLOCK_ORDER_TREE: &[u8] = b"_block_order"; - -/// The `BlockOrderStore` is a `sled` tree storing the order of the -/// blockchain's blocks, where the key is the order number, and the value is -/// the blocks' hash. [`BlockOrderStore`] can be queried with this order number. -#[derive(Clone)] -pub struct BlockOrderStore(pub sled::Tree); - -impl BlockOrderStore { - /// Opens a new or existing `BlockOrderStore` on the given sled database. - pub fn new(db: &sled::Db) -> Result { - let tree = db.open_tree(SLED_BLOCK_ORDER_TREE)?; - Ok(Self(tree)) - } - - /// Insert a slice of `u64` and block hashes into the store. - pub fn insert(&self, order: &[u64], hashes: &[blake3::Hash]) -> Result<()> { - let batch = self.insert_batch(order, hashes)?; - self.0.apply_batch(batch)?; - Ok(()) - } - - /// Generate the sled batch corresponding to an insert, so caller - /// can handle the write operation. - /// The block order number is used as the key, and the block hash is used as value. - pub fn insert_batch(&self, order: &[u64], hashes: &[blake3::Hash]) -> Result { - if order.len() != hashes.len() { - return Err(Error::InvalidInputLengths) - } - - let mut batch = sled::Batch::default(); - - for (i, number) in order.iter().enumerate() { - batch.insert(&number.to_be_bytes(), hashes[i].as_bytes()); - } - - Ok(batch) - } - - /// Check if the block order store contains a given order number. - pub fn contains(&self, number: u64) -> Result { - Ok(self.0.contains_key(number.to_be_bytes())?) - } - - /// Fetch given order numbers from the block order store. - /// The resulting vector contains `Option`, which is `Some` if the number - /// was found in the block order 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 order number was not found. - pub fn get(&self, order: &[u64], strict: bool) -> Result>> { - let mut ret = Vec::with_capacity(order.len()); - - for number in order { - if let Some(found) = self.0.get(number.to_be_bytes())? { - let block_hash = deserialize(&found)?; - ret.push(Some(block_hash)); - continue - } - if strict { - return Err(Error::BlockNumberNotFound(*number)) - } - ret.push(None); - } - - Ok(ret) - } - - /// Retrieve complete order from the block order store in the form of - /// a vector containing (`number`, `hash`) tuples. - /// Be careful as this will try to load everything in memory. - pub fn get_all(&self) -> Result> { - let mut order = vec![]; - - for record in self.0.iter() { - order.push(parse_u64_key_record(record.unwrap())?); - } - - Ok(order) - } - - /// Fetch n hashes after given order number. In the iteration, if an order - /// number is not found, the iteration stops and the function returns what - /// it has found so far in the `BlockOrderStore`. - pub fn get_after(&self, number: u64, n: u64) -> Result> { - let mut ret = vec![]; - - let mut key = number; - let mut counter = 0; - while counter <= n { - if let Some(found) = self.0.get_gt(key.to_be_bytes())? { - let (number, hash) = parse_u64_key_record(found)?; - key = number; - ret.push(hash); - counter += 1; - continue - } - break - } - - Ok(ret) - } - - /// Fetch the first block hash in the tree, based on the `Ord` - /// implementation for `Vec`. - pub fn get_first(&self) -> Result<(u64, blake3::Hash)> { - let found = match self.0.first()? { - Some(s) => s, - None => return Err(Error::BlockNumberNotFound(0)), - }; - let (number, hash) = parse_u64_key_record(found)?; - - Ok((number, hash)) - } - - /// Fetch the last block hash in the tree, based on the `Ord` - /// implementation for `Vec`. - pub fn get_last(&self) -> Result<(u64, blake3::Hash)> { - let found = self.0.last()?.unwrap(); - let (number, hash) = parse_u64_key_record(found)?; - - Ok((number, hash)) - } - - /// Retrieve records count - pub fn len(&self) -> usize { - self.0.len() - } - - /// Check if sled contains any records - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } -} - -/// Overlay structure over a [`BlockOrderStore`] instance. -pub struct BlockOrderStoreOverlay(SledDbOverlayPtr); - -impl BlockOrderStoreOverlay { - pub fn new(overlay: &SledDbOverlayPtr) -> Result { - overlay.lock().unwrap().open_tree(SLED_BLOCK_ORDER_TREE)?; - Ok(Self(overlay.clone())) - } - - /// Insert a slice of `u64` and block hashes into the store. With sled, the - /// operation is done as a batch. - /// The block order number is used as the key, and the blockhash is used as value. - pub fn insert(&self, order: &[u64], hashes: &[blake3::Hash]) -> Result<()> { - if order.len() != hashes.len() { - return Err(Error::InvalidInputLengths) - } - - let mut lock = self.0.lock().unwrap(); - - for (i, number) in order.iter().enumerate() { - lock.insert(SLED_BLOCK_ORDER_TREE, &number.to_be_bytes(), hashes[i].as_bytes())?; - } - - Ok(()) - } - - /// Fetch given order numbers from the overlay. - /// The resulting vector contains `Option`, which is `Some` if the number - /// was found in the overlay, 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 number was not found. - pub fn get(&self, order: &[u64], strict: bool) -> Result>> { - let mut ret = Vec::with_capacity(order.len()); - let lock = self.0.lock().unwrap(); - - for number in order { - if let Some(found) = lock.get(SLED_BLOCK_ORDER_TREE, &number.to_be_bytes())? { - let block_hash = deserialize(&found)?; - ret.push(Some(block_hash)); - continue - } - if strict { - return Err(Error::BlockNumberNotFound(*number)) - } - ret.push(None); - } - - Ok(ret) - } - - /// Fetch the last block hash in the overlay, based on the `Ord` - /// implementation for `Vec`. - pub fn get_last(&self) -> Result<(u64, blake3::Hash)> { - let found = match self.0.lock().unwrap().last(SLED_BLOCK_ORDER_TREE)? { - Some(b) => b, - None => return Err(Error::BlockNumberNotFound(0)), - }; - let (number, hash) = parse_u64_key_record(found)?; - - Ok((number, hash)) - } - - /// Check if overlay contains any records - pub fn is_empty(&self) -> Result { - Ok(self.0.lock().unwrap().is_empty(SLED_BLOCK_ORDER_TREE)?) - } -} - /// Auxiliary structure used to keep track of block ranking information. /// Note: we only need height cummulative ranks, but we also keep its actual /// ranks, so we can verify the sequence and/or know specific block height @@ -610,35 +272,106 @@ impl darkfi_serial::Decodable for BlockDifficulty { } } -/// [`BlockDifficulty`] sled tree +const SLED_BLOCK_TREE: &[u8] = b"_blocks"; +const SLED_BLOCK_ORDER_TREE: &[u8] = b"_block_order"; const SLED_BLOCK_DIFFICULTY_TREE: &[u8] = b"_block_difficulty"; -/// The `BlockDifficultyStore` is a `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. [`BlockDifficultyStore`] can be queried with this -/// height number. +/// The `BlockStore` is a structure representing all `sled` trees related +/// to storing the blockchain's blocks information. #[derive(Clone)] -pub struct BlockDifficultyStore(pub sled::Tree); +pub struct BlockStore { + /// Main `sled` tree, storing all the blockchain's blocks, where the + /// key is the blocks' hash, and value is the serialized block. + pub main: sled::Tree, + /// The `sled` tree storing the order of the blockchain's blocks, + /// where the key is the order number, and the value is the blocks' + /// hash. + pub order: sled::Tree, + /// The `sled` tree storing the 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, +} -impl BlockDifficultyStore { - /// Opens a new or existing `BlockDifficultyStore` on the given sled database. +impl BlockStore { + /// Opens a new or existing `BlockStore` on the given sled database. pub fn new(db: &sled::Db) -> Result { - let tree = db.open_tree(SLED_BLOCK_DIFFICULTY_TREE)?; - Ok(Self(tree)) + 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 }) } - /// Insert a slice of [`BlockDifficulty`] into the store. - pub fn insert(&self, block_difficulties: &[BlockDifficulty]) -> Result<()> { - let batch = self.insert_batch(block_difficulties)?; - self.0.apply_batch(batch)?; + /// Insert a slice of [`Block`] into the store's main tree. + pub fn insert(&self, blocks: &[Block]) -> Result> { + let (batch, ret) = self.insert_batch(blocks)?; + self.main.apply_batch(batch)?; + Ok(ret) + } + + /// Insert a slice of `u64` and block hashes into the store's + /// order tree. + pub fn insert_order(&self, order: &[u64], hashes: &[blake3::Hash]) -> Result<()> { + let batch = self.insert_batch_order(order, hashes)?; + self.order.apply_batch(batch)?; Ok(()) } - /// Generate the sled batch corresponding to an insert, so caller - /// can handle the write operation. + /// Insert a slice of [`BlockDifficulty`] into the store's + /// difficulty tree. + pub fn insert_difficulty(&self, block_difficulties: &[BlockDifficulty]) -> Result<()> { + let batch = self.insert_batch_difficulty(block_difficulties)?; + self.difficulty.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, + /// while value is the serialized [`Block`] itself. + /// On success, the function returns the block hashes in the same order. + pub fn insert_batch(&self, blocks: &[Block]) -> Result<(sled::Batch, Vec)> { + let mut ret = Vec::with_capacity(blocks.len()); + let mut batch = sled::Batch::default(); + + for block in blocks { + let blockhash = block.hash(); + batch.insert(blockhash.as_bytes(), serialize(block)); + ret.push(blockhash); + } + + Ok((batch, ret)) + } + + /// Generate the sled batch corresponding to an insert to the order + /// tree, so caller can handle the write operation. + /// The block order number is used as the key, and the block hash is used as value. + pub fn insert_batch_order( + &self, + order: &[u64], + hashes: &[blake3::Hash], + ) -> Result { + if order.len() != hashes.len() { + return Err(Error::InvalidInputLengths) + } + + let mut batch = sled::Batch::default(); + + for (i, number) in order.iter().enumerate() { + batch.insert(&number.to_be_bytes(), hashes[i].as_bytes()); + } + + Ok(batch) + } + + /// Generate the sled batch corresponding to an insert to the difficulty + /// tree, so caller can handle the write operation. /// The block's height number is used as the key, while value is // the serialized [`BlockDifficulty`] itself. - pub fn insert_batch(&self, block_difficulties: &[BlockDifficulty]) -> Result { + pub fn insert_batch_difficulty( + &self, + block_difficulties: &[BlockDifficulty], + ) -> Result { let mut batch = sled::Batch::default(); for block_difficulty in block_difficulties { @@ -648,17 +381,78 @@ impl BlockDifficultyStore { Ok(batch) } - /// Fetch given block height numbers from the block difficulties store. + /// Check if the store's main tree contains a given block hash. + pub fn contains(&self, blockhash: &blake3::Hash) -> Result { + Ok(self.main.contains_key(blockhash.as_bytes())?) + } + + /// Check if the store's order tree contains a given order number. + pub fn contains_order(&self, number: u64) -> Result { + Ok(self.order.contains_key(number.to_be_bytes())?) + } + + /// Fetch given block hashes from the store's main tree. + /// The resulting vector contains `Option`, which is `Some` if the block + /// was found in the block 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 was not found. + pub fn get(&self, block_hashes: &[blake3::Hash], strict: bool) -> Result>> { + let mut ret = Vec::with_capacity(block_hashes.len()); + + for hash in block_hashes { + if let Some(found) = self.main.get(hash.as_bytes())? { + let block = deserialize(&found)?; + ret.push(Some(block)); + continue + } + if strict { + let s = hash.to_hex().as_str().to_string(); + return Err(Error::BlockNotFound(s)) + } + ret.push(None); + } + + Ok(ret) + } + + /// Fetch given order numbers from the store's order tree. + /// The resulting vector contains `Option`, which is `Some` if the number + /// was found in the block order 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 order number was not found. + pub fn get_order(&self, order: &[u64], strict: bool) -> Result>> { + let mut ret = Vec::with_capacity(order.len()); + + for number in order { + if let Some(found) = self.order.get(number.to_be_bytes())? { + let block_hash = deserialize(&found)?; + ret.push(Some(block_hash)); + continue + } + if strict { + return Err(Error::BlockNumberNotFound(*number)) + } + ret.push(None); + } + + Ok(ret) + } + + /// Fetch given block height numbers from the store's difficulty tree. /// The resulting vector contains `Option`, which is `Some` if the block /// height number was found in the block difficulties 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(&self, heights: &[u64], strict: bool) -> Result>> { + pub fn get_difficulty( + &self, + heights: &[u64], + strict: bool, + ) -> Result>> { let mut ret = Vec::with_capacity(heights.len()); for height in heights { - if let Some(found) = self.0.get(height.to_be_bytes())? { + if let Some(found) = self.difficulty.get(height.to_be_bytes())? { let block_difficulty = deserialize(&found)?; ret.push(Some(block_difficulty)); continue @@ -672,19 +466,101 @@ impl BlockDifficultyStore { Ok(ret) } - /// Fetch the last record in the tree, based on the `Ord` + /// 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. + pub fn get_all(&self) -> Result> { + let mut blocks = vec![]; + + for block in self.main.iter() { + blocks.push(parse_record(block.unwrap())?); + } + + Ok(blocks) + } + + /// Retrieve complete order from the store's order tree in the form + /// of a vector containing (`number`, `hash`) tuples. + /// Be careful as this will try to load everything in memory. + pub fn get_all_order(&self) -> Result> { + let mut order = vec![]; + + for record in self.order.iter() { + order.push(parse_u64_key_record(record.unwrap())?); + } + + Ok(order) + } + + /// Retrieve all block difficulties from the store's difficulty tree in + /// the form of a vector containing (`height`, `difficulty`) tuples. + /// Be careful as this will try to load everything in memory. + pub fn get_all_difficulty(&self) -> Result> { + let mut block_difficulties = vec![]; + + for record in self.difficulty.iter() { + block_difficulties.push(parse_u64_key_record(record.unwrap())?); + } + + Ok(block_difficulties) + } + + /// Fetch n hashes after given order number. In the iteration, if an order + /// number is not found, the iteration stops and the function returns what + /// it has found so far in the `BlockOrderStore`. + pub fn get_after(&self, number: u64, n: u64) -> Result> { + let mut ret = vec![]; + + let mut key = number; + let mut counter = 0; + while counter <= n { + if let Some(found) = self.order.get_gt(key.to_be_bytes())? { + let (number, hash) = parse_u64_key_record(found)?; + key = number; + ret.push(hash); + counter += 1; + continue + } + break + } + + Ok(ret) + } + + /// Fetch the first block hash in the order tree, based on the `Ord` + /// implementation for `Vec`. + pub fn get_first(&self) -> Result<(u64, blake3::Hash)> { + let found = match self.order.first()? { + Some(s) => s, + None => return Err(Error::BlockNumberNotFound(0)), + }; + let (number, hash) = parse_u64_key_record(found)?; + + Ok((number, hash)) + } + + /// Fetch the last block hash in the order tree, based on the `Ord` + /// implementation for `Vec`. + pub fn get_last(&self) -> Result<(u64, blake3::Hash)> { + let found = self.order.last()?.unwrap(); + let (number, hash) = parse_u64_key_record(found)?; + + Ok((number, hash)) + } + + /// Fetch the last record in the difficulty tree, based on the `Ord` /// implementation for `Vec`. If the tree is empty, /// returns `None`. - pub fn get_last(&self) -> Result> { - let Some(found) = self.0.last()? else { return Ok(None) }; + pub fn get_last_difficulty(&self) -> Result> { + let Some(found) = self.difficulty.last()? else { return Ok(None) }; let block_difficulty = deserialize(&found.1)?; Ok(Some(block_difficulty)) } - /// Fetch the last N records from the block difficulties store, in order. - pub fn get_last_n(&self, n: usize) -> Result> { + /// Fetch the last N records from the difficulty store, in order. + pub fn get_last_n_difficulties(&self, n: usize) -> Result> { // Build an iterator to retrieve last N records - let records = self.0.iter().rev().take(n); + let records = self.difficulty.iter().rev().take(n); // Since the iterator grabs in right -> left order, // we deserialize found records, and push them in reverse order let mut last_n = vec![]; @@ -695,31 +571,63 @@ impl BlockDifficultyStore { Ok(last_n) } - /// Retrieve all blockdifficulties from the block difficulties store in - /// the form of a vector containing (`height`, `difficulty`) tuples. - /// Be careful as this will try to load everything in memory. - pub fn get_all(&self) -> Result> { - let mut block_difficulties = vec![]; + /// Retrieve store's order tree records count. + pub fn len(&self) -> usize { + self.order.len() + } - for record in self.0.iter() { - block_difficulties.push(parse_u64_key_record(record.unwrap())?); - } - - Ok(block_difficulties) + /// Check if store's order tree contains any records. + pub fn is_empty(&self) -> bool { + self.order.is_empty() } } -/// Overlay structure over a [`BlockDifficultyStore`] instance. -pub struct BlockDifficultyStoreOverlay(SledDbOverlayPtr); +/// Overlay structure over a [`BlockStore`] instance. +pub struct BlockStoreOverlay(SledDbOverlayPtr); -impl BlockDifficultyStoreOverlay { +impl BlockStoreOverlay { pub fn new(overlay: &SledDbOverlayPtr) -> Result { + overlay.lock().unwrap().open_tree(SLED_BLOCK_TREE)?; + overlay.lock().unwrap().open_tree(SLED_BLOCK_ORDER_TREE)?; overlay.lock().unwrap().open_tree(SLED_BLOCK_DIFFICULTY_TREE)?; Ok(Self(overlay.clone())) } - /// Insert a slice of [`BlockDifficulty`] into the overlay. - pub fn insert(&self, block_difficulties: &[BlockDifficulty]) -> Result<()> { + /// Insert a slice of [`Block`] into the overlay's main tree. + /// The block's hash() function output is used as the key, + /// while value is the serialized [`Block`] itself. + /// On success, the function returns the block hashes in the same order. + pub fn insert(&self, blocks: &[Block]) -> Result> { + let mut ret = Vec::with_capacity(blocks.len()); + let mut lock = self.0.lock().unwrap(); + + for block in blocks { + let blockhash = block.hash(); + lock.insert(SLED_BLOCK_TREE, blockhash.as_bytes(), &serialize(block))?; + ret.push(blockhash); + } + + Ok(ret) + } + + /// Insert a slice of `u64` and block hashes into overlay's order tree. + /// The block order number is used as the key, and the blockhash is used as value. + pub fn insert_order(&self, order: &[u64], hashes: &[blake3::Hash]) -> Result<()> { + if order.len() != hashes.len() { + return Err(Error::InvalidInputLengths) + } + + let mut lock = self.0.lock().unwrap(); + + for (i, number) in order.iter().enumerate() { + lock.insert(SLED_BLOCK_ORDER_TREE, &number.to_be_bytes(), hashes[i].as_bytes())?; + } + + Ok(()) + } + + /// Insert a slice of [`BlockDifficulty`] into the overlay's difficulty tree. + pub fn insert_difficulty(&self, block_difficulties: &[BlockDifficulty]) -> Result<()> { let mut lock = self.0.lock().unwrap(); for block_difficulty in block_difficulties { @@ -732,6 +640,72 @@ impl BlockDifficultyStoreOverlay { Ok(()) } + + /// Fetch given block hashes from the overlay's main tree. + /// The resulting vector contains `Option`, which is `Some` if the block + /// was found in the overlay, 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 was not found. + pub fn get(&self, block_hashes: &[blake3::Hash], strict: bool) -> Result>> { + let mut ret = Vec::with_capacity(block_hashes.len()); + let lock = self.0.lock().unwrap(); + + for hash in block_hashes { + if let Some(found) = lock.get(SLED_BLOCK_TREE, hash.as_bytes())? { + let block = deserialize(&found)?; + ret.push(Some(block)); + continue + } + if strict { + let s = hash.to_hex().as_str().to_string(); + return Err(Error::BlockNotFound(s)) + } + ret.push(None); + } + + Ok(ret) + } + + /// Fetch given order numbers from the overlay's order tree. + /// The resulting vector contains `Option`, which is `Some` if the number + /// was found in the overlay, 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 number was not found. + pub fn get_order(&self, order: &[u64], strict: bool) -> Result>> { + let mut ret = Vec::with_capacity(order.len()); + let lock = self.0.lock().unwrap(); + + for number in order { + if let Some(found) = lock.get(SLED_BLOCK_ORDER_TREE, &number.to_be_bytes())? { + let block_hash = deserialize(&found)?; + ret.push(Some(block_hash)); + continue + } + if strict { + return Err(Error::BlockNumberNotFound(*number)) + } + ret.push(None); + } + + Ok(ret) + } + + /// Fetch the last block hash in the overlay's order tree, based on the `Ord` + /// implementation for `Vec`. + pub fn get_last(&self) -> Result<(u64, blake3::Hash)> { + let found = match self.0.lock().unwrap().last(SLED_BLOCK_ORDER_TREE)? { + Some(b) => b, + None => return Err(Error::BlockNumberNotFound(0)), + }; + let (number, hash) = parse_u64_key_record(found)?; + + Ok((number, hash)) + } + + /// Check if overlay's order tree contains any records. + pub fn is_empty(&self) -> Result { + Ok(self.0.lock().unwrap().is_empty(SLED_BLOCK_ORDER_TREE)?) + } } /// Auxiliary function to append a transaction to a Merkle tree. diff --git a/src/blockchain/mod.rs b/src/blockchain/mod.rs index 67782c916..c2672193c 100644 --- a/src/blockchain/mod.rs +++ b/src/blockchain/mod.rs @@ -27,10 +27,7 @@ use crate::{tx::Transaction, util::time::Timestamp, Error, Result}; /// Block related definitions and storage implementations pub mod block_store; -pub use block_store::{ - Block, BlockDifficulty, BlockDifficultyStore, BlockDifficultyStoreOverlay, BlockInfo, - BlockOrderStore, BlockOrderStoreOverlay, BlockStore, BlockStoreOverlay, -}; +pub use block_store::{Block, BlockDifficulty, BlockInfo, BlockStore, BlockStoreOverlay}; /// Header definition and storage implementation pub mod header_store; @@ -53,10 +50,6 @@ pub struct Blockchain { pub headers: HeaderStore, /// Blocks sled tree pub blocks: BlockStore, - /// Block order sled tree - pub order: BlockOrderStore, - /// Block height difficulties sled tree, - pub difficulties: BlockDifficultyStore, /// Transactions related sled trees pub transactions: TxStore, /// Contracts related sled trees @@ -68,20 +61,10 @@ impl Blockchain { pub fn new(db: &sled::Db) -> Result { let headers = HeaderStore::new(db)?; let blocks = BlockStore::new(db)?; - let order = BlockOrderStore::new(db)?; - let difficulties = BlockDifficultyStore::new(db)?; let transactions = TxStore::new(db)?; let contracts = ContractStore::new(db)?; - Ok(Self { - sled_db: db.clone(), - headers, - blocks, - order, - difficulties, - transactions, - contracts, - }) + Ok(Self { sled_db: db.clone(), headers, blocks, transactions, contracts }) } /// Insert a given [`BlockInfo`] into the blockchain database. @@ -108,13 +91,13 @@ impl Blockchain { let (bocks_batch, block_hashes) = self.blocks.insert_batch(&[blk])?; let block_hash = block_hashes[0]; let block_hash_vec = [block_hash]; - trees.push(self.blocks.0.clone()); + trees.push(self.blocks.main.clone()); batches.push(bocks_batch); // Store block order let blocks_order_batch = - self.order.insert_batch(&[block.header.height], &block_hash_vec)?; - trees.push(self.order.0.clone()); + self.blocks.insert_batch_order(&[block.header.height], &block_hash_vec)?; + trees.push(self.blocks.order.clone()); batches.push(blocks_order_batch); // Perform an atomic transaction over the trees and apply the batches. @@ -125,7 +108,7 @@ impl Blockchain { /// Check if the given [`BlockInfo`] is in the database and all trees. pub fn has_block(&self, block: &BlockInfo) -> Result { - let blockhash = match self.order.get(&[block.header.height], true) { + let blockhash = match self.blocks.get_order(&[block.header.height], true) { Ok(v) => v[0].unwrap(), Err(_) => return Ok(false), }; @@ -172,7 +155,7 @@ impl Blockchain { /// Retrieve [`BlockInfo`]s by given heights. Does not fail if any of them are not found. pub fn get_blocks_by_heights(&self, heights: &[u64]) -> Result> { debug!(target: "blockchain", "get_blocks_by_heights(): {:?}", heights); - let blockhashes = self.order.get(heights, false)?; + let blockhashes = self.blocks.get_order(heights, false)?; let mut hashes = vec![]; for i in blockhashes.into_iter().flatten() { @@ -185,13 +168,13 @@ impl Blockchain { /// Retrieve n blocks after given start block height. pub fn get_blocks_after(&self, height: u64, n: u64) -> Result> { debug!(target: "blockchain", "get_blocks_after(): {} -> {}", height, n); - let hashes = self.order.get_after(height, n)?; + let hashes = self.blocks.get_after(height, n)?; self.get_blocks_by_hash(&hashes) } /// Retrieve stored blocks count pub fn len(&self) -> usize { - self.order.len() + self.blocks.len() } /// Retrieve stored txs count @@ -201,12 +184,12 @@ impl Blockchain { /// Check if blockchain contains any blocks pub fn is_empty(&self) -> bool { - self.order.is_empty() + self.blocks.is_empty() } /// Retrieve genesis (first) block height and hash. pub fn genesis(&self) -> Result<(u64, blake3::Hash)> { - self.order.get_first() + self.blocks.get_first() } /// Retrieve genesis (first) block info. @@ -217,7 +200,7 @@ impl Blockchain { /// Retrieve the last block height and hash. pub fn last(&self) -> Result<(u64, blake3::Hash)> { - self.order.get_last() + self.blocks.get_last() } /// Retrieve the last block info. @@ -229,7 +212,7 @@ impl Blockchain { /// Retrieve the last block difficulty. If the tree is empty, /// returns `BlockDifficulty::genesis` difficulty. pub fn last_block_difficulty(&self) -> Result { - if let Some(found) = self.difficulties.get_last()? { + if let Some(found) = self.blocks.get_last_difficulty()? { return Ok(found) } @@ -239,7 +222,7 @@ impl Blockchain { /// Check if block order for the given height is in the database. pub fn has_height(&self, height: u64) -> Result { - let vec = match self.order.get(&[height], true) { + let vec = match self.blocks.get_order(&[height], true) { Ok(v) => v, Err(_) => return Ok(false), }; @@ -323,7 +306,7 @@ impl Blockchain { /// Retrieve all blocks contained in the blockchain in order. /// Be careful as this will try to load everything in memory. pub fn get_all(&self) -> Result> { - let order = self.order.get_all()?; + let order = self.blocks.get_all_order()?; let order: Vec = order.iter().map(|x| x.1).collect(); let blocks = self.get_blocks_by_hash(&order)?; @@ -345,10 +328,6 @@ pub struct BlockchainOverlay { pub headers: HeaderStoreOverlay, /// Blocks overlay pub blocks: BlockStoreOverlay, - /// Block order overlay - pub order: BlockOrderStoreOverlay, - /// Block height difficulties overlay, - pub difficulties: BlockDifficultyStoreOverlay, /// Transactions overlay pub transactions: TxStoreOverlay, /// Contract overlay @@ -361,30 +340,20 @@ impl BlockchainOverlay { let overlay = Arc::new(Mutex::new(sled_overlay::SledDbOverlay::new(&blockchain.sled_db))); let headers = HeaderStoreOverlay::new(&overlay)?; let blocks = BlockStoreOverlay::new(&overlay)?; - let order = BlockOrderStoreOverlay::new(&overlay)?; - let difficulties = BlockDifficultyStoreOverlay::new(&overlay)?; let transactions = TxStoreOverlay::new(&overlay)?; let contracts = ContractStoreOverlay::new(&overlay)?; - Ok(Arc::new(Mutex::new(Self { - overlay, - headers, - blocks, - order, - difficulties, - transactions, - contracts, - }))) + Ok(Arc::new(Mutex::new(Self { overlay, headers, blocks, transactions, contracts }))) } /// Check if blockchain contains any blocks pub fn is_empty(&self) -> Result { - self.order.is_empty() + self.blocks.is_empty() } /// Retrieve the last block height and hash. pub fn last(&self) -> Result<(u64, blake3::Hash)> { - self.order.get_last() + self.blocks.get_last() } /// Retrieve the last block info. @@ -424,14 +393,14 @@ impl BlockchainOverlay { let block_hash_vec = [block_hash]; // Store block order - self.order.insert(&[block.header.height], &block_hash_vec)?; + self.blocks.insert_order(&[block.header.height], &block_hash_vec)?; Ok(block_hash) } /// Check if the given [`BlockInfo`] is in the database and all trees. pub fn has_block(&self, block: &BlockInfo) -> Result { - let blockhash = match self.order.get(&[block.header.height], true) { + let blockhash = match self.blocks.get_order(&[block.header.height], true) { Ok(v) => v[0].unwrap(), Err(_) => return Ok(false), }; @@ -504,20 +473,10 @@ impl BlockchainOverlay { let overlay = Arc::new(Mutex::new(self.overlay.lock().unwrap().clone())); let headers = HeaderStoreOverlay::new(&overlay)?; let blocks = BlockStoreOverlay::new(&overlay)?; - let order = BlockOrderStoreOverlay::new(&overlay)?; - let difficulties = BlockDifficultyStoreOverlay::new(&overlay)?; let transactions = TxStoreOverlay::new(&overlay)?; let contracts = ContractStoreOverlay::new(&overlay)?; - Ok(Arc::new(Mutex::new(Self { - overlay, - headers, - blocks, - order, - difficulties, - transactions, - contracts, - }))) + Ok(Arc::new(Mutex::new(Self { overlay, headers, blocks, transactions, contracts }))) } } diff --git a/src/validator/pow.rs b/src/validator/pow.rs index 28acfa839..ccb4b4525 100644 --- a/src/validator/pow.rs +++ b/src/validator/pow.rs @@ -99,7 +99,7 @@ impl PoWModule { let mut timestamps = RingBuffer::::new(); let mut difficulties = RingBuffer::::new(); let mut cummulative_difficulty = BigUint::zero(); - let last_n = blockchain.difficulties.get_last_n(BUF_SIZE)?; + let last_n = blockchain.blocks.get_last_n_difficulties(BUF_SIZE)?; for difficulty in last_n { timestamps.push(difficulty.timestamp); difficulties.push(difficulty.cummulative_difficulty.clone()); @@ -285,7 +285,7 @@ impl PoWModule { difficulty: BlockDifficulty, ) -> Result<()> { self.append(difficulty.timestamp, &difficulty.difficulty); - overlay.lock().unwrap().difficulties.insert(&[difficulty]) + overlay.lock().unwrap().blocks.insert_difficulty(&[difficulty]) } /// Mine provided block, based on next mine target diff --git a/src/validator/verification.rs b/src/validator/verification.rs index 346719bfb..40b548bec 100644 --- a/src/validator/verification.rs +++ b/src/validator/verification.rs @@ -155,7 +155,7 @@ pub fn validate_blockchain( // Generate a PoW module let mut module = PoWModule::new(blockchain.clone(), pow_target, pow_fixed_difficulty)?; // We use block order store here so we have all blocks in order - let blocks = blockchain.order.get_all()?; + let blocks = blockchain.blocks.get_all_order()?; for (index, block) in blocks[1..].iter().enumerate() { let full_blocks = blockchain.get_blocks_by_hash(&[blocks[index].1, block.1])?; let full_block = &full_blocks[1];